2.1 --- a/MoinSupport.py Tue Sep 11 21:38:01 2012 +0200
2.2 +++ b/MoinSupport.py Sat Sep 29 16:58:57 2012 +0200
2.3 @@ -10,7 +10,7 @@
2.4
2.5 from DateSupport import *
2.6 from MoinMoin.Page import Page
2.7 -from MoinMoin import config, wikiutil
2.8 +from MoinMoin import config, search, wikiutil
2.9 from StringIO import StringIO
2.10 from shlex import shlex
2.11 import re
2.12 @@ -28,26 +28,272 @@
2.13 accept_regexp_str = ur';\s*q='
2.14 accept_regexp = re.compile(accept_regexp_str)
2.15
2.16 -# Utility functions.
2.17 +# Extraction of shared fragments.
2.18 +
2.19 +marker_regexp_str = r"([{]{3,}|[}]{3,})"
2.20 +marker_regexp = re.compile(marker_regexp_str, re.MULTILINE | re.DOTALL) # {{{... or }}}...
2.21 +
2.22 +# Category extraction from pages.
2.23 +
2.24 +category_regexp = None
2.25 +
2.26 +# Simple content parsing.
2.27 +
2.28 +verbatim_regexp = re.compile(ur'(?:'
2.29 + ur'<<Verbatim\((?P<verbatim>.*?)\)>>'
2.30 + ur'|'
2.31 + ur'\[\[Verbatim\((?P<verbatim2>.*?)\)\]\]'
2.32 + ur'|'
2.33 + ur'!(?P<verbatim3>.*?)(\s|$)?'
2.34 + ur'|'
2.35 + ur'`(?P<monospace>.*?)`'
2.36 + ur'|'
2.37 + ur'{{{(?P<preformatted>.*?)}}}'
2.38 + ur')', re.UNICODE)
2.39 +
2.40 +# Category discovery.
2.41
2.42 -def getContentTypeAndEncoding(content_type):
2.43 +def getCategoryPattern(request):
2.44 + global category_regexp
2.45 +
2.46 + try:
2.47 + return request.cfg.cache.page_category_regexact
2.48 + except AttributeError:
2.49 +
2.50 + # Use regular expression from MoinMoin 1.7.1 otherwise.
2.51 +
2.52 + if category_regexp is None:
2.53 + category_regexp = re.compile(u'^%s$' % ur'(?P<all>Category(?P<key>(?!Template)\S+))', re.UNICODE)
2.54 + return category_regexp
2.55 +
2.56 +def getCategories(request):
2.57 +
2.58 + """
2.59 + From the AdvancedSearch macro, return a list of category page names using
2.60 + the given 'request'.
2.61 + """
2.62 +
2.63 + # This will return all pages with "Category" in the title.
2.64 +
2.65 + cat_filter = getCategoryPattern(request).search
2.66 + return request.rootpage.getPageList(filter=cat_filter)
2.67 +
2.68 +def getCategoryMapping(category_pagenames, request):
2.69
2.70 """
2.71 - Return a tuple with the content/media type and encoding, extracted from the
2.72 - given 'content_type' header value.
2.73 + For the given 'category_pagenames' return a list of tuples of the form
2.74 + (category name, category page name) using the given 'request'.
2.75 + """
2.76 +
2.77 + cat_pattern = getCategoryPattern(request)
2.78 + mapping = []
2.79 + for pagename in category_pagenames:
2.80 + name = cat_pattern.match(pagename).group("key")
2.81 + if name != "Category":
2.82 + mapping.append((name, pagename))
2.83 + mapping.sort()
2.84 + return mapping
2.85 +
2.86 +def getCategoryPages(pagename, request):
2.87 +
2.88 + """
2.89 + Return the pages associated with the given category 'pagename' using the
2.90 + 'request'.
2.91 + """
2.92 +
2.93 + query = search.QueryParser().parse_query('category:%s' % pagename)
2.94 + results = search.searchPages(request, query, "page_name")
2.95 +
2.96 + cat_pattern = getCategoryPattern(request)
2.97 + pages = []
2.98 + for page in results.hits:
2.99 + if not cat_pattern.match(page.page_name):
2.100 + pages.append(page)
2.101 + return pages
2.102 +
2.103 +def getAllCategoryPages(category_names, request):
2.104 +
2.105 + """
2.106 + Return all pages belonging to the categories having the given
2.107 + 'category_names', using the given 'request'.
2.108 + """
2.109 +
2.110 + pages = []
2.111 + pagenames = set()
2.112 +
2.113 + for category_name in category_names:
2.114 +
2.115 + # Get the pages and page names in the category.
2.116 +
2.117 + pages_in_category = getCategoryPages(category_name, request)
2.118 +
2.119 + # Visit each page in the category.
2.120 +
2.121 + for page_in_category in pages_in_category:
2.122 + pagename = page_in_category.page_name
2.123 +
2.124 + # Only process each page once.
2.125 +
2.126 + if pagename in pagenames:
2.127 + continue
2.128 + else:
2.129 + pagenames.add(pagename)
2.130 +
2.131 + pages.append(page_in_category)
2.132 +
2.133 + return pages
2.134 +
2.135 +# WikiDict functions.
2.136 +
2.137 +def getWikiDict(pagename, request):
2.138 +
2.139 + """
2.140 + Return the WikiDict provided by the given 'pagename' using the given
2.141 + 'request'.
2.142 """
2.143
2.144 - m = encoding_regexp.search(content_type)
2.145 - if m:
2.146 - return m.group("content_type"), m.group("encoding")
2.147 + if pagename and Page(request, pagename).exists() and request.user.may.read(pagename):
2.148 + if hasattr(request.dicts, "dict"):
2.149 + return request.dicts.dict(pagename)
2.150 + else:
2.151 + return request.dicts[pagename]
2.152 else:
2.153 - return None, None
2.154 + return None
2.155 +
2.156 +# Searching-related functions.
2.157 +
2.158 +def getPagesFromResults(result_pages, request):
2.159 +
2.160 + "Return genuine pages for the given 'result_pages' using the 'request'."
2.161 +
2.162 + return [Page(request, page.page_name) for page in result_pages]
2.163 +
2.164 +# Region/section parsing.
2.165 +
2.166 +def getRegions(s, include_non_regions=False):
2.167 +
2.168 + """
2.169 + Parse the string 's', returning a list of explicitly declared regions.
2.170 +
2.171 + If 'include_non_regions' is specified as a true value, fragments will be
2.172 + included for text between explicitly declared regions.
2.173 + """
2.174 +
2.175 + regions = []
2.176 + marker = None
2.177 + is_block = True
2.178 +
2.179 + # Start a region for exposed text, if appropriate.
2.180 +
2.181 + if include_non_regions:
2.182 + regions.append("")
2.183 +
2.184 + for match_text in marker_regexp.split(s):
2.185 +
2.186 + # Capture section text.
2.187 +
2.188 + if is_block:
2.189 + if marker or include_non_regions:
2.190 + regions[-1] += match_text
2.191 +
2.192 + # Handle section markers.
2.193 +
2.194 + elif not is_block:
2.195 +
2.196 + # Close any open sections, returning to exposed text regions.
2.197 +
2.198 + if marker:
2.199 + if match_text.startswith("}") and len(marker) == len(match_text):
2.200 + marker = None
2.201 +
2.202 + # Start a region for exposed text, if appropriate.
2.203 +
2.204 + if include_non_regions:
2.205 + regions.append("")
2.206 +
2.207 + # Without a current marker, start a section if an appropriate marker
2.208 + # is given.
2.209 +
2.210 + elif match_text.startswith("{"):
2.211 + marker = match_text
2.212 + regions.append("")
2.213 +
2.214 + # Markers and section text are added to the current region.
2.215 +
2.216 + regions[-1] += match_text
2.217
2.218 -def int_or_none(x):
2.219 - if x is None:
2.220 - return x
2.221 - else:
2.222 - return int(x)
2.223 + # The match text alternates between text between markers and the markers
2.224 + # themselves.
2.225 +
2.226 + is_block = not is_block
2.227 +
2.228 + return regions
2.229 +
2.230 +def getFragmentsFromRegions(regions):
2.231 +
2.232 + """
2.233 + Return fragments from the given 'regions', each having the form
2.234 + (format, arguments, body text).
2.235 + """
2.236 +
2.237 + fragments = []
2.238 +
2.239 + for region in regions:
2.240 + if region.startswith("{{{"):
2.241 +
2.242 + body = region.lstrip("{").rstrip("}").lstrip()
2.243 +
2.244 + # Remove any prelude and process metadata.
2.245 +
2.246 + if body.startswith("#!"):
2.247 + body = body[2:]
2.248 +
2.249 + arguments, body = body.split("\n", 1)
2.250 +
2.251 + # Get any parser/format declaration.
2.252 +
2.253 + if arguments and not arguments[0].isspace():
2.254 + details = arguments.split(None, 1)
2.255 + if len(details) == 2:
2.256 + format, arguments = details
2.257 + else:
2.258 + format = details[0]
2.259 + arguments = ""
2.260 + else:
2.261 + format = None
2.262 +
2.263 + # Get the attributes/arguments for the region.
2.264 +
2.265 + attributes = parseAttributes(arguments, False)
2.266 +
2.267 + # Add an entry for the format in the attribute dictionary.
2.268 +
2.269 + if format and not attributes.has_key(format):
2.270 + attributes[format] = True
2.271 +
2.272 + fragments.append((format, attributes, body))
2.273 +
2.274 + else:
2.275 + fragments.append((None, {}, body))
2.276 +
2.277 + else:
2.278 + fragments.append((None, {}, region))
2.279 +
2.280 + return fragments
2.281 +
2.282 +def getFragments(s, include_non_regions=False):
2.283 +
2.284 + """
2.285 + Return fragments for the given string 's', each having the form
2.286 + (format, arguments, body text).
2.287 +
2.288 + If 'include_non_regions' is specified as a true value, fragments will be
2.289 + included for text between explicitly declared regions.
2.290 + """
2.291 +
2.292 + return getFragmentsFromRegions(getRegions(s, include_non_regions))
2.293 +
2.294 +# Region/section attribute parsing.
2.295
2.296 def parseAttributes(s, escape=True):
2.297
2.298 @@ -113,7 +359,7 @@
2.299 else:
2.300 return token
2.301
2.302 -# Utility classes and associated functions.
2.303 +# Request-related classes and associated functions.
2.304
2.305 class Form:
2.306
2.307 @@ -528,6 +774,21 @@
2.308 else:
2.309 return []
2.310
2.311 +# Content type parsing.
2.312 +
2.313 +def getContentTypeAndEncoding(content_type):
2.314 +
2.315 + """
2.316 + Return a tuple with the content/media type and encoding, extracted from the
2.317 + given 'content_type' header value.
2.318 + """
2.319 +
2.320 + m = encoding_regexp.search(content_type)
2.321 + if m:
2.322 + return m.group("content_type"), m.group("encoding")
2.323 + else:
2.324 + return None, None
2.325 +
2.326 # Page access functions.
2.327
2.328 def getPageURL(page):
2.329 @@ -664,6 +925,32 @@
2.330 buf.close()
2.331 return text
2.332
2.333 +# Textual representations.
2.334 +
2.335 +def getSimpleWikiText(text):
2.336 +
2.337 + """
2.338 + Return the plain text representation of the given 'text' which may employ
2.339 + certain Wiki syntax features, such as those providing verbatim or monospaced
2.340 + text.
2.341 + """
2.342 +
2.343 + # NOTE: Re-implementing support for verbatim text and linking avoidance.
2.344 +
2.345 + return "".join([s for s in verbatim_regexp.split(text) if s is not None])
2.346 +
2.347 +def getEncodedWikiText(text):
2.348 +
2.349 + "Encode the given 'text' in a verbatim representation."
2.350 +
2.351 + return "<<Verbatim(%s)>>" % text
2.352 +
2.353 +def getPrettyTitle(title):
2.354 +
2.355 + "Return a nicely formatted version of the given 'title'."
2.356 +
2.357 + return title.replace("_", " ").replace("/", u" » ")
2.358 +
2.359 # User interface functions.
2.360
2.361 def getParameter(request, name, default=None):
2.362 @@ -707,26 +994,29 @@
2.363 title = page.split_title(force=1)
2.364 return getPrettyTitle(title)
2.365
2.366 -def linkToPage(request, page, text, query_string=None, **kw):
2.367 +def linkToPage(request, page, text, query_string=None, anchor=None, **kw):
2.368
2.369 """
2.370 Using 'request', return a link to 'page' with the given link 'text' and
2.371 - optional 'query_string'.
2.372 + optional 'query_string' and 'anchor'.
2.373 """
2.374
2.375 text = wikiutil.escape(text)
2.376 - return page.link_to_raw(request, text, query_string, **kw)
2.377 + return page.link_to_raw(request, text, query_string, anchor, **kw)
2.378
2.379 -def linkToResource(url, request, text, query_string=None):
2.380 +def linkToResource(url, request, text, query_string=None, anchor=None):
2.381
2.382 """
2.383 Using 'request', return a link to 'url' with the given link 'text' and
2.384 - optional 'query_string'.
2.385 + optional 'query_string' and 'anchor'.
2.386 """
2.387
2.388 + if anchor:
2.389 + url += "#%s" % anchor
2.390 +
2.391 if query_string:
2.392 query_string = wikiutil.makeQueryString(query_string)
2.393 - url = "%s?%s" % (url, query_string)
2.394 + url += "?%s" % query_string
2.395
2.396 formatter = request.page and getattr(request.page, "formatter", None) or request.html_formatter
2.397
4.1 --- a/setup.py Tue Sep 11 21:38:01 2012 +0200
4.2 +++ b/setup.py Sat Sep 29 16:58:57 2012 +0200
4.3 @@ -9,5 +9,7 @@
4.4 author_email = "paul@boddie.org.uk",
4.5 url = "http://hgweb.boddie.org.uk/MoinSupport",
4.6 version = "0.2",
4.7 - py_modules = ["DateSupport", "LocationSupport", "MoinDateSupport", "MoinRemoteSupport", "MoinSupport", "ViewSupport"]
4.8 + py_modules = ["DateSupport", "GeneralSupport", "LocationSupport",
4.9 + "MoinDateSupport", "MoinRemoteSupport", "MoinSupport",
4.10 + "ViewSupport"]
4.11 )
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/tests/test_regions.py Sat Sep 29 16:58:57 2012 +0200
5.3 @@ -0,0 +1,50 @@
5.4 +#!/usr/bin/env python
5.5 +
5.6 +from MoinSupport import getRegions, getFragments
5.7 +
5.8 +s = """
5.9 +Test some shared content...
5.10 +
5.11 +{{{#!MoinShare fragment=101
5.12 +Hello!
5.13 +
5.14 +This is ''testing'' shared content.
5.15 +}}}
5.16 +
5.17 +{{{#!html fragment=100 moinshare
5.18 +<table>
5.19 +<tr>
5.20 +<td>Test some <strong>HTML</strong> content.</td>
5.21 +<td>This is a table.</td>
5.22 +</tr>
5.23 +</table>
5.24 +}}}
5.25 +
5.26 +Some trailing content.
5.27 +"""
5.28 +
5.29 +regions = getRegions(s)
5.30 +fragments = getFragments(s)
5.31 +expected = 2
5.32 +
5.33 +print regions
5.34 +print
5.35 +print len(regions) == expected, ": length is", len(regions), "==", expected
5.36 +print
5.37 +print fragments
5.38 +print
5.39 +print len(fragments) == expected, ": length is", len(fragments), "==", expected
5.40 +
5.41 +regions = getRegions(s, True)
5.42 +fragments = getFragments(s, True)
5.43 +expected = 5
5.44 +
5.45 +print regions
5.46 +print
5.47 +print len(regions) == expected, ": length is", len(regions), "==", expected
5.48 +print
5.49 +print fragments
5.50 +print
5.51 +print len(fragments) == expected, ": length is", len(fragments), "==", expected
5.52 +
5.53 +# vim: tabstop=4 expandtab shiftwidth=4