# HG changeset patch # User Paul Boddie # Date 1298232905 -3600 # Node ID 7a7c02523fb23068868c2fb11f781e45722a7ed7 # Parent faec742b3844490c2b49835362a0b89d24100860 Added a section breakout action. Tidied up the link adding action, introducing common output escaping, and adding selection of the link insertion position if the action is invoked from the sidebar menu. Fixed the link action's form HTML, using the buttons HTML provided to the form method. Changed remote resource text handling and introduced a retrieval condition in the link action. diff -r faec742b3844 -r 7a7c02523fb2 MoinContentSupport.py --- a/MoinContentSupport.py Mon Oct 04 01:03:07 2010 +0200 +++ b/MoinContentSupport.py Sun Feb 20 21:15:05 2011 +0100 @@ -2,14 +2,45 @@ """ MoinMoin - MoinContentSupport library - @copyright: 2008, 2009, 2010 by Paul Boddie + @copyright: 2008, 2009, 2010, 2011 by Paul Boddie @copyright: 2000-2004 Juergen Hermann , 2005-2008 MoinMoin:ThomasWaldmann. @license: GNU GPL (v2 or later), see COPYING.txt for details. """ +from MoinMoin.wikiutil import escape +import re + __version__ = "0.1" +# Regular expressions. +# NOTE: These overlap with ImprovedMoinSearch. + +heading_regexp = re.compile(r"^(?P=+)\s*(?P.*?)\s*(?P=level)$", re.UNICODE | re.MULTILINE) + +def getHeadingDetails(body, min_level=None, max_level=None): + + """ + Return heading details from the given 'body' for headings with the given + 'min_level' to 'max_level' range. Specifying None or omitting 'max_level' or + 'min_level' removes the appropriate constraint on the range. + + A list of tuples containing the heading, the level, and the span (the start + offset and the end offset as a tuple) is returned. + """ + + headings = [] + + for match in heading_regexp.finditer(body): + level = len(match.group("level")) + + if (min_level is None or min_level <= level) and \ + (max_level is None or level <= max_level): + + headings.append((match.group("heading"), level, match.span())) + + return headings + # Utility classes and associated functions. # NOTE: These are a subset of EventAggregatorSupport. @@ -67,4 +98,7 @@ parts = header.split(":") self.request.headers.add(parts[0], ":".join(parts[1:])) +def escattr(s): + return escape(s, 1) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r faec742b3844 -r 7a7c02523fb2 actions/AddLinkToPage.py --- a/actions/AddLinkToPage.py Mon Oct 04 01:03:07 2010 +0200 +++ b/actions/AddLinkToPage.py Sun Feb 20 21:15:05 2011 +0100 @@ -11,10 +11,9 @@ Dependencies = ['pages'] -from MoinMoin import wikiutil from MoinMoin.action import ActionBase from MoinMoin.PageEditor import PageEditor -from MoinContentSupport import ActionSupport +from MoinContentSupport import ActionSupport, escape, escattr import urllib import re @@ -77,7 +76,11 @@ "Get information from the given 'link'." - # NOTE: Insist on remote URLs! + # Insist on remote URLs! + # NOTE: This could probably be done better. + + if not link.startswith("http:"): + return None try: f = urllib.urlopen(link) @@ -85,7 +88,7 @@ return None try: - s = f.read() + s = get_text(f.read()) # Look for metadata. @@ -101,7 +104,7 @@ intro = content if title and intro: - return get_text(title), get_text(intro) + return title, intro # Look for titles/headings and accompanying text. @@ -117,11 +120,11 @@ for intro_match in paragraph_pattern.finditer(s[end:]): intro = get_flattened_content(intro_match.group("text")).strip() if intro: - return get_text(title), get_text(intro) + return title, intro finally: f.close() - return get_text(first_title), u"" + return first_title, u"" def get_flattened_content(s): @@ -134,7 +137,7 @@ l.append(s[last:start]) last = end l.append(s[last:]) - return get_text("".join(l).replace("\n", " ")) + return "".join(l).replace("\n", " ") # Action class and supporting functions. @@ -148,15 +151,28 @@ page = self.page form = self.get_form() - identifier = form.get("identifier", [None])[0] - link = form.get("link", [None])[0] + identifier = form.get("identifier", [""])[0] + identifier_list = [] + + if not identifier: + + # Show all macro identifiers by parsing the page. + + page_body = page.get_raw_body() + + for match in macro_pattern.finditer(page_body): + found_identifier = match.group("identifier") + identifier_list.append('' % ( + escattr(found_identifier), escape(found_identifier))) + + link = form.get("link", [""])[0] insert_before = form.get('insert_before', [""])[0] title = "" introduction = "" # Acquire information from the link. - if link is not None: + if link: link_info = get_link_info(link) # NOTE: Perhaps show a message upon success/failure. @@ -165,31 +181,59 @@ title, introduction = link_info d = { - "identifier" : wikiutil.escape(identifier, 1), - "insert_before" : insert_before and "true" or "", - "link" : wikiutil.escape(link, 1), - "title" : wikiutil.escape(title, 1), - "intro" : wikiutil.escape(introduction, 1), - "url_label" : wikiutil.escape(_("URL")), - "title_label" : wikiutil.escape(_("Title")), - "intro_label" : wikiutil.escape(_("Introduction")), - "description_label" : wikiutil.escape(_("Description")), - "submit_label" : wikiutil.escape(_("Submit link")), - "script_name" : request.getScriptname(), - "page_url" : wikiutil.quoteWikinameURL(page.page_name) + "buttons_html" : buttons_html, + "identifiers_label" : escape(_("Add to position...")), + "identifier" : escattr(identifier), + "insert_before" : insert_before and "true" or "", + "link" : escattr(link), + "title" : escattr(title), + "intro" : escattr(introduction), + "url_label" : escape(_("URL")), + "title_label" : escape(_("Title")), + "intro_label" : escape(_("Introduction")), + "description_label" : escape(_("Description")), + "preview_label" : escape(_("Preview link")), } - html = u''' -
- - + # Given an identifier, preserve the state in a hidden field. + + if not identifier_list: + html = ''' + ''' % d + else: + html = '' + + # Start the rest of the form. + + html += u''' - - + ''' % d + + # Given no identifier, show the choice of insertion positions. + + if identifier_list: + html += ''' + + + + ''' + + # Finish the form. + + html += ''' @@ -203,10 +247,12 @@ - + + -
%(url_label)s
%(identifiers_label)s + +
%(title_label)s
+ %(buttons_html)s +
-
''' % d + ''' % d return html diff -r faec742b3844 -r 7a7c02523fb2 actions/SectionBreakout.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/actions/SectionBreakout.py Sun Feb 20 21:15:05 2011 +0100 @@ -0,0 +1,191 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - SectionBreakout + + Break sections out of a page, making new pages for each of the sections and + replacing them with Include macros. + + @copyright: 2011 Paul Boddie + @license: GNU GPL, see COPYING for details. +""" + +Dependencies = ['pages'] + +from MoinMoin.action import ActionBase +from MoinMoin.PageEditor import PageEditor +from MoinContentSupport import ActionSupport, getHeadingDetails, escape, escattr +import re + +# Action class and supporting functions. + +class SectionBreakout(ActionBase, ActionSupport): + + "An action breaking sections out of pages." + + def get_form_html(self, buttons_html): + _ = self._ + request = self.request + page = self.page + form = self.get_form() + + level = int(form.get("level", ["2"])[0]) + + # Acquire heading details from the page. + + body = page.get_raw_body() + heading_details = getHeadingDetails(body, level, level) + + d = { + "buttons_html" : buttons_html, + "heading_level_label" : escape(_("Heading level")), + "found_headings_label" : escape(_("Headings found in page")), + "preview_label" : escape(_("Preview")), + "level" : escattr(level), + } + + html = u''' + + + + + + + + + + + + + +
%(heading_level_label)s
%(found_headings_label)s''' % d + + for heading, level, span in heading_details: + html += "%s
" % heading + + html += ''' +
%(buttons_html)s
+''' % d + + return html + + def do_action(self): + + "Create the new event." + + _ = self._ + form = self.get_form() + + # A heading level must be provided. + + level = form.get("level", [None])[0] + + if not level: + return 0, _("No heading level specified.") + + return self.break_out_headings(int(level)) + + def render_success(self, msg, msgtype=None): + + """ + Render neither 'msg' nor 'msgtype' since redirection should occur + instead. + NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. + """ + + pass + + def break_out_headings(self, level): + + """ + Break out headings at the given 'level' from the current page. + """ + + _ = self._ + request = self.request + page = self.page + formatter = request.formatter + + # Acquire all heading details from the page. + + page_body = page.get_raw_body() + regions = [] + current_region_start = None + + for heading, found_level, (start, end) in getHeadingDetails(page_body): + + # Upon finding a suitable heading, begin a new region to be broken + # out. + + if current_region_start is None and found_level >= level: + current_region_start = heading, start + + # Upon finding a higher-level heading, end any open region. + + elif current_region_start is not None and found_level <= level: + regions.append(current_region_start + (start,)) + + # For headings at the requested level, open a new region. + + if found_level == level: + current_region_start = heading, start + else: + current_region_start = None + + # End any open region. + + else: + if current_region_start is not None: + regions.append(current_region_start + (len(page_body),)) + + # Make new pages for each region, rebuilding the current page body. + + retained_regions = [] + retained_region_start = 0 + + for heading, start, end in regions: + + # Combine the page name and the heading to make a subpage. + + new_page_name = "%s/%s" % (page.page_name, heading) + + # Open the page for editing. + + new_page = PageEditor(request, new_page_name) + + # Save the new version of the page. + + new_page.saveText(page_body[start:end], 0) + + # Retain the preceding region for the current page. + + retained_regions.append(page_body[retained_region_start:start]) + + # Insert Include macros for the broken out text. + + retained_regions.append("<>\n" % new_page_name) + + # Start a new region to retain. + + retained_region_start = end + + # Retain any remaining text. + + else: + retained_regions.append(page_body[retained_region_start:]) + + # Edit the current page. + + edited_page = PageEditor(request, page.page_name) + edited_page.saveText("".join(retained_regions), 0) + + # NOTE: Perhaps show a message upon failure. + + request.http_redirect(page.url(request)) + return 1, None + +# Action function. + +def execute(pagename, request): + SectionBreakout(pagename, request).render() + +# vim: tabstop=4 expandtab shiftwidth=4