| 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 | from trac.resource import Resource, get_resource_url, render_resource_link |
|---|
| 10 | from trac.wiki.macros import WikiMacroBase |
|---|
| 11 | from trac.util.compat import sorted, set |
|---|
| 12 | from tractags.api import TagSystem |
|---|
| 13 | from genshi.builder import tag as builder |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | def render_cloud(env, req, cloud, renderer=None): |
|---|
| 17 | """Render a tag cloud |
|---|
| 18 | |
|---|
| 19 | :cloud: Dictionary of {object: count} representing the cloud. |
|---|
| 20 | :param renderer: A callable with signature (tag, count, percent) used to |
|---|
| 21 | render the cloud objects. |
|---|
| 22 | """ |
|---|
| 23 | min_px = 10.0 |
|---|
| 24 | max_px = 30.0 |
|---|
| 25 | scale = 1.0 |
|---|
| 26 | |
|---|
| 27 | if renderer is None: |
|---|
| 28 | def default_renderer(tag, count, percent): |
|---|
| 29 | href = get_resource_url(env, Resource('tag', tag), req.href) |
|---|
| 30 | return builder.a(tag, rel='tag', title='%i' % count, href=href, |
|---|
| 31 | style='font-size: %ipx' % |
|---|
| 32 | int(min_px + percent * (max_px - min_px))) |
|---|
| 33 | renderer = default_renderer |
|---|
| 34 | |
|---|
| 35 | # A LUT from count to n/len(cloud) |
|---|
| 36 | size_lut = dict([(c, float(i)) for i, c in |
|---|
| 37 | enumerate(sorted(set([r for r in cloud.values()])))]) |
|---|
| 38 | if size_lut: |
|---|
| 39 | scale = 1.0 / len(size_lut) |
|---|
| 40 | |
|---|
| 41 | ul = builder.ul(class_='tagcloud') |
|---|
| 42 | last = len(cloud) - 1 |
|---|
| 43 | for i, (tag, count) in enumerate(sorted(cloud.iteritems())): |
|---|
| 44 | percent = size_lut[count] * scale |
|---|
| 45 | li = builder.li(renderer(tag, count, percent)) |
|---|
| 46 | if i == last: |
|---|
| 47 | li(class_='last') |
|---|
| 48 | li() |
|---|
| 49 | ul(li) |
|---|
| 50 | return ul |
|---|
| 51 | |
|---|
| 52 | |
|---|
| 53 | class TagCloudMacro(WikiMacroBase): |
|---|
| 54 | def expand_macro(self, formatter, name, content): |
|---|
| 55 | req = formatter.req |
|---|
| 56 | query_result = TagSystem(self.env).query(req, content) |
|---|
| 57 | all_tags = {} |
|---|
| 58 | # Find tag counts |
|---|
| 59 | for resource, tags in query_result: |
|---|
| 60 | for tag in tags: |
|---|
| 61 | try: |
|---|
| 62 | all_tags[tag] += 1 |
|---|
| 63 | except KeyError: |
|---|
| 64 | all_tags[tag] = 1 |
|---|
| 65 | return render_cloud(self.env, req, all_tags) |
|---|
| 66 | |
|---|
| 67 | |
|---|
| 68 | |
|---|
| 69 | class ListTaggedMacro(WikiMacroBase): |
|---|
| 70 | def expand_macro(self, formatter, name, content): |
|---|
| 71 | req = formatter.req |
|---|
| 72 | query_result = TagSystem(self.env).query(req, content) |
|---|
| 73 | |
|---|
| 74 | def link(resource): |
|---|
| 75 | return render_resource_link(self.env, formatter.context, |
|---|
| 76 | resource, 'compact') |
|---|
| 77 | |
|---|
| 78 | ul = builder.ul(class_='taglist') |
|---|
| 79 | # Beware: Resources can be Unicode strings. |
|---|
| 80 | for resource, tags in sorted(query_result, key=lambda r: r[0].id): |
|---|
| 81 | tags = sorted(tags) |
|---|
| 82 | if tags: |
|---|
| 83 | rendered_tags = [ |
|---|
| 84 | link(resource('tag', tag)) |
|---|
| 85 | for tag in tags |
|---|
| 86 | ] |
|---|
| 87 | li = builder.li(link(resource), ' (', rendered_tags[0], |
|---|
| 88 | [(' ', tag) for tag in rendered_tags[1:]], |
|---|
| 89 | ')') |
|---|
| 90 | else: |
|---|
| 91 | li = builder.li(link(resource)) |
|---|
| 92 | ul(li, '\n') |
|---|
| 93 | return ul |
|---|