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