# HG changeset patch # User Paul Boddie # Date 1281829961 -7200 # Node ID d358fd48cf8115728a0972d0c93741d8537df3a8 MoinMoin content editing using a macro plus an action which work together to request links and add details of those links to pages. diff -r 000000000000 -r d358fd48cf81 MoinContentSupport.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MoinContentSupport.py Sun Aug 15 01:52:41 2010 +0200 @@ -0,0 +1,70 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - MoinContentSupport library + + @copyright: 2008, 2009, 2010 by Paul Boddie + @copyright: 2000-2004 Juergen Hermann , + 2005-2008 MoinMoin:ThomasWaldmann. + @license: GNU GPL (v2 or later), see COPYING.txt for details. +""" + +__version__ = "0.1" + +# Utility classes and associated functions. +# NOTE: These are a subset of EventAggregatorSupport. + +class Form: + + """ + A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x + environment. + """ + + def __init__(self, form): + self.form = form + + def get(self, name, default=None): + values = self.form.getlist(name) + if not values: + return default + else: + return values + + def __getitem__(self, name): + return self.form.getlist(name) + +class ActionSupport: + + """ + Work around disruptive MoinMoin changes in 1.9, and also provide useful + convenience methods. + """ + + def get_form(self): + return get_form(self.request) + +def get_form(request): + + "Work around disruptive MoinMoin changes in 1.9." + + if hasattr(request, "values"): + return Form(request.values) + else: + return request.form + +class send_headers: + + """ + A wrapper to preserve MoinMoin 1.8.x (and earlier) request behaviour in a + 1.9.x environment. + """ + + def __init__(self, request): + self.request = request + + def __call__(self, headers): + for header in headers: + parts = header.split(":") + self.request.headers.add(parts[0], ":".join(parts[1:])) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 000000000000 -r d358fd48cf81 actions/AddLinkToPage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/actions/AddLinkToPage.py Sun Aug 15 01:52:41 2010 +0200 @@ -0,0 +1,127 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - AddLinkToPage + + Add a link using a form in the page, getting details of the linked document + and inserting them with the link itself. + + @copyright: 2010 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 +import re + +macro_pattern = re.compile(ur'^(?P.*?)<[^\s,)]+).*?\)>>(?P.*)$', + re.MULTILINE | re.UNICODE) + +# Action class and supporting functions. + +class AddLinkToPage(ActionBase, ActionSupport): + + "An action adding links to pages." + + def do_action(self): + + "Create the new event." + + _ = self._ + form = self.get_form() + + # If no title exists in the request, an error message is returned. + + identifier = form.get("identifier", [None])[0] + + if not identifier: + return 0, _("No identifier specified.") + + link = form.get("link", [None])[0] + + if not link: + return 0, _("No link specified.") + + return self.add_link_to_page(identifier, link) + + 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 add_link_to_page(self, identifier, link): + + """ + For the macro with the given 'identifier', add 'link' to the current + page. + """ + + _ = self._ + request = self.request + page = self.page + formatter = request.formatter + form = self.get_form() + + # Get the link details. + + title = form.get('title', [link])[0] + introduction = form.get('introduction', [""])[0] + description = form.get('description', [""])[0] + insert_before = form.get('insert_before', [""])[0] + + # Encode the link details. + # NOTE: Should support different formatting options. + + link_details = "%s[[%s%s]]%s" % ( + introduction and ('"%s" ' % formatter.escapedText(introduction)) or "", + link, + title and ('|%s' % title) or "", + description and (" - ''%s''" % description) or "" + ) + + # Open the page for editing. + + new_page = PageEditor(request, page.page_name) + + # Parse the page. + + page_body = page.get_raw_body() + + for match in macro_pattern.finditer(page_body): + macro_identifier = match.group("identifier") + leading_text = match.group("leading") + trailing_text = match.group("trailing") + start, end = match.span() + + # Where this identifier matches this macro's identifier, insert the + # link details. + + if macro_identifier == identifier: + if insert_before: + page_body = page_body[:start] + leading_text + link_details + trailing_text + "\n" + page_body[start:] + else: + page_body = page_body[:end] + "\n" + leading_text + link_details + trailing_text + page_body[end:] + + # Save the new version of the page. + + new_page.saveText(page_body, 0) + break + + # NOTE: Perhaps show a message upon failure. + + request.http_redirect(page.url(request)) + return 1, None + +# Action function. + +def execute(pagename, request): + AddLinkToPage(pagename, request).render() + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 000000000000 -r d358fd48cf81 macros/AddLinkToPage.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/macros/AddLinkToPage.py Sun Aug 15 01:52:41 2010 +0200 @@ -0,0 +1,199 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - AddLinkToPage + + Add a link using a form in the page, getting details of the linked document + and inserting them with the link itself. + + @copyright: 2010 Paul Boddie + @license: GNU GPL, see COPYING for details. +""" + +Dependencies = ["pages"] + +from MoinMoin import wikiutil +from MoinMoin.PageEditor import PageEditor +from MoinContentSupport import get_form +import urllib +import re + +title_pattern = re.compile(ur'<(?Ptitle|h\d)(\s.*?)?>(?P.*?)</(?P=tag)>', re.MULTILINE | re.UNICODE | re.DOTALL) +paragraph_pattern = re.compile(ur'<p(\s.*?)?>(?P<text>.*?)(?=<p(\s.*?)?>|</p>)', re.MULTILINE | re.UNICODE | re.DOTALL) +tag_pattern = re.compile(ur'<.*?>', re.MULTILINE | re.UNICODE | re.DOTALL) + +def get_link_info(link): + + "Get information from the given 'link'." + + # NOTE: Insist on remote URLs! + + try: + f = urllib.urlopen(link) + except IOError: + return None + + try: + s = f.read() + first_title = "" + + for title_match in title_pattern.finditer(s): + title = title_match.group("title").strip() + start, end = title_match.span() + + if not first_title: + first_title = title + + for intro_match in paragraph_pattern.finditer(s[end:]): + intro = get_flattened_content(intro_match.group("text")).strip() + if intro: + return title, intro + finally: + f.close() + + return first_title, "" + +def get_flattened_content(s): + + "Get HTML or XHTML without the tags." + + l = [] + last = 0 + for match in tag_pattern.finditer(s): + start, end = match.span() + l.append(s[last:start]) + last = end + l.append(s[last:]) + return "".join(l).replace("\n", " ") + +# Macro functions. + +def execute(macro, args): + + """ + Execute the 'macro' with the given 'args': + """ + + request = macro.request + formatter = macro.formatter + page = formatter.page + form = get_form(request) + _ = macro._ + + output = [] + + # Interpret the arguments. + + try: + parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or [] + except AttributeError: + parsed_args = args.split(",") + + parsed_args = [arg for arg in parsed_args if arg] + + # The macro's identifier should always appear first. + + identifier = parsed_args[0] + + # Look for keywords determining the action of the macro. + + insert_before = "before" in parsed_args[1:] or not ("after" in parsed_args[1:]) + + # If the request refers to this macro, another kind of form will be shown. + + active_identifier = form.get('add_link_to_page', [None])[0] + link = form.get('add_link_to_page_link', [None])[0] + + # Test for usage of this macro. + # Where this macro is not active, don't do anything. + + if identifier != active_identifier: + show_macro = 1 + + # Otherwise, show a form containing link details. + + else: + + # Acquire information from the link. + + link_info = get_link_info(link) + + # NOTE: Perhaps show a message upon success/failure. + + if link_info is None: + show_macro = 1 + + # Information was retrieved and is now shown. + + else: + title, introduction = link_info + + # Show a form containing the retrieved information suitable for the + # corresponding action. + + output.append(u'<form class="macro" method="POST" action="%s/%s">' % ( + request.getScriptname(), wikiutil.quoteWikinameURL(page.page_name))) + + output.append(u''' + <input type="hidden" name="identifier" value="%(identifier)s" /> + <input type="hidden" name="doit" value="1" /> + <input type="hidden" name="insert_before" value="%(insert_before)s" /> + <input type="hidden" name="action" value="AddLinkToPage" />''' % { + "identifier" : wikiutil.escape(identifier, 1), + "insert_before" : insert_before and "true" or "" + }) + + output.append(u''' + <table> + <tr> + <th>%(url_label)s</th> + <td><input type="text" name="link" value="%(link)s" /></td> + </tr> + <tr> + <th>%(title_label)s</th> + <td><input type="text" name="title" value="%(title)s" /></td> + </tr> + <tr> + <th>%(intro_label)s</th> + <td><input type="text" name="introduction" value="%(intro)s" /></td> + </tr> + <tr> + <th>%(description_label)s</th> + <td><input type="text" name="description" /></td> + </tr> + <tr> + <td colspan="2"><input type="submit" value="%(submit_label)s" /></td> + </tr> + </table>''' % { + "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")) + }) + + output.append(u'</form>') + + # Don't show the macro. + + show_macro = 0 + + # If the macro is to be shown, emit the usual fields. + + if show_macro: + output.append(u'<form class="macro" method="POST" action="%s/%s">' % ( + request.getScriptname(), wikiutil.quoteWikinameURL(page.page_name))) + output.append(u'<input type="hidden" name="add_link_to_page" value="%s" />' % + wikiutil.escape(identifier, 1)) + output.append(u'<div>') + output.append(u'<input type="text" name="add_link_to_page_link" />') + output.append(u'<input type="submit" value="%s" />' % + wikiutil.escape(_("Add link"))) + output.append(u'</div>') + output.append(u'</form>') + + return formatter.rawHTML('\n'.join(output)) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 000000000000 -r d358fd48cf81 setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Sun Aug 15 01:52:41 2010 +0200 @@ -0,0 +1,13 @@ +#! /usr/bin/env python + +from distutils.core import setup + +setup( + name = "MoinContentSuite", + description = "Edit MoinMoin content using forms and structured editing techniques", + author = "Paul Boddie", + author_email = "paul@boddie.org.uk", + url = "http://moinmo.in/MacroMarket/MoinContentSuite", + version = "0.1", + py_modules = ["MoinContentSupport"] + )