Edgewall Software

MacroBazaar: TracNav.2.py

File TracNav.2.py, 8.2 KB (added by haui at haumacher.de, 10 years ago)

Updated navigation bar for Trac (wiki-customizable hierarchical replacement for TracGuideToc). Revision 2939.

Line 
1# -*- coding: iso8859-1 -*-
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, you must place the file {{{TracNav.py}}} somewhere
18in your {{{wiki-macros/}}} directory of your Trac
19installation. Additionally, you have to append the following lines to
20the file {{{htdocs/css/wiki.css}}} that can also be found in your Trac
21directoryhierarchy.
22
23{{{
24/* Styles for TracNav */
25.wiki-toc.trac-nav h4 { margin: 0; padding: 0; }
26.wiki-toc.trac-nav ul > li:first-child { margin-right: 4ex; }
27.wiki-toc.trac-nav .edit {
28 position: absolute;
29 top: 2px;
30 right: 3px;
31 margin-left: 3px;
32 border: 0;
33}
34.wiki-toc.trac-nav .edit a { color:blue; border-color:blue; }
35}}}
36
37The lines above define the styles for displaying the navigation
38bar. These styles build upon the styles for !TracGuideToc that come
39with your Trac distribution. If you just install the macro but miss to
40extend the style file, TracNav will work but look somewhat strange.
41
42
43== Usage ==
44
45To use TracNav, you have to create an index page for your site and
46call the TracNav macro on each page, where the navigation bar should
47be displayed. The index page is a regular wiki page. The page with the
48table of contents must include an unordered list of links that should
49be displayed in the navigation bar.
50
51To display the navigation bar on a page, you must call the TracNav
52macro on that page an pass the name of your table of contents as
53argument.
54
55
56== Author and license ==
57
58Copyright 2005 Bernhard Haumacher (haui at haumacher.de)
59
60{{{
61This program is free software; you can redistribute it and/or modify
62it under the terms of the GNU General Public License as published by
63the Free Software Foundation; either version 2 of the License, or
64(at your option) any later version.
65
66This program is distributed in the hope that it will be useful,
67but WITHOUT ANY WARRANTY; without even the implied warranty of
68MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
69GNU General Public License for more details.
70
71You should have received a copy of the GNU General Public License
72along with this program; if not, write to the Free Software
73Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
74}}}
75
76== Additional information and a life example ==
77
78Please visit: http://svn.ipd.uka.de/trac/javaparty/wiki/TracNav
79"""
80
81import re
82import sys
83
84listRule = re.compile(r"""^(?P<indent> *)\* +(?:(?P<wikilink>\[wiki:(?P<link>(&#34;([^&#34;]*)&#34;|'([^']*)')|([^ \]]+)) +(?P<label>[^\]]*)\])|(?P<text>.*))""", re.M)
85
86def getToc(hdf, env, db, curpage, name):
87    preview = hdf.getValue('args.preview', "")
88
89    tocText = "* Table of contents"
90    if preview and (name == curpage):
91        tocText = hdf.getValue('wiki.page_source', tocText);
92    else:
93        cursor = db.cursor()
94        cursor.execute('SELECT text FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1', name)
95        row = cursor.fetchone()
96        if row:
97            tocText = row[0]
98
99    # env.log.debug(tocText)
100    return tocText
101
102
103def parseToc(tocText):
104    stack = []
105    nextPos = 0
106    while 1:
107        match = listRule.search(tocText, nextPos)
108        if not match:
109            # env.log.debug("No more matches")
110            break
111
112        indent = len(match.group('indent'))
113        if match.group('wikilink'):
114            link = match.group('link')
115            label = match.group('label')
116        else:
117            link = None
118            label = match.group('text')
119
120        # if link == None:
121        #     env.log.debug(label + " ---")
122        # else:
123        #     env.log.debug(label + ": " + link)
124
125        if len(stack) == 0:
126            stack.append((indent, []))
127
128        (lastIndent, list) = stack[len(stack) - 1]
129
130        if indent > lastIndent:
131            stack.append((indent, [(link, label, None)]))
132        elif indent == lastIndent:
133            list.append((link, label, None))
134        else:
135            while indent < lastIndent:
136                (_, list) = stack.pop()
137                (lastIndent, topList) = stack[len(stack) - 1]
138                (topLink, topLabel, _) = topList[len(topList) - 1]
139                topList[len(topList) - 1] = (topLink, topLabel, list)
140
141            (lastIndent, list) = stack[len(stack) - 1]
142            list.append((link, label, None))
143               
144        nextPos = match.end()
145
146    while len(stack) > 1:
147        (_, list) = stack.pop()
148        (_, topList) = stack[len(stack) - 1]
149        (topLink, topLabel, _) = topList[len(topList) - 1]
150        topList[len(topList) - 1] = (topLink, topLabel, list)
151
152    (_, list) = stack.pop()
153    return list
154
155
156def execute(hdf, args, env):
157    preview = hdf.getValue('args.preview', "")
158    curpage = hdf.getValue('args.page', "")
159    name = args
160    if not name:
161        name = 'TOC'
162
163    db = env.get_db_cnx()
164    toc = parseToc(getToc(hdf, env, db, curpage, name))
165    if not toc:
166        msg = ''
167        msg += '<div class="system-message"><strong>Error: Table of contents does not exist.'
168        if (not preview) and (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
169            msg += ' Click here to <a href="%s?edit=yes">edit</a>.' % env.href.wiki(name)
170        msg += '</strong></div>\n'
171        return msg
172
173    (found, filtered) = filter(curpage, toc, 0)
174    if found:
175        return displayAll(hdf, env, name, curpage, filtered, 0)
176    else:
177        return displayAll(hdf, env, name, curpage, toc, 0)
178
179
180def filter(curpage, toc, level):
181    found = 0
182    result = []
183    for name, title, sub in toc:
184        if sub == None:
185            if name == curpage:
186                found = 1
187            result.append((name, title, None))
188        else:
189            (subfound, subtoc) = filter(curpage, sub, level + 1)
190            if subfound or (name == None):
191                found = 1
192                if level == 0 and name != None:
193                    prepended = [(name, title, subtoc)]
194                    prepended.extend(result)
195                    result = prepended
196                else:
197                    result.append((name, title, subtoc))
198            else:
199                result.append((name, title, []))
200    return (found, result)
201
202def indentation(col):
203    return ' ' * col
204
205def displayAll(hdf, env, name, curpage, toc, col):
206    preview = hdf.getValue('args.preview', "")
207    html = ''
208    html += '%s<div class="wiki-toc trac-nav">\n' % indentation(col)
209    col += 1
210    if (not preview) and hdf.getValue('trac.acl.WIKI_MODIFY', ''):
211        html += '%s<div class="edit"><a href="%s?edit=yes">edit</a></div>\n' % (indentation(col), env.href.wiki(name))
212    html += '%s<ul>\n' % indentation(col)
213    col += 1
214    html += display(env, curpage, toc, 0, col)
215    col -= 1
216    html += '%s</ul>\n' % indentation(col)
217    col -= 1
218    html += '%s</div>\n' % indentation(col)
219    return html
220
221def display(env, curpage, toc, depth, col):
222    html = ''
223    for name, title, sub in toc:
224        liStyle = ' style="padding-left: %dem;"' % (depth + 1)
225        if sub == None:
226            if name == curpage:
227                cls = ' class="active"'
228            else:
229                cls = ''
230            html += '%s<li%s%s>' % (indentation(col), liStyle, cls)
231            if name == None:
232                html += title
233            else:
234                html += '<a href="%s">%s</a>' % (env.href.wiki(name), title)
235            html += '</li>\n'
236        else:
237            html += '%s<li%s>\n' % (indentation(col), liStyle)
238            col += 1
239            if name == None or len(sub) > 0:
240                html += '%s<h4>%s</h4>\n' % (indentation(col), title)
241            else:
242                html += '%s<h4><a href="%s">%s...</a></h4>\n' % (indentation(col), env.href.wiki(name), title)
243            col -= 1
244            html += '%s</li>\n' % indentation(col)
245            if len(sub) > 0:
246                html += display(env, curpage, sub, depth + 1, col)
247    return html
248