1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - MoinShare library 4 5 @copyright: 2011, 2012, 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from ContentTypeSupport import getContentPreferences 10 from DateSupport import getCurrentTime, getDateTimeFromRFC2822 11 from MoinSupport import * 12 from MoinMoin import wikiutil 13 from email.parser import Parser 14 import re 15 16 try: 17 from cStringIO import StringIO 18 except ImportError: 19 from StringIO import StringIO 20 21 escape = wikiutil.escape 22 _getFragments = getFragments 23 24 __version__ = "0.1" 25 26 # More Moin 1.9 compatibility functions. 27 28 def has_member(request, groupname, username): 29 if hasattr(request.dicts, "has_member"): 30 return request.dicts.has_member(groupname, username) 31 else: 32 return username in request.dicts.get(groupname, []) 33 34 # Fragments employ a "moinshare" attribute. 35 36 fragment_attribute = "moinshare" 37 38 def getFragments(s): 39 40 "Return all fragments in 's' having the MoinShare fragment attribute." 41 42 fragments = [] 43 for format, attributes, body in _getFragments(s): 44 if attributes.has_key(fragment_attribute): 45 fragments.append((format, attributes, body)) 46 return fragments 47 48 def getOutputTypes(request, format): 49 50 """ 51 Using the 'request' and the 'format' of a fragment, return the media types 52 available for the fragment. 53 """ 54 55 # This uses an extended parser API method if available. 56 57 parser = getParserClass(request, format) 58 if hasattr(parser, "getOutputTypes"): 59 return parser.getOutputTypes() 60 else: 61 return ["text/html"] 62 63 def getPreferredOutputTypes(request, mimetypes): 64 65 """ 66 Using the 'request', perform content negotiation, obtaining mimetypes common 67 to the fragment (given by 'mimetypes') and the client (found in the Accept 68 header). 69 """ 70 71 accept = getHeader(request, "Accept", "HTTP") 72 if accept: 73 prefs = getContentPreferences(accept) 74 return prefs.get_preferred_types(mimetypes) 75 else: 76 return mimetypes 77 78 def getUpdatedTime(metadata): 79 80 """ 81 Return the last updated time based on the given 'metadata', using the 82 current time if no explicit last modified time is specified. 83 """ 84 85 # NOTE: We could attempt to get the last edit time of a fragment. 86 87 latest_timestamp = metadata.get("last-modified") 88 if latest_timestamp: 89 return latest_timestamp 90 else: 91 return getCurrentTime() 92 93 def getUpdateSources(request, sources_page): 94 95 """ 96 Using the 'request', return the update sources defined on the given 97 'sources_page'. 98 """ 99 100 # Remote sources are accessed via dictionary page definitions. 101 102 return getWikiDict(sources_page, request) 103 104 # Entry/update classes. 105 106 class Update: 107 108 "A feed update entry." 109 110 def __init__(self): 111 self.title = None 112 self.link = None 113 self.content = None 114 self.content_type = None 115 self.updated = None 116 117 # Page-related attributes. 118 119 self.fragment = None 120 self.preferred = None 121 122 # Message-related attributes. 123 124 self.parts = None 125 126 def __cmp__(self, other): 127 if self.updated is None and other.updated is not None: 128 return 1 129 elif self.updated is not None and other.updated is None: 130 return -1 131 else: 132 return cmp(self.updated, other.updated) 133 134 # Update retrieval from pages. 135 136 def getUpdatesFromPage(page, request): 137 138 """ 139 Get updates from the given 'page' using the 'request'. A list of update 140 objects is returned. 141 """ 142 143 updates = [] 144 145 # NOTE: Use the updated datetime from the page for updates. 146 # NOTE: The published and updated details would need to be deduced from 147 # NOTE: the page history instead of being taken from the page as a whole. 148 149 metadata = getMetadata(page) 150 updated = getUpdatedTime(metadata) 151 152 # Get the fragment regions for the page. 153 154 for n, (format, attributes, body) in enumerate(getFragments(page.get_raw_body())): 155 156 update = Update() 157 158 # Produce a fragment identifier. 159 # NOTE: Choose a more robust identifier where none is explicitly given. 160 161 update.fragment = attributes.get("fragment", str(n)) 162 update.title = attributes.get("summary", "Update #%d" % n) 163 164 # Get the preferred content types available for the fragment. 165 166 update.preferred = getPreferredOutputTypes(request, getOutputTypes(request, format)) 167 168 # Try and obtain some suitable content for the entry. 169 # NOTE: Could potentially get a summary for the fragment. 170 171 update.content = None 172 173 if "text/html" in update.preferred: 174 parser_cls = getParserClass(request, format) 175 parser = parser_cls(body, request) 176 177 if format == "html": 178 update.content = body 179 elif hasattr(parser, "formatForOutputType"): 180 s = StringIO() 181 parser.formatForOutputType("text/html", write=s.write) 182 update.content = s.getvalue() 183 else: 184 fmt = request.html_formatter 185 fmt.setPage(page) 186 update.content = formatText(body, request, fmt, parser_cls) 187 188 update.content_type = "text/html" 189 190 update.link = page.url(request) 191 update.updated = updated 192 193 updates.append(update) 194 195 return updates 196 197 # Update retrieval from message stores. 198 199 def getUpdatesFromStore(page, request): 200 201 """ 202 Get updates from the message store associated with the given 'page' using 203 the 'request'. A list of update objects is returned. 204 """ 205 206 updates = [] 207 208 metadata = getMetadata(page) 209 updated = getUpdatedTime(metadata) 210 211 store = ItemStore(page, "messages", "message-locks") 212 213 for n, message_text in enumerate(iter(store)): 214 215 update = Update() 216 message = Parser().parse(StringIO(message_text)) 217 218 # Produce a fragment identifier. 219 220 update.fragment = update.updated = getDateTimeFromRFC2822(message.get("date")) 221 update.title = message.get("subject", "Update #%d" % n) 222 223 # Determine whether the message has several representations. 224 225 if not message.is_multipart(): 226 update.content = message.get_payload() 227 update.content_type = message.get_content_type() 228 else: 229 update.parts = message.get_payload() 230 231 updates.append(update) 232 233 return updates 234 235 # Source management. 236 237 def getUpdateSources(pagename, request): 238 239 "Return the update sources from the given 'pagename' using the 'request'." 240 241 sources = {} 242 243 source_definitions = getWikiDict(pagename, request) 244 245 if source_definitions: 246 for name, value in source_definitions.items(): 247 sources[name] = getSourceParameters(value) 248 249 return sources 250 251 def getSourceParameters(source_definition): 252 253 "Return the parameters from the given 'source_definition' string." 254 255 parameters = {} 256 unqualified = ("type", "location") 257 258 for arg in source_definition.split(): 259 try: 260 argname, argvalue = arg.split("=", 1) 261 262 # Detect unlikely parameter names. 263 264 if not argname.isalpha(): 265 raise ValueError 266 267 parameters[argname] = argvalue 268 269 # Unqualified parameters are assumed to be one of a recognised set. 270 271 except ValueError: 272 for argname in unqualified: 273 if not parameters.has_key(argname): 274 parameters[argname] = arg 275 break 276 277 return parameters 278 279 # vim: tabstop=4 expandtab shiftwidth=4