1.1 --- a/MoinSupport.py Tue Sep 11 21:38:01 2012 +0200
1.2 +++ b/MoinSupport.py Sat Sep 29 16:58:57 2012 +0200
1.3 @@ -10,7 +10,7 @@
1.4
1.5 from DateSupport import *
1.6 from MoinMoin.Page import Page
1.7 -from MoinMoin import config, wikiutil
1.8 +from MoinMoin import config, search, wikiutil
1.9 from StringIO import StringIO
1.10 from shlex import shlex
1.11 import re
1.12 @@ -28,26 +28,272 @@
1.13 accept_regexp_str = ur';\s*q='
1.14 accept_regexp = re.compile(accept_regexp_str)
1.15
1.16 -# Utility functions.
1.17 +# Extraction of shared fragments.
1.18 +
1.19 +marker_regexp_str = r"([{]{3,}|[}]{3,})"
1.20 +marker_regexp = re.compile(marker_regexp_str, re.MULTILINE | re.DOTALL) # {{{... or }}}...
1.21 +
1.22 +# Category extraction from pages.
1.23 +
1.24 +category_regexp = None
1.25 +
1.26 +# Simple content parsing.
1.27 +
1.28 +verbatim_regexp = re.compile(ur'(?:'
1.29 + ur'<<Verbatim\((?P<verbatim>.*?)\)>>'
1.30 + ur'|'
1.31 + ur'\[\[Verbatim\((?P<verbatim2>.*?)\)\]\]'
1.32 + ur'|'
1.33 + ur'!(?P<verbatim3>.*?)(\s|$)?'
1.34 + ur'|'
1.35 + ur'`(?P<monospace>.*?)`'
1.36 + ur'|'
1.37 + ur'{{{(?P<preformatted>.*?)}}}'
1.38 + ur')', re.UNICODE)
1.39 +
1.40 +# Category discovery.
1.41
1.42 -def getContentTypeAndEncoding(content_type):
1.43 +def getCategoryPattern(request):
1.44 + global category_regexp
1.45 +
1.46 + try:
1.47 + return request.cfg.cache.page_category_regexact
1.48 + except AttributeError:
1.49 +
1.50 + # Use regular expression from MoinMoin 1.7.1 otherwise.
1.51 +
1.52 + if category_regexp is None:
1.53 + category_regexp = re.compile(u'^%s$' % ur'(?P<all>Category(?P<key>(?!Template)\S+))', re.UNICODE)
1.54 + return category_regexp
1.55 +
1.56 +def getCategories(request):
1.57 +
1.58 + """
1.59 + From the AdvancedSearch macro, return a list of category page names using
1.60 + the given 'request'.
1.61 + """
1.62 +
1.63 + # This will return all pages with "Category" in the title.
1.64 +
1.65 + cat_filter = getCategoryPattern(request).search
1.66 + return request.rootpage.getPageList(filter=cat_filter)
1.67 +
1.68 +def getCategoryMapping(category_pagenames, request):
1.69
1.70 """
1.71 - Return a tuple with the content/media type and encoding, extracted from the
1.72 - given 'content_type' header value.
1.73 + For the given 'category_pagenames' return a list of tuples of the form
1.74 + (category name, category page name) using the given 'request'.
1.75 + """
1.76 +
1.77 + cat_pattern = getCategoryPattern(request)
1.78 + mapping = []
1.79 + for pagename in category_pagenames:
1.80 + name = cat_pattern.match(pagename).group("key")
1.81 + if name != "Category":
1.82 + mapping.append((name, pagename))
1.83 + mapping.sort()
1.84 + return mapping
1.85 +
1.86 +def getCategoryPages(pagename, request):
1.87 +
1.88 + """
1.89 + Return the pages associated with the given category 'pagename' using the
1.90 + 'request'.
1.91 + """
1.92 +
1.93 + query = search.QueryParser().parse_query('category:%s' % pagename)
1.94 + results = search.searchPages(request, query, "page_name")
1.95 +
1.96 + cat_pattern = getCategoryPattern(request)
1.97 + pages = []
1.98 + for page in results.hits:
1.99 + if not cat_pattern.match(page.page_name):
1.100 + pages.append(page)
1.101 + return pages
1.102 +
1.103 +def getAllCategoryPages(category_names, request):
1.104 +
1.105 + """
1.106 + Return all pages belonging to the categories having the given
1.107 + 'category_names', using the given 'request'.
1.108 + """
1.109 +
1.110 + pages = []
1.111 + pagenames = set()
1.112 +
1.113 + for category_name in category_names:
1.114 +
1.115 + # Get the pages and page names in the category.
1.116 +
1.117 + pages_in_category = getCategoryPages(category_name, request)
1.118 +
1.119 + # Visit each page in the category.
1.120 +
1.121 + for page_in_category in pages_in_category:
1.122 + pagename = page_in_category.page_name
1.123 +
1.124 + # Only process each page once.
1.125 +
1.126 + if pagename in pagenames:
1.127 + continue
1.128 + else:
1.129 + pagenames.add(pagename)
1.130 +
1.131 + pages.append(page_in_category)
1.132 +
1.133 + return pages
1.134 +
1.135 +# WikiDict functions.
1.136 +
1.137 +def getWikiDict(pagename, request):
1.138 +
1.139 + """
1.140 + Return the WikiDict provided by the given 'pagename' using the given
1.141 + 'request'.
1.142 """
1.143
1.144 - m = encoding_regexp.search(content_type)
1.145 - if m:
1.146 - return m.group("content_type"), m.group("encoding")
1.147 + if pagename and Page(request, pagename).exists() and request.user.may.read(pagename):
1.148 + if hasattr(request.dicts, "dict"):
1.149 + return request.dicts.dict(pagename)
1.150 + else:
1.151 + return request.dicts[pagename]
1.152 else:
1.153 - return None, None
1.154 + return None
1.155 +
1.156 +# Searching-related functions.
1.157 +
1.158 +def getPagesFromResults(result_pages, request):
1.159 +
1.160 + "Return genuine pages for the given 'result_pages' using the 'request'."
1.161 +
1.162 + return [Page(request, page.page_name) for page in result_pages]
1.163 +
1.164 +# Region/section parsing.
1.165 +
1.166 +def getRegions(s, include_non_regions=False):
1.167 +
1.168 + """
1.169 + Parse the string 's', returning a list of explicitly declared regions.
1.170 +
1.171 + If 'include_non_regions' is specified as a true value, fragments will be
1.172 + included for text between explicitly declared regions.
1.173 + """
1.174 +
1.175 + regions = []
1.176 + marker = None
1.177 + is_block = True
1.178 +
1.179 + # Start a region for exposed text, if appropriate.
1.180 +
1.181 + if include_non_regions:
1.182 + regions.append("")
1.183 +
1.184 + for match_text in marker_regexp.split(s):
1.185 +
1.186 + # Capture section text.
1.187 +
1.188 + if is_block:
1.189 + if marker or include_non_regions:
1.190 + regions[-1] += match_text
1.191 +
1.192 + # Handle section markers.
1.193 +
1.194 + elif not is_block:
1.195 +
1.196 + # Close any open sections, returning to exposed text regions.
1.197 +
1.198 + if marker:
1.199 + if match_text.startswith("}") and len(marker) == len(match_text):
1.200 + marker = None
1.201 +
1.202 + # Start a region for exposed text, if appropriate.
1.203 +
1.204 + if include_non_regions:
1.205 + regions.append("")
1.206 +
1.207 + # Without a current marker, start a section if an appropriate marker
1.208 + # is given.
1.209 +
1.210 + elif match_text.startswith("{"):
1.211 + marker = match_text
1.212 + regions.append("")
1.213 +
1.214 + # Markers and section text are added to the current region.
1.215 +
1.216 + regions[-1] += match_text
1.217
1.218 -def int_or_none(x):
1.219 - if x is None:
1.220 - return x
1.221 - else:
1.222 - return int(x)
1.223 + # The match text alternates between text between markers and the markers
1.224 + # themselves.
1.225 +
1.226 + is_block = not is_block
1.227 +
1.228 + return regions
1.229 +
1.230 +def getFragmentsFromRegions(regions):
1.231 +
1.232 + """
1.233 + Return fragments from the given 'regions', each having the form
1.234 + (format, arguments, body text).
1.235 + """
1.236 +
1.237 + fragments = []
1.238 +
1.239 + for region in regions:
1.240 + if region.startswith("{{{"):
1.241 +
1.242 + body = region.lstrip("{").rstrip("}").lstrip()
1.243 +
1.244 + # Remove any prelude and process metadata.
1.245 +
1.246 + if body.startswith("#!"):
1.247 + body = body[2:]
1.248 +
1.249 + arguments, body = body.split("\n", 1)
1.250 +
1.251 + # Get any parser/format declaration.
1.252 +
1.253 + if arguments and not arguments[0].isspace():
1.254 + details = arguments.split(None, 1)
1.255 + if len(details) == 2:
1.256 + format, arguments = details
1.257 + else:
1.258 + format = details[0]
1.259 + arguments = ""
1.260 + else:
1.261 + format = None
1.262 +
1.263 + # Get the attributes/arguments for the region.
1.264 +
1.265 + attributes = parseAttributes(arguments, False)
1.266 +
1.267 + # Add an entry for the format in the attribute dictionary.
1.268 +
1.269 + if format and not attributes.has_key(format):
1.270 + attributes[format] = True
1.271 +
1.272 + fragments.append((format, attributes, body))
1.273 +
1.274 + else:
1.275 + fragments.append((None, {}, body))
1.276 +
1.277 + else:
1.278 + fragments.append((None, {}, region))
1.279 +
1.280 + return fragments
1.281 +
1.282 +def getFragments(s, include_non_regions=False):
1.283 +
1.284 + """
1.285 + Return fragments for the given string 's', each having the form
1.286 + (format, arguments, body text).
1.287 +
1.288 + If 'include_non_regions' is specified as a true value, fragments will be
1.289 + included for text between explicitly declared regions.
1.290 + """
1.291 +
1.292 + return getFragmentsFromRegions(getRegions(s, include_non_regions))
1.293 +
1.294 +# Region/section attribute parsing.
1.295
1.296 def parseAttributes(s, escape=True):
1.297
1.298 @@ -113,7 +359,7 @@
1.299 else:
1.300 return token
1.301
1.302 -# Utility classes and associated functions.
1.303 +# Request-related classes and associated functions.
1.304
1.305 class Form:
1.306
1.307 @@ -528,6 +774,21 @@
1.308 else:
1.309 return []
1.310
1.311 +# Content type parsing.
1.312 +
1.313 +def getContentTypeAndEncoding(content_type):
1.314 +
1.315 + """
1.316 + Return a tuple with the content/media type and encoding, extracted from the
1.317 + given 'content_type' header value.
1.318 + """
1.319 +
1.320 + m = encoding_regexp.search(content_type)
1.321 + if m:
1.322 + return m.group("content_type"), m.group("encoding")
1.323 + else:
1.324 + return None, None
1.325 +
1.326 # Page access functions.
1.327
1.328 def getPageURL(page):
1.329 @@ -664,6 +925,32 @@
1.330 buf.close()
1.331 return text
1.332
1.333 +# Textual representations.
1.334 +
1.335 +def getSimpleWikiText(text):
1.336 +
1.337 + """
1.338 + Return the plain text representation of the given 'text' which may employ
1.339 + certain Wiki syntax features, such as those providing verbatim or monospaced
1.340 + text.
1.341 + """
1.342 +
1.343 + # NOTE: Re-implementing support for verbatim text and linking avoidance.
1.344 +
1.345 + return "".join([s for s in verbatim_regexp.split(text) if s is not None])
1.346 +
1.347 +def getEncodedWikiText(text):
1.348 +
1.349 + "Encode the given 'text' in a verbatim representation."
1.350 +
1.351 + return "<<Verbatim(%s)>>" % text
1.352 +
1.353 +def getPrettyTitle(title):
1.354 +
1.355 + "Return a nicely formatted version of the given 'title'."
1.356 +
1.357 + return title.replace("_", " ").replace("/", u" » ")
1.358 +
1.359 # User interface functions.
1.360
1.361 def getParameter(request, name, default=None):
1.362 @@ -707,26 +994,29 @@
1.363 title = page.split_title(force=1)
1.364 return getPrettyTitle(title)
1.365
1.366 -def linkToPage(request, page, text, query_string=None, **kw):
1.367 +def linkToPage(request, page, text, query_string=None, anchor=None, **kw):
1.368
1.369 """
1.370 Using 'request', return a link to 'page' with the given link 'text' and
1.371 - optional 'query_string'.
1.372 + optional 'query_string' and 'anchor'.
1.373 """
1.374
1.375 text = wikiutil.escape(text)
1.376 - return page.link_to_raw(request, text, query_string, **kw)
1.377 + return page.link_to_raw(request, text, query_string, anchor, **kw)
1.378
1.379 -def linkToResource(url, request, text, query_string=None):
1.380 +def linkToResource(url, request, text, query_string=None, anchor=None):
1.381
1.382 """
1.383 Using 'request', return a link to 'url' with the given link 'text' and
1.384 - optional 'query_string'.
1.385 + optional 'query_string' and 'anchor'.
1.386 """
1.387
1.388 + if anchor:
1.389 + url += "#%s" % anchor
1.390 +
1.391 if query_string:
1.392 query_string = wikiutil.makeQueryString(query_string)
1.393 - url = "%s?%s" % (url, query_string)
1.394 + url += "?%s" % query_string
1.395
1.396 formatter = request.page and getattr(request.page, "formatter", None) or request.html_formatter
1.397