# HG changeset patch # User Paul Boddie # Date 1342380804 -7200 # Node ID 938ae4097683f432d02b3ed0bba5b964cb4b50ae # Parent d8163362c0d9159cafdfe10f6ba709cf2e4e5ae9 Added a SharedUpdate action that attempts to provide appropriate fragment content. Added the "doit" parameter to SharedUpdates summary URLs. Moved functionality into the MoinShare module as utility functions. diff -r d8163362c0d9 -r 938ae4097683 MoinShare.py --- a/MoinShare.py Wed Jul 11 00:52:41 2012 +0200 +++ b/MoinShare.py Sun Jul 15 21:33:24 2012 +0200 @@ -7,7 +7,8 @@ """ from MoinSupport import * -from MoinMoin import wikiutil +from MoinMoin import config, wikiutil +from DateSupport import getCurrentTime import re escape = wikiutil.escape @@ -125,4 +126,72 @@ return getFragmentsFromRegions(getRegions(s)) +def getOutputTypes(request, format): + + """ + Using the 'request' and the 'format' of a fragment, return the media types + available for the fragment. + """ + + # This uses an extended parser API method if available. + + parser = getParserClass(request, format) + if hasattr(parser, "getOutputTypes"): + return parser.getOutputTypes() + else: + return ["text/html"] + +def getPreferredOutputTypes(request, mimetypes): + + """ + Using the 'request', perform content negotiation, obtaining mimetypes common + to the fragment (given by 'mimetypes') and the client (found in the Accept + header). + """ + + accept = getHeader(request, "Accept", "HTTP") + prefs = getContentPreferences(accept) + return prefs.get_preferred_types(mimetypes) + +def writeHeaders(request, mimetype, metadata, status=None): + + """ + Using the 'request', write resource headers using the given 'mimetype', + based on the given 'metadata'. If the optional 'status' is specified, set + the status header to the given value. + """ + + send_headers = get_send_headers(request) + + # Define headers. + + headers = ["Content-Type: %s; charset=%s" % (mimetype, config.charset)] + + # Define the last modified time. + # NOTE: Consider using request.httpDate. + + latest_timestamp = metadata.get("last-modified") + if latest_timestamp: + headers.append("Last-Modified: %s" % latest_timestamp.as_HTTP_datetime_string()) + + if status: + headers.append("Status: %s" % status) + + send_headers(headers) + +def getUpdatedTime(metadata): + + """ + Return the last updated time based on the given 'metadata', using the + current time if no explicit last modified time is specified. + """ + + # NOTE: We could attempt to get the last edit time of a fragment. + + latest_timestamp = metadata.get("last-modified") + if latest_timestamp: + return latest_timestamp.as_ISO8601_datetime_string() + else: + return getCurrentTime().as_ISO8601_datetime_string() + # vim: tabstop=4 expandtab shiftwidth=4 diff -r d8163362c0d9 -r 938ae4097683 actions/SharedUpdate.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/actions/SharedUpdate.py Sun Jul 15 21:33:24 2012 +0200 @@ -0,0 +1,176 @@ +# -*- coding: iso-8859-1 -*- +""" + MoinMoin - SharedUpdate Action + + @copyright: 2012 by Paul Boddie + @license: GNU GPL (v2 or later), see COPYING.txt for details. +""" + +from MoinMoin.action import ActionBase +from MoinMoin import wikiutil +from MoinShare import * + +Dependencies = ['pages'] + +# Action class and supporting functions. + +class SharedUpdate(ActionBase, ActionSupport): + + "A summary dialogue requesting various parameters." + + def get_form_html(self, buttons_html): + _ = self._ + request = self.request + form = self.get_form() + page = self.page + + pagename = form.get("pagename", [page.page_name])[0] + fragment = form.get("fragment", [""])[0] + mimetype = form.get("type", [""])[0] + + d = { + "buttons_html" : buttons_html, + "pagename_label" : escape(_("Page name for updates")), + "pagename" : escattr(pagename), + "fragment_label" : escape(_("Fragment to download")), + "fragment" : escattr(fragment), + "mimetype_label" : escape(_("Content/media type")), + "mimetype" : escattr(mimetype), + } + + return ''' + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ %(buttons_html)s +
+''' % d + + def do_action(self): + + "Write the syndication resource." + + _ = self._ + form = self.get_form() + + # If no fragment exists in the request, an error message is returned. + + fragment = form.get("fragment") + + if not fragment: + return 0, _("No fragment to download specified.") + + write_resource(self.request) + return 1, None + + def render_success(self, msg, msgtype=None): + + """ + Render neither 'msg' nor 'msgtype' since a resource has already been + produced. + NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. + """ + + pass + +def write_resource(request): + + """ + For the given 'request', write the requested fragment with the specified + content/media type (or use content negotiation to choose a media type). + """ + + form = get_form(request) + + pagename = form.get("pagename", [request.page.page_name])[0] + fragment = form.get("fragment", [None])[0] + mimetype = form.get("type", [None])[0] + + page = Page(request, pagename) + metadata = getMetadata(page) + + # Set the default error status. + + status = "404 Not Found" + + # Get the fragment regions for the page, trying to find the requested + # fragment. + + for n, (format, attributes, body) in enumerate(getFragments(page.get_raw_body())): + + # Produce a fragment identifier. + # NOTE: Choose a more robust identifier where none is explicitly given. + + region_fragment = attributes.get("fragment", str(n)) + + # Check the region's fragment identifier. + + if fragment != region_fragment: + continue + + # Check the mimetype availability. + + mimetypes = getOutputTypes(request, format) + + # Perform content negotiation if no mimetype was specified. + + if mimetype is None: + mimetype = getPreferredOutputTypes(request, mimetypes)[0] + + # Where no suitable mimetype is found, break out of the loop and return + # an error. + + elif mimetype not in mimetypes: + status = "406 Not Acceptable" + break + + # Define headers. + + writeHeaders(request, mimetype, metadata) + updated = getUpdatedTime(metadata) + + # Fragment output... + + parser_cls = getParserClass(request, format) + parser = parser_cls(body, request) + + if hasattr(parser, "formatForOutputType"): + parser.formatForOutputType(mimetype) + else: + fmt = request.html_formatter + parser.format(fmt) + + return + + # Error output. + + writeHeaders(request, "text/plain", metadata, status) + request.write("Sorry, but the specified fragment %s could not be provided as type %s." % (fragment, mimetype)) + +# Action function. + +def execute(pagename, request): + SharedUpdate(pagename, request).render() + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r d8163362c0d9 -r 938ae4097683 actions/SharedUpdates.py --- a/actions/SharedUpdates.py Wed Jul 11 00:52:41 2012 +0200 +++ b/actions/SharedUpdates.py Sun Jul 15 21:33:24 2012 +0200 @@ -7,10 +7,8 @@ """ from MoinMoin.action import ActionBase -from MoinMoin import config from MoinMoin import wikiutil from MoinShare import * -from DateSupport import getCurrentTime Dependencies = ['pages'] @@ -80,30 +78,13 @@ pagename = form.get("pagename", [request.page.page_name])[0] - # Output summary data... - - send_headers = get_send_headers(request) - - # Define headers. - - headers = ["Content-Type: application/atom+xml; charset=%s" % config.charset] - - # Define the last modified time. - # NOTE: We could attempt to get the last edit time of the fragment. - page = Page(request, pagename) metadata = getMetadata(page) - # NOTE: Consider using request.httpDate. + # Output summary data... - if metadata.has_key("last-modified"): - latest_timestamp = metadata["last-modified"] - headers.append("Last-Modified: %s" % latest_timestamp.as_HTTP_datetime_string()) - updated = latest_timestamp.as_ISO8601_datetime_string() - else: - updated = getCurrentTime().as_ISO8601_datetime_string() - - send_headers(headers) + writeHeaders(request, "application/atom+xml", metadata) + updated = getUpdatedTime(metadata) # Atom output... @@ -141,28 +122,16 @@ # Get the URL that yields only the fragment. - fragment_link = "%s?action=ShowUpdate&fragment=%s" % (link, fragment) - - # Get the types available for the fragment. - # This uses an extended parser API method if available. + fragment_link = "%s?action=SharedUpdate&fragment=%s" % (link, fragment) - parser = getParserClass(request, format) - if hasattr(parser, "getOutputTypes"): - mimetypes = parser.getOutputTypes() - else: - mimetypes = ["text/html"] + # Get the preferred content types available for the fragment. - # Perform content negotiation, obtaining mimetypes common to the - # fragment and the client. - - accept = getHeader(request, "Accept", "HTTP") - prefs = getContentPreferences(accept) - preferred = prefs.get_preferred_types(mimetypes) + preferred = getPreferredOutputTypes(request, getOutputTypes(request, format)) download_links = [] for mimetype in preferred: - specific_link = "%s&mimetype=%s" % (fragment_link, mimetype) + specific_link = "%s&type=%s&doit=1" % (fragment_link, mimetype) download_links.append('' % ( escattr(mimetype), escattr(specific_link)))