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

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

TagsPlugin:

Fix for XSS vulnerability. Closes #1581.

File size: 7.9 KB
Line 
1from trac.core import *
2from trac.web.main import IRequestHandler
3from trac.web.chrome import ITemplateProvider, INavigationContributor
4from trac.util import Markup, escape
5from StringIO import StringIO
6from trac.wiki.web_ui import WikiModule
7from trac.wiki.formatter import wiki_to_oneliner
8from tractags.expr import Expression
9import re
10try:
11    set = set
12except:
13    from sets import Set as set
14
15_tag_split = re.compile('[,\s]+')
16
17class TagsWikiModule(WikiModule):
18    """ Replacement for the default Wiki module. Tag editing is much more
19        intuitive now, as it no longer requires the TagIt macro and JavaScript
20        magic. """
21
22    def _do_save(self, req, db, page):
23        # This method is overridden so the user doesn't get "Page not modified"
24        # exceptions when updating tags but not wiki content.
25        from tractags.api import TagEngine
26        if 'tags' in req.args:
27            newtags = set([t.strip() for t in
28                          _tag_split.split(req.args.get('tags')) if t.strip()])
29            wikitags = TagEngine(self.env).tagspace.wiki
30            oldtags = wikitags.get_tags([page.name])
31
32            if oldtags != newtags:
33                wikitags.replace_tags(req, page.name, newtags)
34                # No changes, just redirect
35                if req.args.get('text') == page.text:
36                    req.redirect(self.env.href.wiki(page.name))
37                    return
38        return WikiModule._do_save(self, req, db, page)
39
40    def process_request(self, req):
41        from tractags.api import TagEngine
42        from trac.web.chrome import add_stylesheet
43
44        add_stylesheet(req, 'tags/css/tractags.css')
45
46        pagename = req.args.get('page', 'WikiStart')
47        action = req.args.get('action', 'view')
48
49        engine = TagEngine(self.env)
50        wikitags = engine.tagspace.wiki
51        tags = list(wikitags.get_tags([pagename]))
52        tags.sort()
53
54        if action == 'edit':
55            req.hdf['tags'] = req.args.get('tags', ', '.join(tags))
56        elif action == 'view':
57            hdf_tags = []
58            for tag in tags:
59                href, title = engine.get_tag_link(tag)
60                hdf_tags.append({'name': tag,
61                                 'href': href,
62                                 'title': title})
63            req.hdf['tags'] = hdf_tags
64        result = WikiModule.process_request(self, req)
65        if result is None:
66            return None
67        if result[0] == 'wiki.cs':
68            return 'tagswiki.cs', None
69        return result
70
71class TagsModule(Component):
72    """ Serve a /tags namespace. Top-level displays tag cloud, sub-levels
73        display output of ListTagged(tag).
74
75        The following configuration options are supported:
76
77        [tags]
78        # Use a tag list or cloud for the main index
79        index = cloud|list
80        # The keyword arguments to pass to the TagCloud or ListTags macros that
81        # is being used for the index.
82        index.args = ...
83        # Keyword arguments to pass to the listing for each tag under the
84        # /tags/ URL space.
85        listing.args = ...
86    """
87    implements(IRequestHandler, INavigationContributor, ITemplateProvider)
88
89    def _prepare_wiki(self, req):
90        from tractags.api import TagEngine
91        page = req.path_info[6:] or 'WikiStart'
92        engine = TagEngine(self.env)
93        wikitags = engine.tagspace.wiki
94        tags = list(wikitags.get_tags(page))
95        tags.sort()
96
97        action = req.args.get('action', 'view')
98        if action == 'edit':
99            req.hdf['tags'] = req.args.get('tags', ', '.join(tags))
100        elif action == 'view':
101            hdf_tags = []
102            for tag in tags:
103                href, title = engine.get_tag_link(tag)
104                hdf_tags.append({'name': tag,
105                                 'href': href,
106                                 'title': title})
107            req.hdf['tags'] = hdf_tags
108
109    # ITemplateProvider methods
110    def get_templates_dirs(self):
111        """
112        Return the absolute path of the directory containing the provided
113        ClearSilver templates.
114        """
115        from pkg_resources import resource_filename
116        return [resource_filename(__name__, 'templates')]
117
118    def get_htdocs_dirs(self):
119        """Return the absolute path of a directory containing additional
120        static resources (such as images, style sheets, etc).
121        """
122        from pkg_resources import resource_filename
123        return [('tags', resource_filename(__name__, 'htdocs'))]
124   
125    # INavigationContributor methods
126    def get_active_navigation_item(self, req):
127        return 'tags'
128
129    def get_navigation_items(self, req):
130        from trac.web.chrome import Chrome
131        yield ('mainnav', 'tags',
132               Markup('<a href="%s" accesskey="T">Tags</a>',
133                      self.env.href.tags()))
134
135    # IRequestHandler methods
136    def match_request(self, req):
137        return req.path_info.startswith('/tags')
138
139    def process_request(self, req):
140        from tractags.macros import TagMacros
141        from tractags.parseargs import parseargs
142        from trac.web.chrome import add_stylesheet
143
144        add_stylesheet(req, 'tags/css/tractags.css')
145        req.hdf['trac.href.tags'] = self.env.href.tags()
146
147        def update_from_req(args):
148            for k in req.args.keys():
149                args[str(k)] = str(req.args.get(k))
150
151        if not req.args.has_key('e') and re.match('^/tags/?$', req.path_info):
152            index = self.env.config.get('tags', 'index', 'cloud')
153            index_kwargs = {'smallest': 10, 'biggest': 30}
154            _, config_kwargs = parseargs(self.env.config.get('tags', 'index.args', ''))
155            index_kwargs.update(config_kwargs)
156            update_from_req(index_kwargs)
157
158            if index == 'cloud':
159                req.hdf['tag.body'] = Markup(
160                    TagMacros(self.env).render_tagcloud(req, **index_kwargs))
161            elif index == 'list':
162                req.hdf['tag.body'] = Markup(
163                    TagMacros(self.env).render_listtagged(req, **index_kwargs))
164            else:
165                raise TracError("Invalid index style '%s'" % index)
166        else:
167            _, args = parseargs(self.env.config.get('tags', 'listing.args', ''))
168            if req.args.has_key('e'):
169                expr = req.args.get('e')
170            else:
171                expr = req.path_info[6:]
172            req.hdf['tag.title'] = Markup('Objects matching the expression <i>%s</i>' % escape(expr))
173            req.hdf['tag.expression'] = expr
174            try:
175                Expression(expr)
176            except Exception, e:
177                req.hdf['tag.expression.error'] = str(e).replace(' (line 1)', '')
178            args['expression'] = expr
179            tags = []
180            update_from_req(args)
181            req.hdf['tag.body'] = Markup(
182                TagMacros(self.env).render_listtagged(req, *tags, **args))
183        return 'tags.cs', None
184
185# XXX I think this is planned for some AJAX goodness, commenting out for now. (Alec) XXX
186#class TagsLi(Component):
187#    implements(IRequestHandler)
188#   
189#    # IRequestHandler methods
190#    def match_request(self, req):
191#        return req.path_info == '/tagli'
192#               
193#    def process_request(self, req):
194#        db = self.env.get_db_cnx()
195#        cursor = db.cursor()
196#        cs = db.cursor()
197#        tag = req.args.get('tag')
198#        req.send_response(200)
199#        req.send_header('Content-Type', 'text/plain')
200#        req.end_headers()
201#        buf = StringIO()
202#        if tag:
203#            buf.write('WHERE tag LIKE \'%s%s\'' % (tag,'%'))
204#           
205#        cursor.execute('SELECT DISTINCT tag FROM tags %s ORDER BY tag' % (buf.getvalue()))
206#
207#        msg = StringIO()
208#
209#        msg.write('<ul>')
210#        while 1:
211#            row = cursor.fetchone()
212#            if row == None:
213#                 break
214#
215#            t = row[0]
216#            msg.write('<li>')
217#            msg.write(t)
218#            msg.write('</li>')
219#
220#        msg.write('</ul>')
221#        req.write(msg.getvalue())
Note: See TracBrowser for help on using the repository browser.