Edgewall Software

Version 1 (modified by Peter Suter, 13 years ago) ( diff )

ComponentModule Examples

Some 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.

Trac allows assigning tickets to 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.

Per-Interface examples

The 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:

Full example

One could combine and slightly expand the above examples into one module trac/component/web_ui.py:

import pkg_resources
import re

from genshi.builder import tag

from trac.core import *
from trac.perm import IPermissionRequestor
from trac.resource import IResourceManager, Resource
from trac.ticket import model
from trac.util.text import shorten_line
from trac.util.translation import _
from trac.web import IRequestHandler
from trac.web.chrome import INavigationContributor, ITemplateProvider

class ComponentModule(Component):

    implements(IPermissionRequestor, IResourceManager, IRequestHandler,
               INavigationContributor, ITemplateProvider)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ('COMPONENT_LIST', 'COMPONENT_VIEW')

    # IResourceManager methods
        
    def get_resource_realms(self):
        yield 'component'

    def get_resource_url(self, resource, href, **kwargs):
        return href.component(resource.id)

    def get_resource_description(self, resource, format='default', context=None,
                                 **kwargs):
        if format == 'compact':
            return resource.id
        desc = _('Componment %(name)s', name=resource.id)
        if format == 'summary':
            comp = model.Component(self.env, resource.id)
            if comp.owner:
                desc += _(' (by %(owner)s)', owner=comp.owner)
            if comp.description:
                desc += ': %s' % shorten_line(comp.description)
        return desc

    def resource_exists(self, resource):
        return bool(self.env.db_query("""
                    SELECT name FROM component WHERE name=%s""", (resource.id,)))

    # IRequestHandler methods
    
    def match_request(self, req):
        match = re.match(r'/component(?:/(.+))?$', req.path_info)
        if match:
            if match.group(1):
                req.args['name'] = match.group(1)
            return True

    def process_request(self, req):
        if 'name' in req.args:
            name = req.args.get('name')
            req.perm('component', name).require('COMPONENT_VIEW')
            data = {'component': model.Component(self.env, name)}
            return 'component.html', data, None
        req.perm.require('COMPONENT_LIST')
        list = [{'name': c.name, 
                 'href': self.get_resource_url(Resource('component', c.name),
                                               req.href)}
                 for c in model.Component.select(self.env)]
        data = {'components': list}
        return 'component_list.html', data, None

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'component'

    def get_navigation_items(self, req):
        if 'COMPONENT_LIST' in req.perm:
            yield ('mainnav', 'component', 
                   tag.a(_("Components"), href=req.href.component()))

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        return []

    def get_templates_dirs(self):
        return [pkg_resources.resource_filename('trac.component',
                                                'templates')]

With the templates in trac/component/templates/component.html:

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:i18n="http://genshi.edgewall.org/i18n">
  <xi:include href="layout.html" />
  <head>
    <title>Component $component.name</title>
  </head>
  <body>
    <h2>Component $component.name</h2>
    <p>Owner: $component.owner</p>
    <p>$component.description</p>
  </body>
</html>

and trac/component/templates/component_list.html:

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:i18n="http://genshi.edgewall.org/i18n">
  <xi:include href="layout.html" />
  <head>
    <title>Components</title>
  </head>
  <body>
    <h2>Components</h2>
    <ul>
      <li py:for="c in components">
        <a href="$c.href">$c.name</a>
      </li>
    </ul>
  </body>
</html>
Note: See TracWiki for help on using the wiki.