# HG changeset patch
# User gerg.ward+moin@gmail.com
# Date 1225380804 14400
# Node ID 53ff55dee8f99d6d3e78e73cab1c2c3d6b3df8e4
# Parent  4eae0f3824d2084833ff9397ec48bc6b3a684d61
Factor out functions that work on including a single page.

diff -r 4eae0f3824d2 -r 53ff55dee8f9 MoinMoin/macro/Include.py
--- a/MoinMoin/macro/Include.py	Thu Oct 30 11:16:34 2008 -0400
+++ b/MoinMoin/macro/Include.py	Thu Oct 30 11:33:24 2008 -0400
@@ -80,6 +80,120 @@
     if skipitems:
         del pagelist[:int(skipitems)]
 
+class NoIncludeError(Exception):
+    def __init__(self, message=None):
+        self.message = message
+
+def check_page(macro, request, this_page, inc_name):
+    """
+    Check if it's OK to include the page named by 'inc_name' and return
+    a Page object corresponding to 'inc_page' if so.  If not, raise
+    NoIncludeError with an untranslated end-user error message.  The
+    NoIncludeError have no message, in which case no error message will
+    be shown to the end user.
+    """
+    if not request.user.may.read(inc_name):
+        raise NoIncludeError()
+    if inc_name in this_page._macroInclude_pagelist:
+        raise NoIncludeError(u'Recursive include of "%s" forbidden' % (inc_name, ))
+    fmt = macro.formatter.__class__(request, is_included=True)
+    fmt._base_depth = macro.formatter._base_depth
+    inc_page = Page(request, inc_name, formatter=fmt)
+    if not inc_page.exists():
+        raise NoIncludeError()
+
+    return inc_page
+
+def trim_page_body(request, inc_page, args, result):
+    _ = request.getText
+    body = inc_page.get_raw_body() + '\n'
+    from_pos = 0
+    to_pos = -1
+    from_re = args.group('from')
+    if from_re:
+        try:
+            from_match = re.compile(from_re, re.M).search(body)
+        except re.error:
+            ##result.append("*** fe=%s ***" % e)
+            from_match = re.compile(re.escape(from_re), re.M).search(body)
+        if from_match:
+            from_pos = from_match.end()
+        else:
+            result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
+    to_re = args.group('to')
+    if to_re:
+        try:
+            to_match = re.compile(to_re, re.M).search(body, from_pos)
+        except re.error:
+            to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
+        if to_match:
+            to_pos = to_match.start()
+        else:
+            result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
+
+    trimmed = (from_pos > 0 or to_pos > -1)
+    return (body[from_pos:to_pos], trimmed)
+
+def format_titles(macro, request, title_re, inc_page, body, result):
+    levelstack = []
+    for title, level in extract_titles(body, title_re):
+        if levelstack:
+            if level > levelstack[-1]:
+                result.append(macro.formatter.bullet_list(1))
+                levelstack.append(level)
+            else:
+                while levelstack and level < levelstack[-1]:
+                    result.append(macro.formatter.bullet_list(0))
+                    levelstack.pop()
+                if not levelstack or level != levelstack[-1]:
+                    result.append(macro.formatter.bullet_list(1))
+                    levelstack.append(level)
+        else:
+            result.append(macro.formatter.bullet_list(1))
+            levelstack.append(level)
+        result.append(macro.formatter.listitem(1))
+        result.append(inc_page.link_to(request, title))
+        result.append(macro.formatter.listitem(0))
+    while levelstack:
+        result.append(macro.formatter.bullet_list(0))
+        levelstack.pop()
+
+def add_heading(macro, request, print_mode, inc_page, args, result):
+    level = None
+    if args.group('heading') and args.group('hquote'):
+        # XXX that "or ..." clause is dead code: the arg_heading regex
+        # guarantees that args.group('htext') will return a non-empty
+        # string as long as args.group('hquote') does too.
+        heading = args.group('htext') or inc_page.split_title()
+        level = 1
+        if args.group('level'):
+            level = int(args.group('level'))
+        if print_mode:
+            result.append(macro.formatter.heading(1, level) +
+                          macro.formatter.text(heading) +
+                          macro.formatter.heading(0, level))
+        else:
+            url = inc_page.url(request)
+            result.extend([
+                macro.formatter.heading(1, level, id=heading),
+                macro.formatter.url(1, url, css="include-heading-link"),
+                macro.formatter.text(heading),
+                macro.formatter.url(0),
+                macro.formatter.heading(0, level),
+            ])
+        return True
+    else:
+        return False
+
+def add_editlink(macro, request, inc_name, inc_page, result):
+    _ = request.getText
+    result.extend([
+        macro.formatter.div(1, css_class="include-link"),
+        inc_page.link_to(request, '[%s]' % (inc_name, ), css_class="include-page-link"),
+        inc_page.link_to(request, '[%s]' % (_('edit'), ), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
+        macro.formatter.div(0),
+    ])
+
 def execute(macro, text, args_re=re.compile(_args_re_pattern), title_re=re.compile(_title_re, re.M)):
     request = macro.request
     _ = request.getText
@@ -112,97 +226,31 @@
 
     # iterate over pages
     for inc_name in pagelist:
-        if not request.user.may.read(inc_name):
+        try:
+            inc_page = check_page(macro, request, this_page, inc_name)
+        except NoIncludeError, err:
+            if err.message:
+                result.append(u'<p><strong class="error">%s</strong></p>' % (err.message, ))
             continue
-        if inc_name in this_page._macroInclude_pagelist:
-            result.append(u'<p><strong class="error">Recursive include of "%s" forbidden</strong></p>' % (inc_name, ))
-            continue
-        fmt = macro.formatter.__class__(request, is_included=True)
-        fmt._base_depth = macro.formatter._base_depth
-        inc_page = Page(request, inc_name, formatter=fmt)
-        if not inc_page.exists():
-            continue
+
         inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
 
-        # check for "from" and "to" arguments (allowing partial includes)
-        body = inc_page.get_raw_body() + '\n'
-        from_pos = 0
-        to_pos = -1
-        from_re = args.group('from')
-        if from_re:
-            try:
-                from_match = re.compile(from_re, re.M).search(body)
-            except re.error:
-                ##result.append("*** fe=%s ***" % e)
-                from_match = re.compile(re.escape(from_re), re.M).search(body)
-            if from_match:
-                from_pos = from_match.end()
-            else:
-                result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
-        to_re = args.group('to')
-        if to_re:
-            try:
-                to_match = re.compile(to_re, re.M).search(body, from_pos)
-            except re.error:
-                to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
-            if to_match:
-                to_pos = to_match.start()
-            else:
-                result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
+        # get the body of the included page, applying 'from' and 'to' to trim it
+        # (partial includes)
+        (body, trimmed) = trim_page_body(request, inc_page, args, result)
 
         if titlesonly:
-            levelstack = []
-            for title, level in extract_titles(body[from_pos:to_pos], title_re):
-                if levelstack:
-                    if level > levelstack[-1]:
-                        result.append(macro.formatter.bullet_list(1))
-                        levelstack.append(level)
-                    else:
-                        while levelstack and level < levelstack[-1]:
-                            result.append(macro.formatter.bullet_list(0))
-                            levelstack.pop()
-                        if not levelstack or level != levelstack[-1]:
-                            result.append(macro.formatter.bullet_list(1))
-                            levelstack.append(level)
-                else:
-                    result.append(macro.formatter.bullet_list(1))
-                    levelstack.append(level)
-                result.append(macro.formatter.listitem(1))
-                result.append(inc_page.link_to(request, title))
-                result.append(macro.formatter.listitem(0))
-            while levelstack:
-                result.append(macro.formatter.bullet_list(0))
-                levelstack.pop()
+            format_titles(macro, request, title_re, inc_page, body, result)
             continue
 
-        if from_pos or to_pos != -1:
-            inc_page.set_raw_body(body[from_pos:to_pos], modified=True)
-        ##result.append("*** f=%s t=%s ***" % (from_re, to_re))
-        ##result.append("*** f=%d t=%d ***" % (from_pos, to_pos))
+        if trimmed:
+            inc_page.set_raw_body(body, modified=True)
 
         if not hasattr(request, "_Include_backto"):
             request._Include_backto = this_page.page_name
 
         # do headings
-        level = None
-        if args.group('heading') and args.group('hquote'):
-            heading = args.group('htext') or inc_page.split_title()
-            level = 1
-            if args.group('level'):
-                level = int(args.group('level'))
-            if print_mode:
-                result.append(macro.formatter.heading(1, level) +
-                              macro.formatter.text(heading) +
-                              macro.formatter.heading(0, level))
-            else:
-                url = inc_page.url(request)
-                result.extend([
-                    macro.formatter.heading(1, level, id=heading),
-                    macro.formatter.url(1, url, css="include-heading-link"),
-                    macro.formatter.text(heading),
-                    macro.formatter.url(0),
-                    macro.formatter.heading(0, level),
-                ])
+        added_heading = add_heading(macro, request, print_mode, inc_page, args, result)
 
         # set or increment include marker
         this_page._macroInclude_pagelist[inc_name] = \
@@ -227,13 +275,9 @@
             del this_page._macroInclude_pagelist[inc_name]
 
         # if no heading and not in print mode, then output a helper link
-        if editlink and not (level or print_mode):
-            result.extend([
-                macro.formatter.div(1, css_class="include-link"),
-                inc_page.link_to(request, '[%s]' % (inc_name, ), css_class="include-page-link"),
-                inc_page.link_to(request, '[%s]' % (_('edit'), ), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
-                macro.formatter.div(0),
-            ])
+        if editlink and not (added_heading or print_mode):
+            add_editlink(macro, request, inc_name, inc_page, result)
+
         # XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
 
     # return include text
