changeset 106:212dfbb75031

Page formatter/resolver changeS: * Better handle edge cases in inclusions, do more stuff at the end of the resolving step.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 12 Nov 2013 13:50:26 -0800
parents 986f399a1471
children 2654e243de8c
files wikked/formatter.py wikked/resolver.py wikked/utils.py
diffstat 3 files changed, 62 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/wikked/formatter.py	Tue Nov 12 13:48:06 2013 -0800
+++ b/wikked/formatter.py	Tue Nov 12 13:50:26 2013 -0800
@@ -1,7 +1,7 @@
 import os
 import os.path
 import re
-from utils import get_absolute_url, get_meta_name_and_modifiers
+from utils import get_meta_name_and_modifiers
 
 
 class BaseContext(object):
@@ -13,9 +13,6 @@
     def urldir(self):
         return os.path.dirname(self.url)
 
-    def getAbsoluteUrl(self, url, do_slugify=True):
-        return get_absolute_url(self.url, url, do_slugify)
-
 
 class FormattingContext(BaseContext):
     """ Context for formatting pages. """
@@ -122,9 +119,9 @@
     def _coerceInclude(self, ctx, value):
         pipe_idx = value.find('|')
         if pipe_idx < 0:
-            return ctx.getAbsoluteUrl(value.strip())
+            return value.strip()
         else:
-            url = ctx.getAbsoluteUrl(value[:pipe_idx].strip())
+            url = value[:pipe_idx].strip()
             parameters = value[pipe_idx + 1:].replace('\n', '')
             return url + '|' + parameters
 
@@ -154,12 +151,6 @@
         for m in re.finditer(arg_pattern, query):
             name = str(m.group('name')).strip()
             value = str(m.group('value')).strip()
-            if re.match(r'^\[\[.*\]\]$', value):
-                url = value[2:-2]
-                abs_url = ctx.getAbsoluteUrl(url)
-                value = '[[%s]]' % abs_url
-            if len(processed_args) > 0:
-                processed_args += '|'
             processed_args += '%s=%s' % (name, value)
 
         mod_attr = ''
@@ -168,9 +159,8 @@
         return '<div class="wiki-query"%s>%s</div>\n' % (mod_attr, processed_args)
 
     def _formatWikiLink(self, ctx, display, url):
-        abs_url = ctx.getAbsoluteUrl(url)
-        ctx.out_links.append(abs_url)
-        return '<a class="wiki-link" data-wiki-url="%s">%s</a>' % (abs_url, display)
+        ctx.out_links.append(url)
+        return '<a class="wiki-link" data-wiki-url="%s">%s</a>' % (url, display)
 
     @staticmethod
     def parseWikiLinks(text):
--- a/wikked/resolver.py	Tue Nov 12 13:48:06 2013 -0800
+++ b/wikked/resolver.py	Tue Nov 12 13:50:26 2013 -0800
@@ -1,6 +1,8 @@
 import re
+import os.path
 import jinja2
-from utils import get_meta_name_and_modifiers
+from utils import (get_meta_name_and_modifiers, namespace_title_to_url,
+        get_absolute_url)
 
 
 class FormatterNotFound(Exception):
@@ -36,6 +38,11 @@
             return len(self.url_trail) > 1
         raise ValueError("Unknown modifier: " + modifier)
 
+    def getAbsoluteUrl(self, url, base_url=None):
+        if base_url is None:
+            base_url = self.root_page.url
+        return get_absolute_url(base_url, url)
+
 
 class ResolveOutput(object):
     """ The results of a resolve operation. """
@@ -81,6 +88,11 @@
         self.output = None
         self.env = None
 
+        self.resolvers = {
+                'query': self._runQuery,
+                'include': self._runInclude
+            }
+
     @property
     def wiki(self):
         return self.page.wiki
@@ -93,6 +105,8 @@
         try:
             return self._unsafeRun()
         except Exception as e:
+            self.wiki.logger.error("Error resolving page '%s':" % self.page.url)
+            self.wiki.logger.exception(e)
             self.output = ResolveOutput(self.page)
             self.output.text = u'<div class="error">%s</div>' % e
             return self.output
@@ -100,10 +114,13 @@
     def _unsafeRun(self):
         # Create default parameters.
         if not self.parameters:
+            urldir = os.path.dirname(self.page.url)
+            full_title = os.path.join(urldir, self.page.title).replace('\\', '/')
             self.parameters = {
                 '__page': {
                     'url': self.page.url,
-                    'title': self.page.title
+                    'title': self.page.title,
+                    'full_title': full_title
                     },
                 '__args': []
                 }
