== !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 [TicketComponent 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`: * [../trac.perm.IPermissionRequestor IPermissionRequestor] * [../trac.resource.IResourceManager IResourceManager] * [../trac.web.api.IRequestHandler IRequestHandler] * [../trac.web.chrome.INavigationContributor INavigationContributor] * [../trac.web.chrome.ITemplateProvider ITemplateProvider] * [../trac.attachment.ILegacyAttachmentPolicyDelegate ILegacyAttachmentPolicyDelegate] === Full example One could combine and slightly expand the above examples into one module `trac/component/web_ui.py`: {{{ #!python 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`: {{{ #!xml Component $component.name

Component $component.name

Owner: $component.owner

$component.description

}}} and `trac/component/templates/component_list.html`: {{{ #!xml Components

Components

}}}