| 1 | # vim: expandtab tabstop=4 |
|---|
| 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 | |
|---|
| 16 | for line in body.splitlines(): |
|---|
| 17 | line = escape(line) |
|---|
| 18 | |
|---|
| 19 | # Skip over wiki-escaped code, e.g. code examples (Steven N. |
|---|
| 20 | # Severinghaus <sns@severinghaus.org>) |
|---|
| 21 | if in_pre: |
|---|
| 22 | if line == '}}}': |
|---|
| 23 | in_pre = False |
|---|
| 24 | else: |
|---|
| 25 | continue |
|---|
| 26 | if line == '{{{': |
|---|
| 27 | in_pre = True |
|---|
| 28 | continue |
|---|
| 29 | |
|---|
| 30 | match = rules_re.match(line) |
|---|
| 31 | if match: |
|---|
| 32 | header = match.group('header') |
|---|
| 33 | new_depth = len(match.group('hdepth')) |
|---|
| 34 | if new_depth < min_depth: |
|---|
| 35 | continue |
|---|
| 36 | elif new_depth < current_depth: |
|---|
| 37 | while new_depth < current_depth: |
|---|
| 38 | current_depth -= 1 |
|---|
| 39 | if current_depth < max_depth: |
|---|
| 40 | out.write("</ol>") |
|---|
| 41 | out.write("<li>") |
|---|
| 42 | elif new_depth >= current_depth: |
|---|
| 43 | i = current_depth |
|---|
| 44 | while new_depth > i or first_li : |
|---|
| 45 | i += 1 |
|---|
| 46 | first_li = False |
|---|
| 47 | if current_depth <= max_depth: |
|---|
| 48 | out.write("<ol>") |
|---|
| 49 | current_depth = new_depth |
|---|
| 50 | out.write("<li>") |
|---|
| 51 | default_anchor = anchor = anchor_re.sub("", header) |
|---|
| 52 | anchor_n = 1 |
|---|
| 53 | while anchor in seen_anchors: |
|---|
| 54 | anchor = default_anchor + str(anchor_n) |
|---|
| 55 | anchor_n += 1 |
|---|
| 56 | seen_anchors.append(anchor) |
|---|
| 57 | link = page + "#" + anchor |
|---|
| 58 | if current_depth <= max_depth: |
|---|
| 59 | out.write('<a href="%s">%s</a></li>\n' % (env.href.wiki(link), header)) |
|---|
| 60 | while current_depth >= min_depth and not first_li: |
|---|
| 61 | if current_depth <= max_depth: |
|---|
| 62 | out.write("</ol>\n") |
|---|
| 63 | current_depth -= 1 |
|---|
| 64 | |
|---|
| 65 | def execute(hdf, args, env): |
|---|
| 66 | db = env.get_db_cnx() |
|---|
| 67 | if not args: |
|---|
| 68 | args = '' |
|---|
| 69 | pre_pages = re.split('\s*,\s*', args) |
|---|
| 70 | # Options |
|---|
| 71 | inline = False |
|---|
| 72 | heading = 'Table of Contents' |
|---|
| 73 | pages = [] |
|---|
| 74 | root = '' |
|---|
| 75 | params = { } |
|---|
| 76 | # Global options |
|---|
| 77 | for page in pre_pages: |
|---|
| 78 | if page == 'inline': |
|---|
| 79 | inline = True |
|---|
| 80 | elif page == 'noheading': |
|---|
| 81 | heading = None |
|---|
| 82 | elif page[:8] == 'heading=': |
|---|
| 83 | heading = page[8:] |
|---|
| 84 | elif page == '': |
|---|
| 85 | continue |
|---|
| 86 | elif page[:6] == 'depth=': |
|---|
| 87 | params['max_depth'] = int(page[6:]) |
|---|
| 88 | elif page[:5] == 'root=': |
|---|
| 89 | root = page[5:] |
|---|
| 90 | else: |
|---|
| 91 | pages.append(page) |
|---|
| 92 | # Has the user supplied a list of pages? |
|---|
| 93 | if not pages: |
|---|
| 94 | pages.append(hdf.getValue("args.page", "WikiStart")) |
|---|
| 95 | root = '' |
|---|
| 96 | params['min_depth'] = 2 # Skip page title |
|---|
| 97 | out = StringIO() |
|---|
| 98 | if not inline: |
|---|
| 99 | out.write("<div class='wiki-toc'>\n") |
|---|
| 100 | if heading: |
|---|
| 101 | out.write("<h4>%s</h4>\n" % heading) |
|---|
| 102 | for page in pages: |
|---|
| 103 | # Override arguments |
|---|
| 104 | if page[:6] == 'depth=': |
|---|
| 105 | params['max_depth'] = int(page[6:]) |
|---|
| 106 | elif page[:5] == 'root=': |
|---|
| 107 | root = page[5:] |
|---|
| 108 | else: |
|---|
| 109 | page = root + page |
|---|
| 110 | cursor = db.cursor() |
|---|
| 111 | cursor.execute("SELECT text FROM wiki WHERE name='%s' ORDER BY version desc LIMIT 1" % page) |
|---|
| 112 | row = cursor.fetchone() |
|---|
| 113 | if row: |
|---|
| 114 | parse_toc(env, out, page, row[0], **params) |
|---|
| 115 | else: |
|---|
| 116 | out.write('<div class="system-message"><strong>Error: Page %s does not exist</strong></div>' % page) |
|---|
| 117 | if not inline: |
|---|
| 118 | out.write("</div>\n") |
|---|
| 119 | return out.getvalue() |
|---|