source: tagsplugin/tags/0.3/tractags/macros.py

Last change on this file was 1795, checked in by Alec Thomas, 17 years ago
  • Fixed a bug with default macro arguments from the configuration file.
File size: 9.2 KB
Line 
1from trac.core import *
2from trac.wiki.api import IWikiMacroProvider
3from trac.wiki import model
4from trac.util import Markup
5from trac.wiki import wiki_to_html, wiki_to_oneliner
6from StringIO import StringIO
7from tractags.api import TagEngine, ITagSpaceUser, sorted, set
8import inspect
9import re
10import string
11
12class TagMacros(Component):
13    """ Versions of the old Wiki-only macros using the new tag API. """
14
15    implements(IWikiMacroProvider)
16
17    def _page_titles(self, pages):
18        """ Extract page titles, if possible. """
19        titles = {}
20        tagspace = TagEngine(self.env).tagspace.wiki
21        for pagename in pages:
22            href, link, title = tagspace.name_details(pagename)
23            titles[pagename] = title
24        return titles
25
26    def _tag_details(self, links, tags):
27        """ Extract dictionary of tag:(href, title) for all tags. """
28        for tag in tags:
29            if tag not in links:
30                links[tag] = TagEngine(self.env).get_tag_link(tag)
31        return links
32
33    def _current_page(self, req):
34        return req.hdf.getValue('wiki.page_name', '')
35
36    # IWikiMacroProvider methods
37    def get_macros(self):
38        yield 'TagCloud'
39        yield 'ListTagged'
40        yield 'TagIt'
41        yield 'ListTags'
42
43    def get_macro_description(self, name):
44        import pydoc
45        return pydoc.getdoc(getattr(self, 'render_' + name.lower()))
46
47    def render_macro(self, req, name, content):
48        from trac.web.chrome import add_stylesheet
49        from parseargs import parseargs
50        add_stylesheet(req, 'tags/css/tractags.css')
51        # Translate macro args into python args
52        args = []
53        kwargs = {}
54        try:
55            # Set default args from config
56            _, config_args = parseargs(self.env.config.get('tags', '%s.args' % name.lower(), ''))
57            kwargs.update(config_args)
58            if content is not None:
59                args, macro_args = parseargs(content)
60                kwargs.update(macro_args)
61        except Exception, e:
62            raise TracError("Invalid arguments '%s' (%s %s)" % (content, e.__class__.__name__, e))
63
64        return getattr(self, 'render_' + name.lower(), content)(req, *args, **kwargs)
65
66    # Macro implementations
67    def render_tagcloud(self, req, smallest=10, biggest=20, showcount=True, tagspace=None, mincount=1, tagspaces=[]):
68        """ This macro displays a [http://en.wikipedia.org/wiki/Tag_cloud tag cloud] (weighted list)
69            of all tags.
70
71            ||'''Argument'''||'''Description'''||
72            ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.||
73            ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.||
74            ||`smallest=<n>`||The lower bound of the font size for the tag cloud.||
75            ||`biggest=<n>`||The upper bound of the font size for the tag cloud.||
76            ||`showcount=true|false`||Show the count of objects for each tag?||
77            ||`mincount=<n>`||Hide tags with a count less than `<n>`.||
78            """
79
80        smallest = int(smallest)
81        biggest = int(biggest)
82        mincount = int(mincount)
83
84        engine = TagEngine(self.env)
85        # Get wiki tagspace
86        if tagspace:
87            tagspaces = [tagspace]
88        else:
89            tagspaces = tagspaces or engine.tagspaces
90        cloud = {}
91
92        for tag, names in engine.get_tags(tagspaces=tagspaces, detailed=True).iteritems():
93            count = len(names)
94            if count >= mincount:
95                cloud[tag] = len(names)
96
97        tags = cloud.keys()
98
99        # No tags?
100        if not tags: return ''
101
102        # by_count maps tag counts to an index in the set of counts
103        by_count = list(set(cloud.values()))
104        by_count.sort()
105        by_count = dict([(c, float(i)) for i, c in enumerate(by_count)])
106
107        taginfo = self._tag_details({}, tags)
108        tags.sort()
109        rlen = float(biggest - smallest)
110        tlen = float(len(by_count))
111        scale = 1.0
112        if tlen:
113            scale = rlen / tlen
114        out = StringIO()
115        out.write('<ul class="tagcloud">\n')
116        last = tags[-1]
117        for tag in tags:
118            if tag == last:
119                cls = ' class="last"'
120            else:
121                cls = ''
122            if showcount != 'false':
123                count = ' <span class="tagcount">(%i)</span>' % cloud[tag]
124            else:
125                count = ''
126            out.write('<li%s><a rel="tag" title="%s" style="font-size: %ipx" href="%s">%s</a>%s</li>\n' % (
127                       cls,
128                       taginfo[tag][1],
129                       smallest + int(by_count[cloud[tag]] * scale),
130                       taginfo[tag][0],
131                       tag,
132                       count))
133        out.write('</ul>\n')
134        return out.getvalue()
135
136    def render_listtagged(self, req, *tags, **kwargs):
137        """ List tagged objects. Optionally accepts a list of tags to match
138            against.  The special tag '''. (dot)''' inserts the current Wiki page name.
139
140            `[[ListTagged(<tag>, ...)]]`
141
142            ||'''Argument'''||'''Description'''||
143            ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.||
144            ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.||
145            ||`operation=intersection|union`||The set operation to perform on the discovered objects.||
146            ||`showheadings=true|false`||List objects under the tagspace they occur in.||
147        """
148
149        if 'tagspace' in kwargs:
150            tagspaces = [kwargs.get('tagspace', None)]
151        else:
152            tagspaces = kwargs.get('tagspaces', '') or \
153                        list(TagEngine(self.env).tagspaces)
154        showheadings = kwargs.get('showheadings', 'false')
155        operation = kwargs.get('operation', 'intersection')
156        if operation not in ('union', 'intersection'):
157            raise TracError("Invalid tag set operation '%s'" % operation)
158
159        engine = TagEngine(self.env)
160        page_name = req.hdf.get('wiki.page_name')
161        if page_name:
162            tags = [tag == '.' and page_name or tag for tag in tags]
163
164        taginfo = {}
165        out = StringIO()
166        out.write('<ul class="listtagged">')
167        # Cull empty names
168        tagged_names = [(tagspace, names) for tagspace, names in
169                        engine.get_tagged_names(tags=tags, tagspaces=tagspaces,
170                            operation=operation, detailed=True).iteritems()
171                        if names]
172        for tagspace, tagspace_names in sorted(tagged_names):
173            if showheadings == 'true':
174                out.write('<lh>%s tags</lh>' % tagspace)
175            for name, tags in sorted(tagspace_names.iteritems()):
176                if tagspace == 'wiki' and unicode(name).startswith('tags/'): continue
177                tags = sorted(tags)
178                taginfo = self._tag_details(taginfo, tags)
179                href, link, title = engine.name_details(tagspace, name)
180                htitle = wiki_to_oneliner(title, self.env)
181                name_tags = ['<a href="%s" title="%s">%s</a>'
182                              % (taginfo[tag][0], taginfo[tag][1], tag)
183                              for tag in tags]
184                if not name_tags:
185                    name_tags = ''
186                else:
187                    name_tags = ' (' + ', '.join(sorted(name_tags)) + ')'
188                out.write('<li>%s %s%s</li>\n' %
189                          (link, htitle, name_tags))
190        out.write('</ul>')
191
192        return out.getvalue()
193
194    def render_tagit(self, req, *tags):
195        """ '''''Deprecated. Does nothing.''''' """
196        return ''
197
198    def render_listtags(self, req, *tags, **kwargs):
199        """ List all tags.
200
201            ||'''Argument'''||'''Description'''||
202            ||`tagspace=<tagspace>`||Specify the tagspace the macro should operate on.||
203            ||`tagspaces=(<tagspace>,...)`||Specify a set of tagspaces the macro should operate on.||
204            ||`shownames=true|false`||Whether to show the objects that tags appear on ''(long)''.||
205            """
206
207        if tags:
208            # Backwards compatibility
209            return self.render_listtagged(req, *tags, **kwargs)
210
211        page = self._current_page(req)
212        engine = TagEngine(self.env)
213
214        showpages = kwargs.get('showpages', None) or kwargs.get('shownames', 'false')
215
216        if 'tagspace' in kwargs:
217            tagspaces = [kwargs['tagspace']]
218        else:
219            tagspaces = kwargs.get('tagspaces', []) or \
220                        list(TagEngine(self.env).tagspaces)
221
222        out = StringIO()
223        out.write('<ul class="listtags">\n')
224        tag_details = {}
225        for tag, names in sorted(engine.get_tags(tagspaces=tagspaces, detailed=True).iteritems()):
226            href, title = engine.get_tag_link(tag)
227            htitle = wiki_to_oneliner(title, self.env)
228            out.write('<li><a href="%s" title="%s">%s</a> %s <span class="tagcount">(%i)</span>' % (href, title, tag, htitle, len(names)))
229            if showpages == 'true':
230                out.write('\n')
231                out.write(self.render_listtagged(req, tag, tagspaces=tagspaces))
232                out.write('</li>\n')
233
234        out.write('</ul>\n')
235
236        return out.getvalue()
Note: See TracBrowser for help on using the repository browser.