| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | # |
|---|
| 3 | # Copyright (C) 2006 Alec Thomas <alec@swapoff.org> |
|---|
| 4 | # |
|---|
| 5 | # This software is licensed as described in the file COPYING, which |
|---|
| 6 | # you should have received as part of this distribution. |
|---|
| 7 | # |
|---|
| 8 | |
|---|
| 9 | import re |
|---|
| 10 | import math |
|---|
| 11 | from trac.core import * |
|---|
| 12 | from trac.web.api import IRequestHandler |
|---|
| 13 | from trac.web.chrome import ITemplateProvider, INavigationContributor, \ |
|---|
| 14 | add_stylesheet, add_ctxtnav |
|---|
| 15 | from genshi.builder import tag as builder |
|---|
| 16 | from trac.util import to_unicode |
|---|
| 17 | from trac.util.compat import sorted, set, any |
|---|
| 18 | from tractags.api import TagSystem, ITagProvider |
|---|
| 19 | from tractags.query import InvalidQuery |
|---|
| 20 | from trac.resource import Resource |
|---|
| 21 | from trac.mimeview import Context |
|---|
| 22 | from trac.wiki.formatter import Formatter |
|---|
| 23 | |
|---|
| 24 | |
|---|
| 25 | class TagTemplateProvider(Component): |
|---|
| 26 | """Provides templates and static resources for the tags plugin.""" |
|---|
| 27 | |
|---|
| 28 | implements(ITemplateProvider) |
|---|
| 29 | |
|---|
| 30 | # ITemplateProvider methods |
|---|
| 31 | def get_templates_dirs(self): |
|---|
| 32 | """ |
|---|
| 33 | Return the absolute path of the directory containing the provided |
|---|
| 34 | ClearSilver templates. |
|---|
| 35 | """ |
|---|
| 36 | from pkg_resources import resource_filename |
|---|
| 37 | return [resource_filename(__name__, 'templates')] |
|---|
| 38 | |
|---|
| 39 | def get_htdocs_dirs(self): |
|---|
| 40 | """Return the absolute path of a directory containing additional |
|---|
| 41 | static resources (such as images, style sheets, etc). |
|---|
| 42 | """ |
|---|
| 43 | from pkg_resources import resource_filename |
|---|
| 44 | return [('tags', resource_filename(__name__, 'htdocs'))] |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | class TagRequestHandler(Component): |
|---|
| 48 | """Implements the /tags handler.""" |
|---|
| 49 | |
|---|
| 50 | implements(IRequestHandler, INavigationContributor) |
|---|
| 51 | |
|---|
| 52 | tag_providers = ExtensionPoint(ITagProvider) |
|---|
| 53 | |
|---|
| 54 | # INavigationContributor methods |
|---|
| 55 | def get_active_navigation_item(self, req): |
|---|
| 56 | if 'TAGS_VIEW' in req.perm: |
|---|
| 57 | return 'tags' |
|---|
| 58 | |
|---|
| 59 | def get_navigation_items(self, req): |
|---|
| 60 | if 'TAGS_VIEW' in req.perm: |
|---|
| 61 | yield ('mainnav', 'tags', |
|---|
| 62 | builder.a('Tags', href=req.href.tags(), accesskey='T')) |
|---|
| 63 | |
|---|
| 64 | # IRequestHandler methods |
|---|
| 65 | def match_request(self, req): |
|---|
| 66 | return 'TAGS_VIEW' in req.perm and req.path_info.startswith('/tags') |
|---|
| 67 | |
|---|
| 68 | def process_request(self, req): |
|---|
| 69 | req.perm.require('TAGS_VIEW') |
|---|
| 70 | add_ctxtnav(req, 'Cloud', req.href.tags()) |
|---|
| 71 | match = re.match(r'/tags/?(.*)', req.path_info) |
|---|
| 72 | if match.group(1): |
|---|
| 73 | req.redirect(req.href('tags', q=match.group(1))) |
|---|
| 74 | add_stylesheet(req, 'tags/css/tractags.css') |
|---|
| 75 | query = req.args.get('q', '') |
|---|
| 76 | data = {'title': 'Tags'} |
|---|
| 77 | formatter = Formatter( |
|---|
| 78 | self.env, Context.from_request(req, Resource('tag')) |
|---|
| 79 | ) |
|---|
| 80 | |
|---|
| 81 | realms = [p.get_taggable_realm() for p in self.tag_providers] |
|---|
| 82 | checked_realms = [r for r in realms if r in req.args] or realms |
|---|
| 83 | data['tag_realms'] = [{'name': realm, 'checked': realm in checked_realms} |
|---|
| 84 | for realm in realms] |
|---|
| 85 | |
|---|
| 86 | if query: |
|---|
| 87 | data['tag_title'] = 'Showing objects matching "%s"' % query |
|---|
| 88 | data['tag_query'] = query |
|---|
| 89 | |
|---|
| 90 | from tractags.macros import TagCloudMacro, ListTaggedMacro |
|---|
| 91 | if not query: |
|---|
| 92 | macro = TagCloudMacro(self.env) |
|---|
| 93 | else: |
|---|
| 94 | macro = ListTaggedMacro(self.env) |
|---|
| 95 | query = '(%s) (%s)' % (' or '.join(['realm:' + r for r in realms |
|---|
| 96 | if r in checked_realms]), query) |
|---|
| 97 | self.env.log.debug('Tag query: %s', query) |
|---|
| 98 | try: |
|---|
| 99 | data['tag_body'] = macro.expand_macro(formatter, None, query) |
|---|
| 100 | except InvalidQuery, e: |
|---|
| 101 | data['tag_query_error'] = to_unicode(e) |
|---|
| 102 | data['tag_body'] = TagCloudMacro(self.env) \ |
|---|
| 103 | .expand_macro(formatter, None, '') |
|---|
| 104 | return 'tag_view.html', data, None |
|---|