Back to index

moin  1.9.0~rc2
Include.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - Include macro
00004 
00005     This macro includes the formatted content of the given page(s). See
00006 
00007         http://purl.net/wiki/moinmaster/HelpOnMacros/Include
00008 
00009     for detailed docs.
00010 
00011     @copyright: 2000-2004 Juergen Hermann <jh@web.de>,
00012                 2000-2001 Richard Jones <richard@bizarsoftware.com.au>
00013     @license: GNU GPL, see COPYING for details.
00014 """
00015 
00016 #Dependencies = ["pages"] # included page
00017 Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
00018 
00019 generates_headings = True
00020 
00021 import re, StringIO
00022 from MoinMoin import wikiutil
00023 from MoinMoin.Page import Page
00024 
00025 
00026 _sysmsg = '<p><strong class="%s">%s</strong></p>'
00027 
00028 ## keep in sync with TableOfContents macro!
00029 _arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
00030 _arg_level = r',\s*(?P<level>\d*)'
00031 _arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
00032 _arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
00033 _arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
00034 _arg_items = r'(,\s*items=(?P<items>\d+))?'
00035 _arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
00036 _arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
00037 _arg_editlink = r'(,\s*(?P<editlink>editlink))?'
00038 _args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s%s)?$' % (
00039     _arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
00040     _arg_skipitems, _arg_titlesonly, _arg_editlink)
00041 
00042 _title_re = r"^(?P<heading>\s*(?P<hmarker>=+)\s.*\s(?P=hmarker))$"
00043 
00044 def extract_titles(body, title_re):
00045     titles = []
00046     for title, _ in title_re.findall(body):
00047         h = title.strip()
00048         level = 1
00049         while h[level:level+1] == '=':
00050             level += 1
00051         title_text = h[level:-level].strip()
00052         titles.append((title_text, level))
00053     return titles
00054 
00055 def execute(macro, text, args_re=re.compile(_args_re_pattern), title_re=re.compile(_title_re, re.M)):
00056     request = macro.request
00057     _ = request.getText
00058 
00059     # return immediately if getting links for the current page
00060     if request.mode_getpagelinks:
00061         return ''
00062 
00063     # parse and check arguments
00064     args = text and args_re.match(text)
00065     if not args:
00066         return (_sysmsg % ('error', _('Invalid include arguments "%s"!')) % (text, ))
00067 
00068     # prepare including page
00069     result = []
00070     print_mode = request.action in ("print", "format")
00071     this_page = macro.formatter.page
00072     if not hasattr(this_page, '_macroInclude_pagelist'):
00073         this_page._macroInclude_pagelist = {}
00074 
00075     # get list of pages to include
00076     inc_name = wikiutil.AbsPageName(this_page.page_name, args.group('name'))
00077     pagelist = [inc_name]
00078     if inc_name.startswith("^"):
00079         try:
00080             inc_match = re.compile(inc_name)
00081         except re.error:
00082             pass # treat as plain page name
00083         else:
00084             # Get user filtered readable page list
00085             pagelist = request.rootpage.getPageList(filter=inc_match.match)
00086 
00087     # sort and limit page list
00088     pagelist.sort()
00089     sort_dir = args.group('sort')
00090     if sort_dir == 'descending':
00091         pagelist.reverse()
00092     max_items = args.group('items')
00093     if max_items:
00094         pagelist = pagelist[:int(max_items)]
00095 
00096     skipitems = 0
00097     if args.group("skipitems"):
00098         skipitems = int(args.group("skipitems"))
00099     titlesonly = args.group('titlesonly')
00100     editlink = args.group('editlink')
00101 
00102     # iterate over pages
00103     for inc_name in pagelist:
00104         if not request.user.may.read(inc_name):
00105             continue
00106         if inc_name in this_page._macroInclude_pagelist:
00107             result.append(u'<p><strong class="error">Recursive include of "%s" forbidden</strong></p>' % (inc_name, ))
00108             continue
00109         if skipitems:
00110             skipitems -= 1
00111             continue
00112         fmt = macro.formatter.__class__(request, is_included=True)
00113         fmt._base_depth = macro.formatter._base_depth
00114         inc_page = Page(request, inc_name, formatter=fmt)
00115         if not inc_page.exists():
00116             continue
00117         inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
00118 
00119         # check for "from" and "to" arguments (allowing partial includes)
00120         body = inc_page.get_raw_body() + '\n'
00121         from_pos = 0
00122         to_pos = -1
00123         from_re = args.group('from')
00124         if from_re:
00125             try:
00126                 from_match = re.compile(from_re, re.M).search(body)
00127             except re.error:
00128                 ##result.append("*** fe=%s ***" % e)
00129                 from_match = re.compile(re.escape(from_re), re.M).search(body)
00130             if from_match:
00131                 from_pos = from_match.end()
00132             else:
00133                 result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
00134         to_re = args.group('to')
00135         if to_re:
00136             try:
00137                 to_match = re.compile(to_re, re.M).search(body, from_pos)
00138             except re.error:
00139                 to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
00140             if to_match:
00141                 to_pos = to_match.start()
00142             else:
00143                 result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
00144 
00145         if titlesonly:
00146             levelstack = []
00147             for title, level in extract_titles(body[from_pos:to_pos], title_re):
00148                 if levelstack:
00149                     if level > levelstack[-1]:
00150                         result.append(macro.formatter.bullet_list(1))
00151                         levelstack.append(level)
00152                     else:
00153                         while levelstack and level < levelstack[-1]:
00154                             result.append(macro.formatter.bullet_list(0))
00155                             levelstack.pop()
00156                         if not levelstack or level != levelstack[-1]:
00157                             result.append(macro.formatter.bullet_list(1))
00158                             levelstack.append(level)
00159                 else:
00160                     result.append(macro.formatter.bullet_list(1))
00161                     levelstack.append(level)
00162                 result.append(macro.formatter.listitem(1))
00163                 result.append(inc_page.link_to(request, title))
00164                 result.append(macro.formatter.listitem(0))
00165             while levelstack:
00166                 result.append(macro.formatter.bullet_list(0))
00167                 levelstack.pop()
00168             continue
00169 
00170         if from_pos or to_pos != -1:
00171             inc_page.set_raw_body(body[from_pos:to_pos], modified=True)
00172         ##result.append("*** f=%s t=%s ***" % (from_re, to_re))
00173         ##result.append("*** f=%d t=%d ***" % (from_pos, to_pos))
00174 
00175         if not hasattr(request, "_Include_backto"):
00176             request._Include_backto = this_page.page_name
00177 
00178         # do headings
00179         level = None
00180         if args.group('heading') and args.group('hquote'):
00181             heading = args.group('htext') or inc_page.split_title()
00182             level = 1
00183             if args.group('level'):
00184                 level = int(args.group('level'))
00185             if print_mode:
00186                 result.append(macro.formatter.heading(1, level) +
00187                               macro.formatter.text(heading) +
00188                               macro.formatter.heading(0, level))
00189             else:
00190                 url = inc_page.url(request)
00191                 result.extend([
00192                     macro.formatter.heading(1, level, id=heading),
00193                     macro.formatter.url(1, url, css="include-heading-link"),
00194                     macro.formatter.text(heading),
00195                     macro.formatter.url(0),
00196                     macro.formatter.heading(0, level),
00197                 ])
00198 
00199         # set or increment include marker
00200         this_page._macroInclude_pagelist[inc_name] = \
00201             this_page._macroInclude_pagelist.get(inc_name, 0) + 1
00202 
00203         # output the included page
00204         strfile = StringIO.StringIO()
00205         request.redirect(strfile)
00206         try:
00207             inc_page.send_page(content_only=True,
00208                                omit_footnotes=True,
00209                                count_hit=False)
00210             result.append(strfile.getvalue())
00211         finally:
00212             request.redirect()
00213 
00214         # decrement or remove include marker
00215         if this_page._macroInclude_pagelist[inc_name] > 1:
00216             this_page._macroInclude_pagelist[inc_name] = \
00217                 this_page._macroInclude_pagelist[inc_name] - 1
00218         else:
00219             del this_page._macroInclude_pagelist[inc_name]
00220 
00221         # if no heading and not in print mode, then output a helper link
00222         if editlink and not (level or print_mode):
00223             result.extend([
00224                 macro.formatter.div(1, css_class="include-link"),
00225                 inc_page.link_to(request, '[%s]' % (inc_name, ), css_class="include-page-link"),
00226                 inc_page.link_to(request, '[%s]' % (_('edit'), ), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
00227                 macro.formatter.div(0),
00228             ])
00229         # XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
00230 
00231     # return include text
00232     return ''.join(result)
00233 
00234 # vim:ts=4:sw=4:et