paul@5 | 1 | # -*- coding: iso-8859-1 -*- |
paul@5 | 2 | """ |
paul@5 | 3 | MoinMoin - SharedUpdates Action |
paul@5 | 4 | |
paul@19 | 5 | @copyright: 2012, 2013 by Paul Boddie <paul@boddie.org.uk> |
paul@5 | 6 | @license: GNU GPL (v2 or later), see COPYING.txt for details. |
paul@5 | 7 | """ |
paul@5 | 8 | |
paul@5 | 9 | from MoinMoin.action import ActionBase |
paul@19 | 10 | from MoinMoin.Page import Page |
paul@5 | 11 | from MoinMoin import wikiutil |
paul@19 | 12 | from MoinShare import getPreferredOutputTypes, getOutputTypes, getUpdatedTime |
paul@19 | 13 | from MoinSupport import escattr, formatText, get_form, getFragments, \ |
paul@19 | 14 | getMetadata, getParserClass, getPathInfo, writeHeaders, ActionSupport |
paul@19 | 15 | |
paul@19 | 16 | try: |
paul@19 | 17 | from cStringIO import StringIO |
paul@19 | 18 | except ImportError: |
paul@19 | 19 | from StringIO import StringIO |
paul@19 | 20 | |
paul@19 | 21 | escape = wikiutil.escape |
paul@5 | 22 | |
paul@5 | 23 | Dependencies = ['pages'] |
paul@5 | 24 | |
paul@5 | 25 | # Action class and supporting functions. |
paul@5 | 26 | |
paul@5 | 27 | class SharedUpdates(ActionBase, ActionSupport): |
paul@5 | 28 | |
paul@15 | 29 | "An update manifest retrieval dialogue requesting various parameters." |
paul@5 | 30 | |
paul@5 | 31 | def get_form_html(self, buttons_html): |
paul@5 | 32 | _ = self._ |
paul@5 | 33 | request = self.request |
paul@5 | 34 | form = self.get_form() |
paul@5 | 35 | page = self.page |
paul@5 | 36 | |
paul@5 | 37 | pagename = form.get("pagename", [page.page_name])[0] |
paul@5 | 38 | |
paul@5 | 39 | d = { |
paul@5 | 40 | "buttons_html" : buttons_html, |
paul@5 | 41 | "pagename_label" : escape(_("Page name for updates")), |
paul@5 | 42 | "pagename" : escattr(pagename), |
paul@5 | 43 | } |
paul@5 | 44 | |
paul@5 | 45 | return ''' |
paul@5 | 46 | <table> |
paul@5 | 47 | <tr> |
paul@5 | 48 | <td class="label"><label>%(pagename_label)s</label></td> |
paul@5 | 49 | <td class="content"> |
paul@5 | 50 | <input name="pagename" type="text" size="40" value="%(pagename)s" /> |
paul@5 | 51 | </td> |
paul@5 | 52 | </tr> |
paul@5 | 53 | <tr> |
paul@5 | 54 | <td></td> |
paul@5 | 55 | <td class="buttons"> |
paul@5 | 56 | %(buttons_html)s |
paul@5 | 57 | </td> |
paul@5 | 58 | </tr> |
paul@5 | 59 | </table> |
paul@5 | 60 | ''' % d |
paul@5 | 61 | |
paul@5 | 62 | def do_action(self): |
paul@5 | 63 | |
paul@5 | 64 | "Write the syndication resource." |
paul@5 | 65 | |
paul@5 | 66 | write_resource(self.request) |
paul@5 | 67 | return 1, None |
paul@5 | 68 | |
paul@5 | 69 | def render_success(self, msg, msgtype=None): |
paul@5 | 70 | |
paul@5 | 71 | """ |
paul@5 | 72 | Render neither 'msg' nor 'msgtype' since a resource has already been |
paul@5 | 73 | produced. |
paul@5 | 74 | NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. |
paul@5 | 75 | """ |
paul@5 | 76 | |
paul@5 | 77 | pass |
paul@5 | 78 | |
paul@5 | 79 | def write_resource(request): |
paul@5 | 80 | |
paul@5 | 81 | """ |
paul@5 | 82 | For the given 'request', write an Atom summary of updates found on the |
paul@5 | 83 | specified page. |
paul@5 | 84 | See: http://tools.ietf.org/html/rfc4287 |
paul@5 | 85 | """ |
paul@5 | 86 | |
paul@5 | 87 | form = get_form(request) |
paul@5 | 88 | |
paul@8 | 89 | pagename = form.get("pagename", [request.page.page_name])[0] |
paul@5 | 90 | |
paul@5 | 91 | page = Page(request, pagename) |
paul@5 | 92 | metadata = getMetadata(page) |
paul@5 | 93 | |
paul@9 | 94 | # Output summary data... |
paul@7 | 95 | |
paul@9 | 96 | writeHeaders(request, "application/atom+xml", metadata) |
paul@9 | 97 | updated = getUpdatedTime(metadata) |
paul@5 | 98 | |
paul@5 | 99 | # Atom output... |
paul@5 | 100 | |
paul@5 | 101 | # Using the page name and the page URL in the title, link and |
paul@5 | 102 | # subtitle. |
paul@5 | 103 | |
paul@5 | 104 | path_info = getPathInfo(request) |
paul@5 | 105 | link = "%s%s" % (request.getBaseURL(), path_info) |
paul@5 | 106 | |
paul@5 | 107 | d = { |
paul@5 | 108 | "title" : escape(path_info[1:]), |
paul@5 | 109 | "link" : escape(link), |
paul@5 | 110 | "href" : escattr(link), |
paul@5 | 111 | "updated" : escape(updated), |
paul@5 | 112 | } |
paul@5 | 113 | |
paul@19 | 114 | # Start the output. |
paul@19 | 115 | |
paul@5 | 116 | request.write('''\ |
paul@5 | 117 | <?xml version="1.0" encoding="utf-8"?>\r |
paul@5 | 118 | <feed xmlns="http://www.w3.org/2005/Atom">\r |
paul@5 | 119 | <title type="text">%(title)s</title>\r |
paul@5 | 120 | <subtitle type="text">Shared updates published on %(link)s</subtitle>\r |
paul@5 | 121 | <link rel="self" href="%(href)s"/>\r |
paul@5 | 122 | <updated>%(updated)s</updated>\r |
paul@5 | 123 | ''' % d) |
paul@5 | 124 | |
paul@5 | 125 | # Get the fragment regions for the page. |
paul@5 | 126 | |
paul@5 | 127 | for n, (format, attributes, body) in enumerate(getFragments(page.get_raw_body())): |
paul@5 | 128 | |
paul@5 | 129 | # Produce a fragment identifier. |
paul@5 | 130 | # NOTE: Choose a more robust identifier where none is explicitly given. |
paul@5 | 131 | |
paul@5 | 132 | fragment = attributes.get("fragment", str(n)) |
paul@5 | 133 | summary = attributes.get("summary", "Update #%d" % n) |
paul@5 | 134 | |
paul@5 | 135 | # Get the URL that yields only the fragment. |
paul@5 | 136 | |
paul@9 | 137 | fragment_link = "%s?action=SharedUpdate&fragment=%s" % (link, fragment) |
paul@5 | 138 | |
paul@9 | 139 | # Get the preferred content types available for the fragment. |
paul@5 | 140 | |
paul@9 | 141 | preferred = getPreferredOutputTypes(request, getOutputTypes(request, format)) |
paul@6 | 142 | |
paul@6 | 143 | download_links = [] |
paul@6 | 144 | |
paul@7 | 145 | for mimetype in preferred: |
paul@9 | 146 | specific_link = "%s&type=%s&doit=1" % (fragment_link, mimetype) |
paul@5 | 147 | |
paul@6 | 148 | download_links.append('<link rel="alternate" type="%s" href="%s"/>' % ( |
paul@5 | 149 | escattr(mimetype), escattr(specific_link))) |
paul@5 | 150 | |
paul@19 | 151 | # Try and obtain some suitable content for the entry. |
paul@5 | 152 | # NOTE: Could potentially get a summary for the fragment. |
paul@19 | 153 | |
paul@19 | 154 | content = None |
paul@19 | 155 | |
paul@19 | 156 | if "text/html" in preferred: |
paul@19 | 157 | parser_cls = getParserClass(request, format) |
paul@19 | 158 | parser = parser_cls(body, request) |
paul@19 | 159 | |
paul@19 | 160 | if format == "html": |
paul@19 | 161 | content = body |
paul@19 | 162 | elif hasattr(parser, "formatForOutputType"): |
paul@19 | 163 | s = StringIO() |
paul@19 | 164 | parser.formatForOutputType("text/html", write=s.write) |
paul@19 | 165 | content = s.getvalue() |
paul@19 | 166 | else: |
paul@19 | 167 | fmt = request.html_formatter |
paul@19 | 168 | fmt.setPage(page) |
paul@19 | 169 | content = formatText(body, request, fmt, parser_cls) |
paul@19 | 170 | |
paul@5 | 171 | # NOTE: The published and updated details would need to be deduced from |
paul@21 | 172 | # NOTE: the page history instead of being taken for the page as a whole. |
paul@5 | 173 | |
paul@6 | 174 | d = { |
paul@6 | 175 | "title" : escape(summary), |
paul@6 | 176 | "fragment_link" : escape(fragment_link), |
paul@6 | 177 | "download_links" : "\r\n".join(download_links), |
paul@19 | 178 | "content" : content and ('<content type="html">%s</content>' % escape(content)) or "", |
paul@21 | 179 | "updated" : escape(updated), |
paul@6 | 180 | } |
paul@6 | 181 | |
paul@19 | 182 | # Write the entry output. |
paul@19 | 183 | |
paul@6 | 184 | request.write('''\ |
paul@6 | 185 | <entry>\r |
paul@6 | 186 | <title>%(title)s</title>\r |
paul@6 | 187 | <id>%(fragment_link)s</id>\r |
paul@21 | 188 | <updated>%(updated)s</updated>\r |
paul@6 | 189 | %(download_links)s\r |
paul@19 | 190 | %(content)s\r |
paul@6 | 191 | </entry>\r |
paul@6 | 192 | ''' % d) |
paul@5 | 193 | |
paul@19 | 194 | # End the feed output. |
paul@19 | 195 | |
paul@5 | 196 | request.write('</feed>\r\n') |
paul@5 | 197 | |
paul@5 | 198 | # Action function. |
paul@5 | 199 | |
paul@5 | 200 | def execute(pagename, request): |
paul@5 | 201 | SharedUpdates(pagename, request).render() |
paul@5 | 202 | |
paul@5 | 203 | # vim: tabstop=4 expandtab shiftwidth=4 |