@@ -112,17 +129,8 @@
         # with child outputs (from included pages).
         self.output = ResolveOutput(self.page)
 
-        # Resolve link states.
-        def repl1(m):
-            url = str(m.group('url'))
-            if self.wiki.pageExists(url):
-                return str(m.group())
-            return '<a class="wiki-link missing" data-wiki-url="%s">' % url
-
-        final_text = re.sub(
-                r'<a class="wiki-link" data-wiki-url="(?P<url>[^"]+)">',
-                repl1,
-                self.page.getFormattedText())
+        # Start with the page's text.
+        final_text = self.page.getFormattedText()
 
         # Resolve queries, includes, etc.
         def repl2(m):
@@ -137,10 +145,9 @@
                     opt_value = str(c.group('value'))
                     meta_opts[opt_name] = opt_value
 
-            if meta_name == 'query':
-                return self._runQuery(meta_opts, meta_value)
-            elif meta_name == 'include':
-                return self._runInclude(meta_opts, meta_value)
+            resolver = self.resolvers.get(meta_name)
+            if resolver:
+                return resolver(meta_opts, meta_value)
             return ''
 
         final_text = re.sub(
@@ -151,11 +158,29 @@
                 final_text,
                 flags=re.MULTILINE)
 
-        # Run text through templating and formatting if this
-        # is the root page.
+        # If this is the root page, with all the includes resolved and
+        # collapsed into one text, we need to run the final steps.
         if self.is_root:
+            # Resolve any `{{foo}}` variable references.
             parameters = dict(self.parameters)
             final_text = self._renderTemplate(final_text, parameters, error_url=self.page.url)
+
+            # Resolve link states.
+            def repl1(m):
+                raw_url = str(m.group('url'))
+                raw_url = self.ctx.getAbsoluteUrl(raw_url)
+                url = namespace_title_to_url(raw_url)
+                self.wiki.logger.debug("Converting '%s' to '%s'" % (raw_url, url))
+                if self.wiki.pageExists(url):
+                    return '<a class="wiki-link" data-wiki-url="%s">' % url
+                return '<a class="wiki-link missing" data-wiki-url="%s">' % url
+
+            final_text = re.sub(
+                    r'<a class="wiki-link" data-wiki-url="(?P<url>[^"]+)">',
+                    repl1,
+                    final_text)
+
+            # Format the text.
             formatter = self._getFormatter(self.page.extension)
             final_text = formatter(final_text)
 
@@ -170,7 +195,7 @@
                 return ''
 
         # Check for circular includes.
-        include_url = opts['url']
+        include_url = self.ctx.getAbsoluteUrl(opts['url'], self.page.url)
         if include_url in self.ctx.url_trail:
             raise CircularIncludeError("Circular include detected at: %s" % include_url, self.ctx.url_trail)
 
--- a/wikked/utils.py	Tue Nov 12 13:48:06 2013 -0800
+++ b/wikked/utils.py	Tue Nov 12 13:50:26 2013 -0800
@@ -15,15 +15,20 @@
         raw_abs_url = os.path.join(urldir, url)
         abs_url = os.path.normpath(raw_abs_url).replace('\\', '/')
     if do_slugify:
-        abs_url_parts = abs_url.split('/')
-        abs_url = ''
-        for i, part in enumerate(abs_url_parts):
-            if i > 0:
-                abs_url += '/'
-            abs_url += title_to_url(part)
+        abs_url = namespace_title_to_url(abs_url)
     return abs_url
 
 
+def namespace_title_to_url(url):
+    url_parts = url.split('/')
+    result = ''
+    for i, part in enumerate(url_parts):
+        if i > 0:
+            result += '/'
+        result += title_to_url(part)
+    return result
+
+
 def title_to_url(title):
     # Remove diacritics (accents, etc.) and replace them with ASCII
     # equivelent.
@@ -31,7 +36,7 @@
         unicodedata.normalize('NFD', unicode(title))
         if unicodedata.category(c) != 'Mn'))
     # Now replace spaces and punctuation with a hyphen.
-    return re.sub(r'[^A-Za-z0-9_\.\-\(\)]+', '-', ansi_title.lower())
+    return re.sub(r'[^A-Za-z0-9_\.\-\(\)\{\}]+', '-', ansi_title.lower())
 
 
 def url_to_title(url):