1.1 --- a/MoinShare.py Wed Jul 11 00:52:41 2012 +0200
1.2 +++ b/MoinShare.py Sun Jul 15 21:33:24 2012 +0200
1.3 @@ -7,7 +7,8 @@
1.4 """
1.5
1.6 from MoinSupport import *
1.7 -from MoinMoin import wikiutil
1.8 +from MoinMoin import config, wikiutil
1.9 +from DateSupport import getCurrentTime
1.10 import re
1.11
1.12 escape = wikiutil.escape
1.13 @@ -125,4 +126,72 @@
1.14
1.15 return getFragmentsFromRegions(getRegions(s))
1.16
1.17 +def getOutputTypes(request, format):
1.18 +
1.19 + """
1.20 + Using the 'request' and the 'format' of a fragment, return the media types
1.21 + available for the fragment.
1.22 + """
1.23 +
1.24 + # This uses an extended parser API method if available.
1.25 +
1.26 + parser = getParserClass(request, format)
1.27 + if hasattr(parser, "getOutputTypes"):
1.28 + return parser.getOutputTypes()
1.29 + else:
1.30 + return ["text/html"]
1.31 +
1.32 +def getPreferredOutputTypes(request, mimetypes):
1.33 +
1.34 + """
1.35 + Using the 'request', perform content negotiation, obtaining mimetypes common
1.36 + to the fragment (given by 'mimetypes') and the client (found in the Accept
1.37 + header).
1.38 + """
1.39 +
1.40 + accept = getHeader(request, "Accept", "HTTP")
1.41 + prefs = getContentPreferences(accept)
1.42 + return prefs.get_preferred_types(mimetypes)
1.43 +
1.44 +def writeHeaders(request, mimetype, metadata, status=None):
1.45 +
1.46 + """
1.47 + Using the 'request', write resource headers using the given 'mimetype',
1.48 + based on the given 'metadata'. If the optional 'status' is specified, set
1.49 + the status header to the given value.
1.50 + """
1.51 +
1.52 + send_headers = get_send_headers(request)
1.53 +
1.54 + # Define headers.
1.55 +
1.56 + headers = ["Content-Type: %s; charset=%s" % (mimetype, config.charset)]
1.57 +
1.58 + # Define the last modified time.
1.59 + # NOTE: Consider using request.httpDate.
1.60 +
1.61 + latest_timestamp = metadata.get("last-modified")
1.62 + if latest_timestamp:
1.63 + headers.append("Last-Modified: %s" % latest_timestamp.as_HTTP_datetime_string())
1.64 +
1.65 + if status:
1.66 + headers.append("Status: %s" % status)
1.67 +
1.68 + send_headers(headers)
1.69 +
1.70 +def getUpdatedTime(metadata):
1.71 +
1.72 + """
1.73 + Return the last updated time based on the given 'metadata', using the
1.74 + current time if no explicit last modified time is specified.
1.75 + """
1.76 +
1.77 + # NOTE: We could attempt to get the last edit time of a fragment.
1.78 +
1.79 + latest_timestamp = metadata.get("last-modified")
1.80 + if latest_timestamp:
1.81 + return latest_timestamp.as_ISO8601_datetime_string()
1.82 + else:
1.83 + return getCurrentTime().as_ISO8601_datetime_string()
1.84 +
1.85 # vim: tabstop=4 expandtab shiftwidth=4
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/actions/SharedUpdate.py Sun Jul 15 21:33:24 2012 +0200
2.3 @@ -0,0 +1,176 @@
2.4 +# -*- coding: iso-8859-1 -*-
2.5 +"""
2.6 + MoinMoin - SharedUpdate Action
2.7 +
2.8 + @copyright: 2012 by Paul Boddie <paul@boddie.org.uk>
2.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
2.10 +"""
2.11 +
2.12 +from MoinMoin.action import ActionBase
2.13 +from MoinMoin import wikiutil
2.14 +from MoinShare import *
2.15 +
2.16 +Dependencies = ['pages']
2.17 +
2.18 +# Action class and supporting functions.
2.19 +
2.20 +class SharedUpdate(ActionBase, ActionSupport):
2.21 +
2.22 + "A summary dialogue requesting various parameters."
2.23 +
2.24 + def get_form_html(self, buttons_html):
2.25 + _ = self._
2.26 + request = self.request
2.27 + form = self.get_form()
2.28 + page = self.page
2.29 +
2.30 + pagename = form.get("pagename", [page.page_name])[0]
2.31 + fragment = form.get("fragment", [""])[0]
2.32 + mimetype = form.get("type", [""])[0]
2.33 +
2.34 + d = {
2.35 + "buttons_html" : buttons_html,
2.36 + "pagename_label" : escape(_("Page name for updates")),
2.37 + "pagename" : escattr(pagename),
2.38 + "fragment_label" : escape(_("Fragment to download")),
2.39 + "fragment" : escattr(fragment),
2.40 + "mimetype_label" : escape(_("Content/media type")),
2.41 + "mimetype" : escattr(mimetype),
2.42 + }
2.43 +
2.44 + return '''
2.45 +<table>
2.46 + <tr>
2.47 + <td class="label"><label>%(pagename_label)s</label></td>
2.48 + <td class="content">
2.49 + <input name="pagename" type="text" size="40" value="%(pagename)s" />
2.50 + </td>
2.51 + </tr>
2.52 + <tr>
2.53 + <td class="label"><label>%(fragment_label)s</label></td>
2.54 + <td class="content">
2.55 + <input name="fragment" type="text" size="40" value="%(fragment)s" />
2.56 + </td>
2.57 + </tr>
2.58 + <tr>
2.59 + <td class="label"><label>%(mimetype_label)s</label></td>
2.60 + <td class="content">
2.61 + <input name="type" type="text" size="40" value="%(mimetype)s" />
2.62 + </td>
2.63 + </tr>
2.64 + <tr>
2.65 + <td></td>
2.66 + <td class="buttons">
2.67 + %(buttons_html)s
2.68 + </td>
2.69 + </tr>
2.70 +</table>
2.71 +''' % d
2.72 +
2.73 + def do_action(self):
2.74 +
2.75 + "Write the syndication resource."
2.76 +
2.77 + _ = self._
2.78 + form = self.get_form()
2.79 +
2.80 + # If no fragment exists in the request, an error message is returned.
2.81 +
2.82 + fragment = form.get("fragment")
2.83 +
2.84 + if not fragment:
2.85 + return 0, _("No fragment to download specified.")
2.86 +
2.87 + write_resource(self.request)
2.88 + return 1, None
2.89 +
2.90 + def render_success(self, msg, msgtype=None):
2.91 +
2.92 + """
2.93 + Render neither 'msg' nor 'msgtype' since a resource has already been
2.94 + produced.
2.95 + NOTE: msgtype is optional because MoinMoin 1.5.x does not support it.
2.96 + """
2.97 +
2.98 + pass
2.99 +
2.100 +def write_resource(request):
2.101 +
2.102 + """
2.103 + For the given 'request', write the requested fragment with the specified
2.104 + content/media type (or use content negotiation to choose a media type).
2.105 + """
2.106 +
2.107 + form = get_form(request)
2.108 +
2.109 + pagename = form.get("pagename", [request.page.page_name])[0]
2.110 + fragment = form.get("fragment", [None])[0]
2.111 + mimetype = form.get("type", [None])[0]
2.112 +
2.113 + page = Page(request, pagename)
2.114 + metadata = getMetadata(page)
2.115 +
2.116 + # Set the default error status.
2.117 +
2.118 + status = "404 Not Found"
2.119 +
2.120 + # Get the fragment regions for the page, trying to find the requested
2.121 + # fragment.
2.122 +
2.123 + for n, (format, attributes, body) in enumerate(getFragments(page.get_raw_body())):
2.124 +
2.125 + # Produce a fragment identifier.
2.126 + # NOTE: Choose a more robust identifier where none is explicitly given.
2.127 +
2.128 + region_fragment = attributes.get("fragment", str(n))
2.129 +
2.130 + # Check the region's fragment identifier.
2.131 +
2.132 + if fragment != region_fragment:
2.133 + continue
2.134 +
2.135 + # Check the mimetype availability.
2.136 +
2.137 + mimetypes = getOutputTypes(request, format)
2.138 +
2.139 + # Perform content negotiation if no mimetype was specified.
2.140 +
2.141 + if mimetype is None:
2.142 + mimetype = getPreferredOutputTypes(request, mimetypes)[0]
2.143 +
2.144 + # Where no suitable mimetype is found, break out of the loop and return
2.145 + # an error.
2.146 +
2.147 + elif mimetype not in mimetypes:
2.148 + status = "406 Not Acceptable"
2.149 + break
2.150 +
2.151 + # Define headers.
2.152 +
2.153 + writeHeaders(request, mimetype, metadata)
2.154 + updated = getUpdatedTime(metadata)
2.155 +
2.156 + # Fragment output...
2.157 +
2.158 + parser_cls = getParserClass(request, format)
2.159 + parser = parser_cls(body, request)
2.160 +
2.161 + if hasattr(parser, "formatForOutputType"):
2.162 + parser.formatForOutputType(mimetype)
2.163 + else:
2.164 + fmt = request.html_formatter
2.165 + parser.format(fmt)
2.166 +
2.167 + return
2.168 +
2.169 + # Error output.
2.170 +
2.171 + writeHeaders(request, "text/plain", metadata, status)
2.172 + request.write("Sorry, but the specified fragment %s could not be provided as type %s." % (fragment, mimetype))
2.173 +
2.174 +# Action function.
2.175 +
2.176 +def execute(pagename, request):
2.177 + SharedUpdate(pagename, request).render()
2.178 +
2.179 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/actions/SharedUpdates.py Wed Jul 11 00:52:41 2012 +0200
3.2 +++ b/actions/SharedUpdates.py Sun Jul 15 21:33:24 2012 +0200
3.3 @@ -7,10 +7,8 @@
3.4 """
3.5
3.6 from MoinMoin.action import ActionBase
3.7 -from MoinMoin import config
3.8 from MoinMoin import wikiutil
3.9 from MoinShare import *
3.10 -from DateSupport import getCurrentTime
3.11
3.12 Dependencies = ['pages']
3.13
3.14 @@ -80,30 +78,13 @@
3.15
3.16 pagename = form.get("pagename", [request.page.page_name])[0]
3.17
3.18 - # Output summary data...
3.19 -
3.20 - send_headers = get_send_headers(request)
3.21 -
3.22 - # Define headers.
3.23 -
3.24 - headers = ["Content-Type: application/atom+xml; charset=%s" % config.charset]
3.25 -
3.26 - # Define the last modified time.
3.27 - # NOTE: We could attempt to get the last edit time of the fragment.
3.28 -
3.29 page = Page(request, pagename)
3.30 metadata = getMetadata(page)
3.31
3.32 - # NOTE: Consider using request.httpDate.
3.33 + # Output summary data...
3.34
3.35 - if metadata.has_key("last-modified"):
3.36 - latest_timestamp = metadata["last-modified"]
3.37 - headers.append("Last-Modified: %s" % latest_timestamp.as_HTTP_datetime_string())
3.38 - updated = latest_timestamp.as_ISO8601_datetime_string()
3.39 - else:
3.40 - updated = getCurrentTime().as_ISO8601_datetime_string()
3.41 -
3.42 - send_headers(headers)
3.43 + writeHeaders(request, "application/atom+xml", metadata)
3.44 + updated = getUpdatedTime(metadata)
3.45
3.46 # Atom output...
3.47
3.48 @@ -141,28 +122,16 @@
3.49
3.50 # Get the URL that yields only the fragment.
3.51
3.52 - fragment_link = "%s?action=ShowUpdate&fragment=%s" % (link, fragment)
3.53 -
3.54 - # Get the types available for the fragment.
3.55 - # This uses an extended parser API method if available.
3.56 + fragment_link = "%s?action=SharedUpdate&fragment=%s" % (link, fragment)
3.57
3.58 - parser = getParserClass(request, format)
3.59 - if hasattr(parser, "getOutputTypes"):
3.60 - mimetypes = parser.getOutputTypes()
3.61 - else:
3.62 - mimetypes = ["text/html"]
3.63 + # Get the preferred content types available for the fragment.
3.64
3.65 - # Perform content negotiation, obtaining mimetypes common to the
3.66 - # fragment and the client.
3.67 -
3.68 - accept = getHeader(request, "Accept", "HTTP")
3.69 - prefs = getContentPreferences(accept)
3.70 - preferred = prefs.get_preferred_types(mimetypes)
3.71 + preferred = getPreferredOutputTypes(request, getOutputTypes(request, format))
3.72
3.73 download_links = []
3.74
3.75 for mimetype in preferred:
3.76 - specific_link = "%s&mimetype=%s" % (fragment_link, mimetype)
3.77 + specific_link = "%s&type=%s&doit=1" % (fragment_link, mimetype)
3.78
3.79 download_links.append('<link rel="alternate" type="%s" href="%s"/>' % (
3.80 escattr(mimetype), escattr(specific_link)))