| 1 | # Copyright David Abrahams 2007. Distributed under the Boost |
|---|
| 2 | # Software License, Version 1.0. (See accompanying |
|---|
| 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 4 | from datetime import datetime |
|---|
| 5 | import imp |
|---|
| 6 | import inspect |
|---|
| 7 | import os |
|---|
| 8 | import re |
|---|
| 9 | from StringIO import StringIO |
|---|
| 10 | |
|---|
| 11 | from genshi.builder import Element, tag |
|---|
| 12 | from genshi.core import Markup |
|---|
| 13 | |
|---|
| 14 | from trac.core import * |
|---|
| 15 | from trac.util.datefmt import format_date, utc |
|---|
| 16 | from trac.util.compat import sorted, groupby, any, set |
|---|
| 17 | from trac.util.html import escape |
|---|
| 18 | from trac.util.translation import _ |
|---|
| 19 | from trac.wiki.api import IWikiMacroProvider, WikiSystem, parse_args |
|---|
| 20 | from trac.wiki.formatter import format_to_html, format_to_oneliner, \ |
|---|
| 21 | extract_link, OutlineFormatter |
|---|
| 22 | from trac.wiki.model import WikiPage |
|---|
| 23 | from trac.web.chrome import add_stylesheet |
|---|
| 24 | |
|---|
| 25 | from trac.wiki.macros import WikiMacroBase |
|---|
| 26 | |
|---|
| 27 | __all__ = [ 'MyTitleIndexMacro' ] |
|---|
| 28 | |
|---|
| 29 | class MyTitleIndexMacro(WikiMacroBase): |
|---|
| 30 | """Inserts an alphabetic list of all wiki pages into the output. |
|---|
| 31 | |
|---|
| 32 | Accepts a prefix string as parameter: if provided, only pages with names |
|---|
| 33 | that start with the prefix are included in the resulting list. If this |
|---|
| 34 | parameter is omitted, all pages are listed. |
|---|
| 35 | |
|---|
| 36 | Alternate `format` and `depth` can be specified: |
|---|
| 37 | - `format=group`: The list of page will be structured in groups |
|---|
| 38 | according to common prefix. This format also supports a `min=n` |
|---|
| 39 | argument, where `n` is the minimal number of pages for a group. |
|---|
| 40 | - `depth=n`: limit the depth of the pages to list. If set to 0, |
|---|
| 41 | n only toplevel pages will be shown, if set to 1, only immediate |
|---|
| 42 | children pages will be shown, etc. If not set, or set to -1, |
|---|
| 43 | all pages in the hierarchy will be shown. |
|---|
| 44 | """ |
|---|
| 45 | |
|---|
| 46 | SPLIT_RE = re.compile(r"( |/|[0-9])") |
|---|
| 47 | |
|---|
| 48 | def expand_macro(self, formatter, name, content): |
|---|
| 49 | args, kw = parse_args(content) |
|---|
| 50 | prefix = args and args[0] or None |
|---|
| 51 | format = kw.get('format', '') |
|---|
| 52 | hideprefix = kw.get('hideprefix', None) |
|---|
| 53 | minsize = max(int(kw.get('min', 2)), 2) |
|---|
| 54 | depth = int(kw.get('depth', -1)) |
|---|
| 55 | start = prefix and prefix.count('/') or 0 |
|---|
| 56 | |
|---|
| 57 | wiki = formatter.wiki |
|---|
| 58 | pages = sorted(wiki.get_pages(prefix)) |
|---|
| 59 | |
|---|
| 60 | if format != 'group': |
|---|
| 61 | return tag.ul([tag.li(tag.a(wiki.format_page_name(page), |
|---|
| 62 | href=formatter.href.wiki(page))) |
|---|
| 63 | for page in pages |
|---|
| 64 | if depth < 0 or depth >= page.count('/') - start]) |
|---|
| 65 | |
|---|
| 66 | def lstripstring(s, start): |
|---|
| 67 | if s.startswith(start): |
|---|
| 68 | return s[len(start):] |
|---|
| 69 | else: |
|---|
| 70 | return s |
|---|
| 71 | |
|---|
| 72 | # the string to strip off the beginning of each displayed page title |
|---|
| 73 | strip_prefix = hideprefix and prefix or '' |
|---|
| 74 | |
|---|
| 75 | # How many elements of each title should be stripped? This eliminates |
|---|
| 76 | # upper levels that have no siblings. Otherwise, when stripping |
|---|
| 77 | # Foo/Bar/ you get something like: |
|---|
| 78 | # |
|---|
| 79 | # * Foo/ |
|---|
| 80 | # * Bar/ |
|---|
| 81 | # * x1 |
|---|
| 82 | # * x2 |
|---|
| 83 | # * ... |
|---|
| 84 | prefix_parts = hideprefix and len(self.SPLIT_RE.split(strip_prefix)) or 0 |
|---|
| 85 | |
|---|
| 86 | # Group by Wiki word and/or Wiki hierarchy |
|---|
| 87 | pages = [ |
|---|
| 88 | (self.SPLIT_RE.split( |
|---|
| 89 | wiki.format_page_name( |
|---|
| 90 | page, split=True) |
|---|
| 91 | )[prefix_parts:], |
|---|
| 92 | page) |
|---|
| 93 | |
|---|
| 94 | for page in pages |
|---|
| 95 | if depth < 0 or depth >= page.count('/') - start] |
|---|
| 96 | |
|---|
| 97 | def split_in_groups(group): |
|---|
| 98 | """Return list of pagename or (key, sublist) elements""" |
|---|
| 99 | groups = [] |
|---|
| 100 | for key, subgrp in groupby(group, lambda (k,p): k and k[0] or ''): |
|---|
| 101 | subgrp = [(k[1:],p) for k,p in subgrp] |
|---|
| 102 | if key and len(subgrp) >= minsize: |
|---|
| 103 | sublist = split_in_groups(sorted(subgrp)) |
|---|
| 104 | if len(sublist) == 1: |
|---|
| 105 | elt = (key+sublist[0][0], sublist[0][1]) |
|---|
| 106 | else: |
|---|
| 107 | elt = (key, sublist) |
|---|
| 108 | groups.append(elt) |
|---|
| 109 | else: |
|---|
| 110 | for elt in subgrp: |
|---|
| 111 | groups.append(elt[1]) |
|---|
| 112 | return groups |
|---|
| 113 | |
|---|
| 114 | def render_groups(groups): |
|---|
| 115 | return tag.ul( |
|---|
| 116 | [tag.li(isinstance(elt, tuple) and |
|---|
| 117 | tag(tag.strong(elt[0]), render_groups(elt[1])) or |
|---|
| 118 | tag.a( |
|---|
| 119 | lstripstring(wiki.format_page_name(elt),strip_prefix), |
|---|
| 120 | href=formatter.href.wiki(elt))) |
|---|
| 121 | for elt in groups]) |
|---|
| 122 | |
|---|
| 123 | return render_groups(split_in_groups(pages)) |
|---|