| 1 |
|
|---|
| 2 |
from StringIO import StringIO |
|---|
| 3 |
import re |
|---|
| 4 |
import string |
|---|
| 5 |
from trac.util import escape |
|---|
| 6 |
|
|---|
| 7 |
rules_re = re.compile(r"""(?P<heading>^\s*(?P<hdepth>=+)\s(?P<header>.*)\s(?P=hdepth)\s*$)""") |
|---|
| 8 |
anchor_re = re.compile('[^\w\d]+') |
|---|
| 9 |
|
|---|
| 10 |
def parse_toc(env, out, page, body, max_depth = 999, min_depth = 1): |
|---|
| 11 |
current_depth = min_depth |
|---|
| 12 |
in_pre = False |
|---|
| 13 |
first_li = True |
|---|
| 14 |
seen_anchors = [] |
|---|
| 15 |
has_hits = False |
|---|
| 16 |
|
|---|
| 17 |
for line in body.splitlines(): |
|---|
| 18 |
line = escape(line) |
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
if in_pre: |
|---|
| 23 |
if line == '}}}': |
|---|
| 24 |
in_pre = False |
|---|
| 25 |
else: |
|---|
| 26 |
continue |
|---|
| 27 |
if line == '{{{': |
|---|
| 28 |
in_pre = True |
|---|
| 29 |
continue |
|---|
| 30 |
|
|---|
| 31 |
match = rules_re.match(line) |
|---|
| 32 |
if match: |
|---|
| 33 |
header = match.group('header') |
|---|
| 34 |
new_depth = len(match.group('hdepth')) |
|---|
| 35 |
if new_depth < min_depth: |
|---|
| 36 |
continue |
|---|
| 37 |
elif new_depth < current_depth: |
|---|
| 38 |
while new_depth < current_depth: |
|---|
| 39 |
current_depth -= 1 |
|---|
| 40 |
if current_depth < max_depth: |
|---|
| 41 |
out.write("</ul>") |
|---|
| 42 |
indent = current_depth - 1 |
|---|
| 43 |
out.write("<li style='padding-left: %sem;'>" % indent) |
|---|
| 44 |
has_hits = True |
|---|
| 45 |
elif new_depth >= current_depth: |
|---|
| 46 |
i = current_depth |
|---|
| 47 |
while new_depth > i or first_li : |
|---|
| 48 |
i += 1 |
|---|
| 49 |
first_li = False |
|---|
| 50 |
if current_depth <= max_depth: |
|---|
| 51 |
out.write("<ul>") |
|---|
| 52 |
current_depth = new_depth |
|---|
| 53 |
indent = current_depth - 1 |
|---|
| 54 |
out.write("<li style='padding-left: %sem;'>" % indent) |
|---|
| 55 |
has_hits = True |
|---|
| 56 |
default_anchor = anchor = anchor_re.sub("", header) |
|---|
| 57 |
anchor_n = 1 |
|---|
| 58 |
while anchor in seen_anchors: |
|---|
| 59 |
anchor = default_anchor + str(anchor_n) |
|---|
| 60 |
anchor_n += 1 |
|---|
| 61 |
seen_anchors.append(anchor) |
|---|
| 62 |
link = page + "#" + anchor |
|---|
| 63 |
if current_depth <= max_depth: |
|---|
| 64 |
out.write('<a href="%s">%s</a></li>\n' % (env.href.wiki(link), header)) |
|---|
| 65 |
while current_depth >= min_depth and not first_li: |
|---|
| 66 |
if current_depth <= max_depth: |
|---|
| 67 |
out.write("</ul>\n") |
|---|
| 68 |
current_depth -= 1 |
|---|
| 69 |
return has_hits |
|---|
| 70 |
|
|---|
| 71 |
def execute(hdf, args, env): |
|---|
| 72 |
db = env.get_db_cnx() |
|---|
| 73 |
if not args: |
|---|
| 74 |
args = '' |
|---|
| 75 |
pre_pages = re.split('\s*,\s*', args) |
|---|
| 76 |
|
|---|
| 77 |
inline = False |
|---|
| 78 |
heading = 'TOC for this Page' |
|---|
| 79 |
pages = [] |
|---|
| 80 |
root = '' |
|---|
| 81 |
params = { } |
|---|
| 82 |
|
|---|
| 83 |
for page in pre_pages: |
|---|
| 84 |
if page == 'inline': |
|---|
| 85 |
inline = True |
|---|
| 86 |
elif page == 'noheading': |
|---|
| 87 |
heading = None |
|---|
| 88 |
elif page[:8] == 'heading=': |
|---|
| 89 |
heading = page[8:] |
|---|
| 90 |
elif page == '': |
|---|
| 91 |
continue |
|---|
| 92 |
elif page[:6] == 'depth=': |
|---|
| 93 |
params['max_depth'] = int(page[6:]) |
|---|
| 94 |
elif page[:5] == 'root=': |
|---|
| 95 |
root = page[5:] |
|---|
| 96 |
else: |
|---|
| 97 |
pages.append(page) |
|---|
| 98 |
|
|---|
| 99 |
if not pages: |
|---|
| 100 |
pages.append(hdf.getValue("args.page", "WikiStart")) |
|---|
| 101 |
root = '' |
|---|
| 102 |
params['min_depth'] = 2 |
|---|
| 103 |
out = StringIO() |
|---|
| 104 |
if not inline: |
|---|
| 105 |
out.write("<div class='wiki-toc trac-nav'>\n") |
|---|
| 106 |
if heading: |
|---|
| 107 |
out.write("<h3>%s</h3>\n" % heading) |
|---|
| 108 |
has_hits = False |
|---|
| 109 |
for page in pages: |
|---|
| 110 |
|
|---|
| 111 |
if page[:6] == 'depth=': |
|---|
| 112 |
params['max_depth'] = int(page[6:]) |
|---|
| 113 |
elif page[:5] == 'root=': |
|---|
| 114 |
root = page[5:] |
|---|
| 115 |
else: |
|---|
| 116 |
page = root + page |
|---|
| 117 |
cursor = db.cursor() |
|---|
| 118 |
cursor.execute("SELECT text FROM wiki WHERE name='%s' ORDER BY version desc LIMIT 1" % page) |
|---|
| 119 |
row = cursor.fetchone() |
|---|
| 120 |
if row: |
|---|
| 121 |
has_hits = parse_toc(env, out, page, row[0], **params) |
|---|
| 122 |
else: |
|---|
| 123 |
out.write('<div class="system-message"><strong>Error: Page %s does not exist</strong></div>' % page) |
|---|
| 124 |
if not inline: |
|---|
| 125 |
out.write("</div>\n") |
|---|
| 126 |
if has_hits: |
|---|
| 127 |
return out.getvalue() |
|---|
| 128 |
else: |
|---|
| 129 |
return '' |
|---|