Edgewall Software

ComponentModule Examples

Some interfaces are not very useful (but possible) when implemented in isolation and are usually accompanied by implementations of other interfaces. It is therefore difficult to provide meaningful minimal examples. It is more useful to look at a few related examples together.

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 such interfaces in this context.

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.attachment import AttachmentModule, ILegacyAttachmentPolicyDelegate
from trac.core import *
from trac.mimeview.api import Context
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, ILegacyAttachmentPolicyDelegate,
               IResourceManager, IRequestHandler, INavigationContributor,
               ITemplateProvider)

    # IPermissionRequestor methods

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

    # ILegacyAttachmentPolicyDelegate methods

    def check_attachment_permission(self, action, username, resource, perm):
        if resource.parent.realm == 'component':
            if action == 'ATTACHMENT_VIEW':
                return 'COMPONENT_VIEW' in perm(resource.parent)
            elif action in ('ATTACHMENT_CREATE','ATTACHMENT_DELETE'): 
                return 'TICKET_ADMIN' in perm(resource.parent)

    # 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 = _('Component %(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')
            resource = Resource('component', name)
            context = Context.from_request(req, resource)
            attachments = AttachmentModule(self.env).attachment_data(context)
            data = {'component': model.Component(self.env, name),
                    'attachments': attachments}
            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>

    <xi:include href="list_of_attachments.html"
                  py:with="alist = attachments; compact = True; foldable = True"/>
    <div class="buttons">
      <xi:include href="attach_file_form.html" py:with="alist = attachments"/>
    </div>
  </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>
Last modified 12 years ago Last modified on Jul 15, 2012, 1:26:45 PM
Note: See TracWiki for help on using the wiki.