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

Last change on this file was 2921, checked in by Alec Thomas, 16 years ago

Lots more 0.11 fixes thanks to osimons. Also made expression parser much more robust, adding support for normal Python "and", "or" and "not" operators and fixing the two-tag limit.

File size: 7.7 KB
Line 
1import re
2from tractags.api import TagEngine
3from StringIO import StringIO
4from trac.core import *
5from trac.perm import IPermissionRequestor
6from trac.web.main import IRequestHandler
7from trac.web.chrome import ITemplateProvider, INavigationContributor, add_stylesheet
8from trac.web.api import ITemplateStreamFilter
9from trac.wiki.api import IWikiPageManipulator
10from trac.util.html import Markup, escape
11from trac.util.compat import set
12from trac.wiki.web_ui import WikiModule
13from tractags.expr import Expression
14from genshi.builder import tag as T
15from genshi.filters.transform import Transformer
16
17
18_tag_split = re.compile('[,\s]+')
19
20
21class TagsUserInterface(Component):
22    implements(ITemplateStreamFilter, IWikiPageManipulator, IPermissionRequestor)
23
24    # Internal methods
25    def _page_tags(self, req):
26        pagename = req.args.get('page', 'WikiStart')
27
28        engine = TagEngine(self.env)
29        wikitags = engine.tagspace.wiki
30        current_tags = list(wikitags.get_tags([pagename]))
31        current_tags.sort()
32        return current_tags
33
34    def _wiki_view(self, req, stream):
35        tags = self._page_tags(req)
36        if not tags:
37            return stream
38        engine = TagEngine(self.env)
39        add_stylesheet(req, 'tags/css/tractags.css')
40        li = []
41        for tag in tags:
42            href, title = engine.get_tag_link(tag)
43            li.append(T.li(T.a(title=title, href=href)(tag), ' '))
44
45        insert = T.ul(class_='tags')(T.lh('Tags'), li)
46
47        return stream | Transformer('//div[@class="buttons"]').before(insert)
48
49    def _update_tags(self, req, page):
50        newtags = set([t.strip() for t in
51                      _tag_split.split(req.args.get('tags')) if t.strip()])
52        wikitags = TagEngine(self.env).tagspace.wiki
53        oldtags = wikitags.get_tags([page.name])
54
55        if oldtags != newtags:
56            wikitags.replace_tags(req, page.name, newtags)
57
58    def _wiki_edit(self, req, stream):
59        insert = T.div(class_='field')(
60            T.label(
61                'Tag under: (', T.a('view all tags', href=req.href.tags()), ')',
62                T.br(),
63                T.input(id='tags', type='text', name='tags', size='30',
64                        value=req.args.get('tags', ' '.join(self._page_tags(req)))),
65                )
66            )
67        return stream | Transformer('//div[@id="changeinfo1"]').append(insert)
68
69    # IPermissionRequestor methods
70    def get_permission_actions(self):
71        return ['TAGS_VIEW', 'TAGS_MODIFY']
72
73    # ITemplateStreamFilter methods
74    def filter_stream(self, req, method, filename, stream, data):
75        if filename == 'wiki_view.html' and 'TAGS_VIEW' in req.perm:
76            return self._wiki_view(req, stream)
77        elif filename == 'wiki_edit.html' and 'TAGS_MODIFY' in req.perm:
78            return self._wiki_edit(req, stream)
79        return stream
80
81    # IWikiPageManipulator methods
82    def prepare_wiki_page(self, req, page, fields):
83        pass
84
85    def validate_wiki_page(self, req, page):
86        if req and 'TAGS_MODIFY' in req.perm and req.path_info.startswith('/wiki') \
87                and 'save' in req.args:
88            self._update_tags(req, page)
89        return []
90
91
92class TagsModule(Component):
93    """ Serve a /tags namespace. Top-level displays tag cloud, sub-levels
94        display output of ListTagged(tag).
95
96        The following configuration options are supported:
97
98        [tags]
99        # Use a tag list or cloud for the main index
100        index = cloud|list
101        # The keyword arguments to pass to the TagCloud or ListTags macros that
102        # is being used for the index.
103        index.args = ...
104        # Keyword arguments to pass to the listing for each tag under the
105        # /tags/ URL space.
106        listing.args = ...
107    """
108    implements(IRequestHandler, INavigationContributor, ITemplateProvider)
109
110    # This method is never really called by anything.... Remove?
111    # def _prepare_wiki(self, req):
112    #     from tractags.api import TagEngine
113    #     page = req.path_info[6:] or 'WikiStart'
114    #     engine = TagEngine(self.env)
115    #     wikitags = engine.tagspace.wiki
116    #     tags = list(wikitags.get_tags(page))
117    #     tags.sort()
118    #
119    #     action = req.args.get('action', 'view')
120    #     if action == 'edit':
121    #         req.hdf['tags'] = req.args.get('tags', ', '.join(tags))
122    #     elif action == 'view':
123    #         hdf_tags = []
124    #         for tag in tags:
125    #             href, title = engine.get_tag_link(tag)
126    #             hdf_tags.append({'name': tag,
127    #                              'href': href,
128    #                              'title': title})
129    #         req.hdf['tags'] = hdf_tags
130
131    # ITemplateProvider methods
132    def get_templates_dirs(self):
133        """
134        Return the absolute path of the directory containing the provided
135        ClearSilver templates.
136        """
137        from pkg_resources import resource_filename
138        return [resource_filename(__name__, 'templates')]
139
140    def get_htdocs_dirs(self):
141        """Return the absolute path of a directory containing additional
142        static resources (such as images, style sheets, etc).
143        """
144        from pkg_resources import resource_filename
145        return [('tags', resource_filename(__name__, 'htdocs'))]
146
147    # INavigationContributor methods
148    def get_active_navigation_item(self, req):
149        if 'TAGS_VIEW' in req.perm:
150            return 'tags'
151
152    def get_navigation_items(self, req):
153        from trac.web.chrome import Chrome
154        if 'TAGS_VIEW' in req.perm:
155            yield ('mainnav', 'tags',
156                   Markup('<a href="%s" accesskey="T">Tags</a>',
157                          req.href.tags()))
158
159    # IRequestHandler methods
160    def match_request(self, req):
161        return 'TAGS_VIEW' in req.perm and req.path_info.startswith('/tags')
162
163    def process_request(self, req):
164        from tractags.macros import TagMacros
165        from tractags.parseargs import parseargs
166        from trac.web.chrome import add_stylesheet
167
168        req.perm.require('TAGS_VIEW')
169
170        add_stylesheet(req, 'tags/css/tractags.css')
171        data = {}
172
173        def update_from_req(args):
174            for k in req.args.keys():
175                args[k] = unicode(req.args.get(k))
176
177        if not req.args.has_key('e') and re.match('^/tags/?$', req.path_info):
178            index = self.env.config.get('tags', 'index', 'cloud')
179            index_kwargs = {'smallest': 10, 'biggest': 30}
180            _, config_kwargs = parseargs(self.env.config.get('tags', 'index.args', ''))
181            index_kwargs.update(config_kwargs)
182            update_from_req(index_kwargs)
183
184            if index == 'cloud':
185                data['tag_body'] = Markup(
186                    TagMacros(self.env).render_tagcloud(req, **index_kwargs))
187            elif index == 'list':
188                data['tag_body'] = Markup(
189                    TagMacros(self.env).render_listtagged(req, **index_kwargs))
190            else:
191                raise TracError("Invalid index style '%s'" % index)
192        else:
193            _, args = parseargs(self.env.config.get('tags', 'listing.args', ''))
194            if req.args.has_key('e'):
195                expr = req.args.get('e')
196            else:
197                expr = req.path_info[6:]
198            data['tag_title'] = Markup('Objects matching the expression <i>%s</i>' % escape(expr))
199            data['tag_expression'] = expr
200            try:
201                Expression(expr)
202            except Exception, e:
203                data['tag_expression_error'] = unicode(e).replace(' (line 1)', '')
204            args['expression'] = expr
205            tags = []
206            update_from_req(args)
207            data['tag_body'] = Markup(
208                TagMacros(self.env).render_listtagged(req, *tags, **args))
209        return 'tags.html', data, None
Note: See TracBrowser for help on using the repository browser.