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 attempt to get the last edit time of the fragment. 103 104 page = Page(request, pagename) 105 metadata = getMetadata(page) 106 107 # NOTE: Consider using request.httpDate. 108 109 if metadata.has_key("last-modified"): 110 latest_timestamp = metadata["last-modified"] 111 headers.append("Last-Modified: %s" % latest_timestamp.as_HTTP_datetime_string()) 112 updated = latest_timestamp.as_ISO8601_datetime_string() 113 else: 114 updated = getCurrentTime().as_ISO8601_datetime_string() 115 116 send_headers(headers) 117 118 # Atom output... 119 120 # Using the page name and the page URL in the title, link and 121 # subtitle. 122 123 path_info = getPathInfo(request) 124 link = "%s%s" % (request.getBaseURL(), path_info) 125 126 d = { 127 "title" : escape(path_info[1:]), 128 "link" : escape(link), 129 "href" : escattr(link), 130 "updated" : escape(updated), 131 } 132 133 request.write('''\ 134 <?xml version="1.0" encoding="utf-8"?>\r 135 <feed xmlns="http://www.w3.org/2005/Atom">\r 136 <title type="text">%(title)s</title>\r 137 <subtitle type="text">Shared updates published on %(link)s</subtitle>\r 138 <link rel="self" href="%(href)s"/>\r 139 <updated>%(updated)s</updated>\r 140 ''' % d) 141 142 # Get the fragment regions for the page. 143 144 for n, (format, attributes, body) in enumerate(getFragments(page.get_raw_body())): 145 146 # Produce a fragment identifier. 147 # NOTE: Choose a more robust identifier where none is explicitly given. 148 149 fragment = attributes.get("fragment", str(n)) 150 summary = attributes.get("summary", "Update #%d" % n) 151 152 # Get the URL that yields only the fragment. 153 154 fragment_link = "%s?action=ShowUpdate&fragment=%s" % (link, fragment) 155 156 # Get the types available for the fragment. 157 # This uses an extended parser API method if available. 158 159 parser = getParserClass(request, format) 160 if hasattr(parser, "getOutputTypes"): 161 mimetypes = parser.getOutputTypes() 162 else: 163 mimetypes = ["text/html"] 164 165 # Perform content negotiation, obtaining mimetypes common to the 166 # fragment and the client. 167 168 accept = getHeader(request, "Accept", "HTTP") 169 prefs = getContentPreferences(accept) 170 preferred = prefs.get_preferred_types(mimetypes) 171 172 download_links = [] 173 174 for mimetype in preferred: 175 specific_link = "%s&mimetype=%s" % (fragment_link, mimetype) 176 177 download_links.append('<link rel="alternate" type="%s" href="%s"/>' % ( 178 escattr(mimetype), escattr(specific_link))) 179 180 # NOTE: Could potentially get a summary for the fragment. 181 # NOTE: The published and updated details would need to be deduced from 182 # NOTE: the page history. 183 184 d = { 185 "title" : escape(summary), 186 "fragment_link" : escape(fragment_link), 187 "download_links" : "\r\n".join(download_links), 188 } 189 190 request.write('''\ 191 <entry>\r 192 <title>%(title)s</title>\r 193 <id>%(fragment_link)s</id>\r 194 %(download_links)s\r 195 </entry>\r 196 ''' % d) 197 198 request.write('</feed>\r\n') 199 200 # Action function. 201 202 def execute(pagename, request): 203 SharedUpdates(pagename, request).render() 204 205 # vim: tabstop=4 expandtab shiftwidth=4