# HG changeset patch # User Paul Boddie # Date 1348930737 -7200 # Node ID 3bd63c166dd56ebd829fb4001a1d58b44bd200dc # Parent cbc8c3fb3fe8145935763d34a57268736ec93fab Added a selection of different functionality from EventAggregator, along with region/section parsing support from MoinShare. diff -r cbc8c3fb3fe8 -r 3bd63c166dd5 GeneralSupport.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GeneralSupport.py Sat Sep 29 16:58:57 2012 +0200 @@ -0,0 +1,28 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - GeneralSupport library + + @copyright: 2008, 2009, 2010, 2011, 2012 by Paul Boddie + @license: GNU GPL (v2 or later), see COPYING.txt for details. +""" + +# General utility functions. + +def int_or_none(x): + if x is None: + return x + else: + return int(x) + +def to_list(s, sep): + return [x.strip() for x in s.split(sep) if x.strip()] + +def sort_none_first(x, y): + if x is None: + return -1 + elif y is None: + return 1 + else: + return cmp(x, y) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r cbc8c3fb3fe8 -r 3bd63c166dd5 MoinSupport.py --- a/MoinSupport.py Tue Sep 11 21:38:01 2012 +0200 +++ b/MoinSupport.py Sat Sep 29 16:58:57 2012 +0200 @@ -10,7 +10,7 @@ from DateSupport import * from MoinMoin.Page import Page -from MoinMoin import config, wikiutil +from MoinMoin import config, search, wikiutil from StringIO import StringIO from shlex import shlex import re @@ -28,26 +28,272 @@ accept_regexp_str = ur';\s*q=' accept_regexp = re.compile(accept_regexp_str) -# Utility functions. +# Extraction of shared fragments. + +marker_regexp_str = r"([{]{3,}|[}]{3,})" +marker_regexp = re.compile(marker_regexp_str, re.MULTILINE | re.DOTALL) # {{{... or }}}... + +# Category extraction from pages. + +category_regexp = None + +# Simple content parsing. + +verbatim_regexp = re.compile(ur'(?:' + ur'<.*?)\)>>' + ur'|' + ur'\[\[Verbatim\((?P.*?)\)\]\]' + ur'|' + ur'!(?P.*?)(\s|$)?' + ur'|' + ur'`(?P.*?)`' + ur'|' + ur'{{{(?P.*?)}}}' + ur')', re.UNICODE) + +# Category discovery. -def getContentTypeAndEncoding(content_type): +def getCategoryPattern(request): + global category_regexp + + try: + return request.cfg.cache.page_category_regexact + except AttributeError: + + # Use regular expression from MoinMoin 1.7.1 otherwise. + + if category_regexp is None: + category_regexp = re.compile(u'^%s$' % ur'(?PCategory(?P(?!Template)\S+))', re.UNICODE) + return category_regexp + +def getCategories(request): + + """ + From the AdvancedSearch macro, return a list of category page names using + the given 'request'. + """ + + # This will return all pages with "Category" in the title. + + cat_filter = getCategoryPattern(request).search + return request.rootpage.getPageList(filter=cat_filter) + +def getCategoryMapping(category_pagenames, request): """ - Return a tuple with the content/media type and encoding, extracted from the - given 'content_type' header value. + For the given 'category_pagenames' return a list of tuples of the form + (category name, category page name) using the given 'request'. + """ + + cat_pattern = getCategoryPattern(request) + mapping = [] + for pagename in category_pagenames: + name = cat_pattern.match(pagename).group("key") + if name != "Category": + mapping.append((name, pagename)) + mapping.sort() + return mapping + +def getCategoryPages(pagename, request): + + """ + Return the pages associated with the given category 'pagename' using the + 'request'. + """ + + query = search.QueryParser().parse_query('category:%s' % pagename) + results = search.searchPages(request, query, "page_name") + + cat_pattern = getCategoryPattern(request) + pages = [] + for page in results.hits: + if not cat_pattern.match(page.page_name): + pages.append(page) + return pages + +def getAllCategoryPages(category_names, request): + + """ + Return all pages belonging to the categories having the given + 'category_names', using the given 'request'. + """ + + pages = [] + pagenames = set() + + for category_name in category_names: + + # Get the pages and page names in the category. + + pages_in_category = getCategoryPages(category_name, request) + + # Visit each page in the category. + + for page_in_category in pages_in_category: + pagename = page_in_category.page_name + + # Only process each page once. + + if pagename in pagenames: + continue + else: + pagenames.add(pagename) + + pages.append(page_in_category) + + return pages + +# WikiDict functions. + +def getWikiDict(pagename, request): + + """ + Return the WikiDict provided by the given 'pagename' using the given + 'request'. """ - m = encoding_regexp.search(content_type) - if m: - return m.group("content_type"), m.group("encoding") + if pagename and Page(request, pagename).exists() and request.user.may.read(pagename): + if hasattr(request.dicts, "dict"): + return request.dicts.dict(pagename) + else: + return request.dicts[pagename] else: - return None, None + return None + +# Searching-related functions. + +def getPagesFromResults(result_pages, request): + + "Return genuine pages for the given 'result_pages' using the 'request'." + + return [Page(request, page.page_name) for page in result_pages] + +# Region/section parsing. + +def getRegions(s, include_non_regions=False): + + """ + Parse the string 's', returning a list of explicitly declared regions. + + If 'include_non_regions' is specified as a true value, fragments will be + included for text between explicitly declared regions. + """ + + regions = [] + marker = None + is_block = True + + # Start a region for exposed text, if appropriate. + + if include_non_regions: + regions.append("") + + for match_text in marker_regexp.split(s): + + # Capture section text. + + if is_block: + if marker or include_non_regions: + regions[-1] += match_text + + # Handle section markers. + + elif not is_block: + + # Close any open sections, returning to exposed text regions. + + if marker: + if match_text.startswith("}") and len(marker) == len(match_text): + marker = None + + # Start a region for exposed text, if appropriate. + + if include_non_regions: + regions.append("") + + # Without a current marker, start a section if an appropriate marker + # is given. + + elif match_text.startswith("{"): + marker = match_text + regions.append("") + + # Markers and section text are added to the current region. + + regions[-1] += match_text -def int_or_none(x): - if x is None: - return x - else: - return int(x) + # The match text alternates between text between markers and the markers + # themselves. + + is_block = not is_block + + return regions + +def getFragmentsFromRegions(regions): + + """ + Return fragments from the given 'regions', each having the form + (format, arguments, body text). + """ + + fragments = [] + + for region in regions: + if region.startswith("{{{"): + + body = region.lstrip("{").rstrip("}").lstrip() + + # Remove any prelude and process metadata. + + if body.startswith("#!"): + body = body[2:] + + arguments, body = body.split("\n", 1) + + # Get any parser/format declaration. + + if arguments and not arguments[0].isspace(): + details = arguments.split(None, 1) + if len(details) == 2: + format, arguments = details + else: + format = details[0] + arguments = "" + else: + format = None + + # Get the attributes/arguments for the region. + + attributes = parseAttributes(arguments, False) + + # Add an entry for the format in the attribute dictionary. + + if format and not attributes.has_key(format): + attributes[format] = True + + fragments.append((format, attributes, body)) + + else: + fragments.append((None, {}, body)) + + else: + fragments.append((None, {}, region)) + + return fragments + +def getFragments(s, include_non_regions=False): + + """ + Return fragments for the given string 's', each having the form + (format, arguments, body text). + + If 'include_non_regions' is specified as a true value, fragments will be + included for text between explicitly declared regions. + """ + + return getFragmentsFromRegions(getRegions(s, include_non_regions)) + +# Region/section attribute parsing. def parseAttributes(s, escape=True): @@ -113,7 +359,7 @@ else: return token -# Utility classes and associated functions. +# Request-related classes and associated functions. class Form: @@ -528,6 +774,21 @@ else: return [] +# Content type parsing. + +def getContentTypeAndEncoding(content_type): + + """ + Return a tuple with the content/media type and encoding, extracted from the + given 'content_type' header value. + """ + + m = encoding_regexp.search(content_type) + if m: + return m.group("content_type"), m.group("encoding") + else: + return None, None + # Page access functions. def getPageURL(page): @@ -664,6 +925,32 @@ buf.close() return text +# Textual representations. + +def getSimpleWikiText(text): + + """ + Return the plain text representation of the given 'text' which may employ + certain Wiki syntax features, such as those providing verbatim or monospaced + text. + """ + + # NOTE: Re-implementing support for verbatim text and linking avoidance. + + return "".join([s for s in verbatim_regexp.split(text) if s is not None]) + +def getEncodedWikiText(text): + + "Encode the given 'text' in a verbatim representation." + + return "<>" % text + +def getPrettyTitle(title): + + "Return a nicely formatted version of the given 'title'." + + return title.replace("_", " ").replace("/", u" » ") + # User interface functions. def getParameter(request, name, default=None): @@ -707,26 +994,29 @@ title = page.split_title(force=1) return getPrettyTitle(title) -def linkToPage(request, page, text, query_string=None, **kw): +def linkToPage(request, page, text, query_string=None, anchor=None, **kw): """ Using 'request', return a link to 'page' with the given link 'text' and - optional 'query_string'. + optional 'query_string' and 'anchor'. """ text = wikiutil.escape(text) - return page.link_to_raw(request, text, query_string, **kw) + return page.link_to_raw(request, text, query_string, anchor, **kw) -def linkToResource(url, request, text, query_string=None): +def linkToResource(url, request, text, query_string=None, anchor=None): """ Using 'request', return a link to 'url' with the given link 'text' and - optional 'query_string'. + optional 'query_string' and 'anchor'. """ + if anchor: + url += "#%s" % anchor + if query_string: query_string = wikiutil.makeQueryString(query_string) - url = "%s?%s" % (url, query_string) + url += "?%s" % query_string formatter = request.page and getattr(request.page, "formatter", None) or request.html_formatter diff -r cbc8c3fb3fe8 -r 3bd63c166dd5 README.txt --- a/README.txt Tue Sep 11 21:38:01 2012 +0200 +++ b/README.txt Sat Sep 29 16:58:57 2012 +0200 @@ -67,12 +67,16 @@ * Added section argument processing functions from the ImprovedTableParser distribution to MoinSupport. - * Added a getCurrentTime function to DateSupport. + * Added region/section parsing functions to MoinSupport. * Added parsing/formatting-related functions from EventAggregator and ImprovedTableParser to MoinSupport. + * Added category-, WikiDict- and various parsing/encoding-related functions + from EventAggregator to MoinSupport. + * Added a header-writing function to MoinSupport. + * Added a getCurrentTime function to DateSupport. * Added remote resource management from EventAggregator to MoinRemoteSupport. - * Added a header-writing function to MoinSupport. + * Added general utility functions from EventAggregator as GeneralSupport. * Added view-related functions from EventAggregator as ViewSupport. Release Procedures diff -r cbc8c3fb3fe8 -r 3bd63c166dd5 setup.py --- a/setup.py Tue Sep 11 21:38:01 2012 +0200 +++ b/setup.py Sat Sep 29 16:58:57 2012 +0200 @@ -9,5 +9,7 @@ author_email = "paul@boddie.org.uk", url = "http://hgweb.boddie.org.uk/MoinSupport", version = "0.2", - py_modules = ["DateSupport", "LocationSupport", "MoinDateSupport", "MoinRemoteSupport", "MoinSupport", "ViewSupport"] + py_modules = ["DateSupport", "GeneralSupport", "LocationSupport", + "MoinDateSupport", "MoinRemoteSupport", "MoinSupport", + "ViewSupport"] ) diff -r cbc8c3fb3fe8 -r 3bd63c166dd5 tests/test_regions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_regions.py Sat Sep 29 16:58:57 2012 +0200 @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +from MoinSupport import getRegions, getFragments + +s = """ +Test some shared content... + +{{{#!MoinShare fragment=101 +Hello! + +This is ''testing'' shared content. +}}} + +{{{#!html fragment=100 moinshare + + + + + +
Test some HTML content.This is a table.
+}}} + +Some trailing content. +""" + +regions = getRegions(s) +fragments = getFragments(s) +expected = 2 + +print regions +print +print len(regions) == expected, ": length is", len(regions), "==", expected +print +print fragments +print +print len(fragments) == expected, ": length is", len(fragments), "==", expected + +regions = getRegions(s, True) +fragments = getFragments(s, True) +expected = 5 + +print regions +print +print len(regions) == expected, ": length is", len(regions), "==", expected +print +print fragments +print +print len(fragments) == expected, ": length is", len(fragments), "==", expected + +# vim: tabstop=4 expandtab shiftwidth=4