source: trac/trunk/wiki-macros/TracNav.py @ 3056

Revision 3056, 9.1 KB checked in by moschny, 8 years ago (diff)
  • Bugfix: Uncaught StopIteration, in case the toc is empty.
  • Fix many pylint warnings (long lines, conventions for variable and method names, unused variables and imports, docstrings).
  • Property svn:keywords set to Id
Line 
1# -*- coding: utf-8 -*-
2"""
3= TracNav: The navigation bar for Trac =
4
5This macro implements a fully customizable navigation bar for the Trac
6wiki engine. The contents of the navigation bar is a wiki page itself
7and can be edited like any other wiki page through the web
8interface. The navigation bar supports hierarchical ordering of
9topics. The design of TracNav mimics the design of the TracGuideToc
10that was originally supplied with Trac. The drawback of TracGuideToc
11is that it is not customizable without editing its source code and
12that it does not support hierarchical ordering.
13
14
15== Installation ==
16
17To install TracNav, place the file `TracNav.py` in the `wiki-macros`
18subdirectory and the accompanying `tracnav.css` file in the
19`templates` subdirectory of your Trac project. Add this line
20{{{
21@import url(<?cs var:chrome.href ?>/site/tracnav.css);
22}}}
23to the `templates/site_css.cs` file of your Trac project.
24
25The `tracnav.css` file defines the styles for displaying the
26navigation bar. These styles build upon the styles for !TracGuideToc
27that come with your Trac distribution. If you just install the macro
28but miss to install the style file, TracNav will work but look
29somewhat strange.
30
31
32== Usage ==
33
34To use TracNav, create an index page for your site and call the
35TracNav macro on each page, where the navigation bar should be
36displayed. The index page is a regular wiki page. The page with the
37table of contents must include an unordered list of links that should
38be displayed in the navigation bar.
39
40To display the navigation bar on a page, you must call the TracNav
41macro on that page an pass the name of your table of contents as
42argument.
43
44
45== Author and license ==
46
47Copyright 2005 Bernhard Haumacher (haui at haumacher.de)
48
49{{{
50This program is free software; you can redistribute it and/or modify
51it under the terms of the GNU General Public License as published by
52the Free Software Foundation; either version 2 of the License, or
53(at your option) any later version.
54
55This program is distributed in the hope that it will be useful,
56but WITHOUT ANY WARRANTY; without even the implied warranty of
57MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
58GNU General Public License for more details.
59
60You should have received a copy of the GNU General Public License
61along with this program; if not, write to the Free Software
62Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
63}}}
64
65== Additional information and a life example ==
66
67Please visit: http://svn.ipd.uka.de/trac/javaparty/wiki/TracNav
68"""
69
70__revision__ = "$Id$"
71
72import re
73from trac.wiki.api import WikiSystem
74from trac.wiki.model import WikiPage
75
76LISTRULE = re.compile(r"""^(?P<indent> *)\* +"""
77                      r"""(?:(?P<wikilink>\[wiki:"""
78                      r"""(?P<link>(&#34;([^&#34;]*)&#34;|"""
79                      r"""'([^']*)')|([^ \]]+))"""
80                      r"""+(?P<label>[^\]]*)\])|(?P<text>.*))""",
81                      re.M)
82
83TRACNAVHOME = "http://svn.ipd.uka.de/trac/javaparty/wiki/TracNav"
84
85def get_toc(hdf, env, curpage, name):
86    """Fetch the wiki page containing the toc, if available."""
87   
88    toc_text = "* Table of contents"
89
90    preview = hdf.getValue('args.preview', "")
91    if preview and (name == curpage):
92        toc_text = hdf.getValue('wiki.page_source', toc_text);
93    else:
94        if WikiSystem(env).has_page(name):
95            toc_text = WikiPage(env, name).text
96
97    # env.log.debug(tocText)
98    return toc_text
99
100
101def get_toc_entry(toc_text):
102    """Generator: returns the next toc entry. Each toc entry consists
103    of it's indentation level, label and link."""
104    next_pos = 0
105    while 1:
106        match = LISTRULE.search(toc_text, next_pos)
107        if not match:
108            # env.log.debug("No more matches")
109            return
110
111        indent = len(match.group('indent'))
112        if match.group('wikilink'):
113            link = match.group('link')
114            label = match.group('label')
115        else:
116            link = None
117            label = match.group('text')
118
119        # if link == None:
120        #     env.log.debug(label + " ---")
121        # else:
122        #     env.log.debug(label + ": " + link)
123   
124        yield indent, link, label
125        next_pos = match.end()
126
127
128def get_toc_entry_and_indent(gen):
129    """Generator, to be used as a filter for get_toc_entry().  Returns
130    link and label of the current toc entry and the indentation level
131    of the next entry or -1 if there are no more entries. The first
132    call to next() returns the indentation of the first entry."""
133   
134    try:
135        indent, link, label = gen.next()
136    except StopIteration:
137        indent = -1
138    yield indent
139
140    ready = False
141    while not ready:
142        yield link, label
143        try:
144            indent, link, label = gen.next()
145        except StopIteration:
146            indent, ready = -1, True
147        yield indent
148
149
150def _parse_toc(gen, next_indent, level = 0):
151    toclist = []
152    if next_indent > level:
153        sublist, next_indent = _parse_toc(gen, next_indent, level + 1)
154        if next_indent < level:
155            # level is empty
156            return sublist, next_indent
157        else:
158            # broken indentation structure
159            toclist.append((None, None, sublist))
160    while 1:
161        if next_indent == level:
162            (link, label), next_indent = gen.next(), gen.next()
163            if next_indent > level:
164                sublist, next_indent = _parse_toc(gen, next_indent, level + 1)
165                toclist.append((link, label, sublist))
166            else:
167                toclist.append((link, label, None))
168        else:
169            assert next_indent < level
170            return toclist, next_indent
171
172
173def parse_toc(toc_text):
174    gen = get_toc_entry_and_indent(get_toc_entry(toc_text))
175    toclist, _ = _parse_toc(gen, gen.next())
176    return toclist
177   
178
179def execute(hdf, args, env):
180    preview = hdf.getValue('args.preview', "")
181    curpage = hdf.getValue('wiki.page_name', "")
182    name = args
183    if not name:
184        name = 'TOC'
185
186    toc = parse_toc(get_toc(hdf, env, curpage, name))
187    if not toc:
188        msg = '<div class="system-message">' \
189              "<strong>Error: Table of contents does not exist or is empty."
190        if (not preview) and (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
191            msg += ' Click here to <a href="%s?edit=yes">edit</a>.' % \
192                   env.href.wiki(name)
193        msg += '</strong></div>\n'
194        return msg
195
196    (found, filtered) = filter_toc(curpage, toc, 0)
197    if found:
198        return display_all(hdf, env, name, curpage, filtered, 0)
199    else:
200        return display_all(hdf, env, name, curpage, toc, 0)
201
202
203def filter_toc(curpage, toc, level):
204    found = 0
205    result = []
206    for name, title, sub in toc:
207        if sub == None:
208            if name == curpage:
209                found = 1
210            result.append((name, title, None))
211        else:
212            (subfound, subtoc) = filter_toc(curpage, sub, level + 1)
213            if subfound:
214                found = 1
215            if subfound or (name == None):
216                if level == 0 and name != None:
217                    prepended = [(name, title, subtoc)]
218                    prepended.extend(result)
219                    result = prepended
220                else:
221                    result.append((name, title, subtoc))
222            else:
223                result.append((name, title, []))
224    return (found, result)
225
226def indentation(col):
227    return ' ' * col
228
229def display_all(hdf, env, name, curpage, toc, col):
230    preview = hdf.getValue('args.preview', "")
231    html = '%s<div class="wiki-toc trac-nav">\n' % indentation(col)
232    col += 1
233    html += '%s<h2><a href="%s">TracNav</a> menu</h2>' % \
234            (indentation(col), TRACNAVHOME)
235
236    if (not preview) and hdf.getValue('trac.acl.WIKI_MODIFY', ''):
237        html += '%s<div class="edit"><a href="%s?edit=yes">edit</a></div>\n' % \
238                (indentation(col), env.href.wiki(name))
239    html += '%s<ul>\n' % indentation(col)
240    col += 1
241    html += display(env, curpage, toc, 0, col)
242    col -= 1
243    html += '%s</ul>\n' % indentation(col)
244    col -= 1
245    html += '%s</div>\n' % indentation(col)
246    return html
247
248def display(env, curpage, toc, depth, col):
249    html = ''
250    for name, title, sub in toc:
251        li_style = ' style="padding-left: %dem;"' % (depth + 1)
252        if sub == None:
253            if name == curpage:
254                cls = ' class="active"'
255            else:
256                cls = ''
257            html += '%s<li%s%s>' % (indentation(col), li_style, cls)
258            if name == None:
259                html += title
260            else:
261                html += '<a href="%s">%s</a>' % (env.href.wiki(name), title)
262            html += '</li>\n'
263        else:
264            html += '%s<li%s>\n' % (indentation(col), li_style)
265            col += 1
266            if name == None or len(sub) > 0:
267                html += '%s<h4>%s</h4>\n' % (indentation(col), title)
268            else:
269                html += '%s<h4><a href="%s">%s</a>...</h4>\n' % \
270                        (indentation(col), env.href.wiki(name), title)
271            col -= 1
272            html += '%s</li>\n' % indentation(col)
273            if len(sub) > 0:
274                html += display(env, curpage, sub, depth + 1, col)
275    return html
276
Note: See TracBrowser for help on using the repository browser.