source: tagsplugin/tags/0.7/tractags/web_ui.py

Last change on this file was 13792, checked in by Steffen Hoffmann, 10 years ago

TagsPlugin: Organize code for realm selection in web-UI and wiki macros code.

File size: 6.2 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006 Alec Thomas <alec@swapoff.org>
4# Copyright (C) 2011 Itamar Ostricher <itamarost@gmail.com>
5# Copyright (C) 2011-2014 Steffen Hoffmann <hoff.st@web.de>
6#
7# This software is licensed as described in the file COPYING, which
8# you should have received as part of this distribution.
9#
10
11import re
12import math
13
14from genshi.builder import tag as builder
15from trac.config import ListOption, Option
16from trac.core import ExtensionPoint, implements
17from trac.mimeview import Context
18from trac.resource import Resource
19from trac.util import to_unicode
20from trac.util.text import CRLF
21from trac.web.api import IRequestHandler
22from trac.web.chrome import INavigationContributor
23from trac.web.chrome import add_stylesheet, add_ctxtnav
24from trac.wiki.formatter import Formatter
25from trac.wiki.model import WikiPage
26
27from tractags.api import TagSystem, ITagProvider, _, tag_
28from tractags.macros import TagTemplateProvider, TagWikiMacros, as_int
29from tractags.macros import query_realms
30from tractags.query import InvalidQuery
31
32
33class TagRequestHandler(TagTemplateProvider):
34    """Implements the /tags handler."""
35
36    implements(INavigationContributor, IRequestHandler)
37
38    tag_providers = ExtensionPoint(ITagProvider)
39
40    cloud_mincount = Option('tags', 'cloud_mincount', 1,
41        doc="""Integer threshold to hide tags with smaller count.""")
42    default_cols = Option('tags', 'default_table_cols', 'id|description|tags',
43        doc="""Select columns and order for table format using a "|"-separated
44            list of column names.
45
46            Supported columns: realm, id, description, tags
47            """)
48    default_format = Option('tags', 'default_format', 'oldlist',
49        doc="""Set the default format for the handler of the `/tags` domain.
50
51            || `oldlist` (default value) || The original format with a
52            bulleted-list of "linked-id description (tags)" ||
53            || `compact` || bulleted-list of "linked-description" ||
54            || `table` || table... (see corresponding column option) ||
55            """)
56    exclude_realms = ListOption('tags', 'exclude_realms', [],
57        doc="""Comma-separated list of realms to exclude from tags queries
58            by default, unless specifically included using "realm:realm-name"
59            in a query.""")
60
61    # INavigationContributor methods
62    def get_active_navigation_item(self, req):
63        if 'TAGS_VIEW' in req.perm:
64            return 'tags'
65
66    def get_navigation_items(self, req):
67        if 'TAGS_VIEW' in req.perm:
68            label = tag_("Tags")
69            yield ('mainnav', 'tags',
70                   builder.a(label, href=req.href.tags(), accesskey='T'))
71
72    # IRequestHandler methods
73    def match_request(self, req):
74        return 'TAGS_VIEW' in req.perm and req.path_info.startswith('/tags')
75
76    def process_request(self, req):
77        req.perm.require('TAGS_VIEW')
78
79        match = re.match(r'/tags/?(.*)', req.path_info)
80        tag_id = match.group(1) and match.group(1) or None
81        query = req.args.get('q', '')
82
83        # Consider only providers, that are permitted for display.
84        realms = [p.get_taggable_realm() for p in self.tag_providers
85                  if (not hasattr(p, 'check_permission') or \
86                      p.check_permission(req.perm, 'view'))]
87        if not (tag_id or query) or [r for r in realms if r in req.args] == []: 
88            for realm in realms:
89                if not realm in self.exclude_realms:
90                    req.args[realm] = 'on'
91        checked_realms = [r for r in realms if r in req.args]
92        if query:
93            # Add permitted realms from query expression.
94            checked_realms.extend(query_realms(query, realms))
95        realm_args = dict(zip([r for r in checked_realms],
96                              ['on' for r in checked_realms]))
97        # Switch between single tag and tag query expression mode.
98        if tag_id and not re.match(r"""(['"]?)(\S+)\1$""", tag_id, re.UNICODE):
99            # Convert complex, invalid tag ID's --> query expression.
100            req.redirect(req.href.tags(realm_args, q=tag_id))
101        elif query:
102            single_page = re.match(r"""(['"]?)(\S+)\1$""", query, re.UNICODE)
103            if single_page:
104                # Convert simple query --> single tag.
105                req.redirect(req.href.tags(single_page.group(2), realm_args))
106
107        data = dict(page_title=_("Tags"), checked_realms=checked_realms)
108        # Populate the TagsQuery form field.
109        data['tag_query'] = tag_id and tag_id or query
110        data['tag_realms'] = list(dict(name=realm,
111                                       checked=realm in checked_realms)
112                                  for realm in realms)
113        if tag_id:
114            page_name = tag_id
115            page = WikiPage(self.env, page_name)
116            data['tag_page'] = page
117
118        macros = TagWikiMacros(self.env)
119        if query or tag_id:
120            # TRANSLATOR: The meta-nav link label.
121            add_ctxtnav(req, _("Back to Cloud"), req.href.tags())
122            macro = 'ListTagged'
123            args = "%s,format=%s,cols=%s" % \
124                   (tag_id and tag_id or query, self.default_format,
125                    self.default_cols)
126            data['mincount'] = None
127        else:
128            macro = 'TagCloud'
129            mincount = as_int(req.args.get('mincount', None),
130                              self.cloud_mincount)
131            args = mincount and "mincount=%s" % mincount or None
132            data['mincount'] = mincount
133        formatter = Formatter(self.env, Context.from_request(req,
134                                                             Resource('tag')))
135        self.env.log.debug(
136            "Tag macro arguments: %s", args and args or '(none)')
137        try:
138            # Query string without realm throws 'NotImplementedError'.
139            data['tag_body'] = checked_realms and \
140                               macros.expand_macro(formatter, macro, args,
141                                                   realms=checked_realms) \
142                               or ''
143        except InvalidQuery, e:
144            data['tag_query_error'] = to_unicode(e)
145            data['tag_body'] = macros.expand_macro(formatter, 'TagCloud', '')
146        add_stylesheet(req, 'tags/css/tractags.css')
147        return 'tag_view.html', data, None
Note: See TracBrowser for help on using the repository browser.