Edgewall Software

Changes between Initial Version and Version 1 of TracDev/PluginDevelopment/ExtensionPoints/ComponentModuleExamples


Ignore:
Timestamp:
Aug 21, 2011, 9:52:43 PM (13 years ago)
Author:
Peter Suter
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/PluginDevelopment/ExtensionPoints/ComponentModuleExamples

    v1 v1  
     1== !ComponentModule Examples
     2
     3Some interfaces are not very useful (but possible) when implemented in isolation and usually accompanied by implementations of other interfaces. It is therefore difficult to provide meaningful minimal examples. It is more useful to understood a few related examples in context.
     4
     5Trac allows assigning tickets to [TicketComponent components], but has no full-fledged component module. This provides an opportunity to build (parts of) such a component to illustrate the various such interfaces.
     6
     7=== Per-Interface examples
     8
     9The following interface descriptions contain minimal examples that can be viewed as partial pieces that together make up a slightly more realistic (but still simplistic) `ComponentModule`:
     10* [../trac.perm.IPermissionRequestor IPermissionRequestor]
     11* [../trac.resource.IResourceManager IResourceManager]
     12* [../trac.web.api.IRequestHandler IRequestHandler]
     13* [../trac.web.chrome.INavigationContributor INavigationContributor]
     14* [../trac.web.chrome.ITemplateProvider ITemplateProvider]
     15
     16=== Full example
     17
     18One could combine and slightly expand the above examples into one module `trac/component/web_ui.py`:
     19{{{
     20#!python
     21import pkg_resources
     22import re
     23
     24from genshi.builder import tag
     25
     26from trac.core import *
     27from trac.perm import IPermissionRequestor
     28from trac.resource import IResourceManager, Resource
     29from trac.ticket import model
     30from trac.util.text import shorten_line
     31from trac.util.translation import _
     32from trac.web import IRequestHandler
     33from trac.web.chrome import INavigationContributor, ITemplateProvider
     34
     35class ComponentModule(Component):
     36
     37    implements(IPermissionRequestor, IResourceManager, IRequestHandler,
     38               INavigationContributor, ITemplateProvider)
     39
     40    # IPermissionRequestor methods
     41
     42    def get_permission_actions(self):
     43        return ('COMPONENT_LIST', 'COMPONENT_VIEW')
     44
     45    # IResourceManager methods
     46       
     47    def get_resource_realms(self):
     48        yield 'component'
     49
     50    def get_resource_url(self, resource, href, **kwargs):
     51        return href.component(resource.id)
     52
     53    def get_resource_description(self, resource, format='default', context=None,
     54                                 **kwargs):
     55        if format == 'compact':
     56            return resource.id
     57        desc = _('Componment %(name)s', name=resource.id)
     58        if format == 'summary':
     59            comp = model.Component(self.env, resource.id)
     60            if comp.owner:
     61                desc += _(' (by %(owner)s)', owner=comp.owner)
     62            if comp.description:
     63                desc += ': %s' % shorten_line(comp.description)
     64        return desc
     65
     66    def resource_exists(self, resource):
     67        return bool(self.env.db_query("""
     68                    SELECT name FROM component WHERE name=%s""", (resource.id,)))
     69
     70    # IRequestHandler methods
     71   
     72    def match_request(self, req):
     73        match = re.match(r'/component(?:/(.+))?$', req.path_info)
     74        if match:
     75            if match.group(1):
     76                req.args['name'] = match.group(1)
     77            return True
     78
     79    def process_request(self, req):
     80        if 'name' in req.args:
     81            name = req.args.get('name')
     82            req.perm('component', name).require('COMPONENT_VIEW')
     83            data = {'component': model.Component(self.env, name)}
     84            return 'component.html', data, None
     85        req.perm.require('COMPONENT_LIST')
     86        list = [{'name': c.name,
     87                 'href': self.get_resource_url(Resource('component', c.name),
     88                                               req.href)}
     89                 for c in model.Component.select(self.env)]
     90        data = {'components': list}
     91        return 'component_list.html', data, None
     92
     93    # INavigationContributor methods
     94
     95    def get_active_navigation_item(self, req):
     96        return 'component'
     97
     98    def get_navigation_items(self, req):
     99        if 'COMPONENT_LIST' in req.perm:
     100            yield ('mainnav', 'component',
     101                   tag.a(_("Components"), href=req.href.component()))
     102
     103    # ITemplateProvider methods
     104
     105    def get_htdocs_dirs(self):
     106        return []
     107
     108    def get_templates_dirs(self):
     109        return [pkg_resources.resource_filename('trac.component',
     110                                                'templates')]
     111}}}
     112With the templates in `trac/component/templates/component.html`:
     113{{{
     114#!xml
     115<!DOCTYPE html
     116    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     117    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
     118<html xmlns="http://www.w3.org/1999/xhtml"
     119      xmlns:xi="http://www.w3.org/2001/XInclude"
     120      xmlns:py="http://genshi.edgewall.org/"
     121      xmlns:i18n="http://genshi.edgewall.org/i18n">
     122  <xi:include href="layout.html" />
     123  <head>
     124    <title>Component $component.name</title>
     125  </head>
     126  <body>
     127    <h2>Component $component.name</h2>
     128    <p>Owner: $component.owner</p>
     129    <p>$component.description</p>
     130  </body>
     131</html>
     132}}}
     133and `trac/component/templates/component_list.html`:
     134{{{
     135#!xml
     136<!DOCTYPE html
     137    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     138    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
     139<html xmlns="http://www.w3.org/1999/xhtml"
     140      xmlns:xi="http://www.w3.org/2001/XInclude"
     141      xmlns:py="http://genshi.edgewall.org/"
     142      xmlns:i18n="http://genshi.edgewall.org/i18n">
     143  <xi:include href="layout.html" />
     144  <head>
     145    <title>Components</title>
     146  </head>
     147  <body>
     148    <h2>Components</h2>
     149    <ul>
     150      <li py:for="c in components">
     151        <a href="$c.href">$c.name</a>
     152      </li>
     153    </ul>
     154  </body>
     155</html>
     156}}}