== !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
Owner: $component.owner
$component.description