1.1 --- a/macros/SharedContent.py Sun Jan 26 00:42:49 2014 +0100
1.2 +++ b/macros/SharedContent.py Mon Mar 31 23:54:42 2014 +0200
1.3 @@ -2,210 +2,19 @@
1.4 """
1.5 MoinMoin - SharedContent macro, based on the FeedReader macro
1.6
1.7 - @copyright: 2008, 2012, 2013 by Paul Boddie <paul@boddie.org.uk>
1.8 + @copyright: 2008, 2012, 2013, 2014 by Paul Boddie <paul@boddie.org.uk>
1.9 @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.10 """
1.11
1.12 -from DateSupport import getDateTimeFromISO8601, DateTime
1.13 from MoinMoin.Page import Page
1.14 -from MoinRemoteSupport import *
1.15 from MoinSupport import parseMacroArguments
1.16 -from MoinShare import getUpdateSources, getUpdatesFromPage, \
1.17 - getUpdatesFromStore, formatUpdate, \
1.18 - Update
1.19 -from email.utils import parsedate
1.20 -import xml.dom.pulldom
1.21 -
1.22 -try:
1.23 - from cStringIO import StringIO
1.24 -except ImportError:
1.25 - from StringIO import StringIO
1.26 +from MoinShare import getUpdateSources, getUpdates, \
1.27 + getUpdatesFromPage, getUpdatesFromStore, \
1.28 + formatUpdate, Update
1.29
1.30 Dependencies = ["time"]
1.31
1.32 MAX_ENTRIES = 5
1.33 -ATOM_NS = "http://www.w3.org/2005/Atom"
1.34 -
1.35 -# Utility functions.
1.36 -
1.37 -def text(element):
1.38 - nodes = []
1.39 - for node in element.childNodes:
1.40 - if node.nodeType == node.TEXT_NODE:
1.41 - nodes.append(node.nodeValue)
1.42 - return "".join(nodes)
1.43 -
1.44 -def children(element):
1.45 - nodes = []
1.46 - for node in element.childNodes:
1.47 - nodes.append(node.toxml())
1.48 - return "".join(nodes)
1.49 -
1.50 -def unescape(text):
1.51 - return text.replace("<", "<").replace(">", ">").replace("&", "&")
1.52 -
1.53 -def linktext(element, feed_type):
1.54 - if feed_type == "rss":
1.55 - return text(element)
1.56 - else:
1.57 - return element.getAttribute("href")
1.58 -
1.59 -def need_content(show_content, tagname):
1.60 - return show_content in ("content", "description") and tagname in ("content", "description")
1.61 -
1.62 -# Error classes.
1.63 -
1.64 -class FeedError(Exception):
1.65 - pass
1.66 -
1.67 -class FeedMissingError(FeedError):
1.68 - pass
1.69 -
1.70 -class FeedContentTypeError(FeedError):
1.71 - pass
1.72 -
1.73 -# Feed retrieval.
1.74 -
1.75 -def getUpdates(request, feed_url, max_entries, show_content):
1.76 -
1.77 - """
1.78 - Using the given 'request', retrieve from 'feed_url' up to the given number
1.79 - 'max_entries' of update entries. The 'show_content' parameter can indicate
1.80 - that a "summary" is to be obtained for each update, that the "content" of
1.81 - each update is to be obtained (falling back to a summary if no content is
1.82 - provided), or no content (indicated by a false value) is to be obtained.
1.83 -
1.84 - A tuple of the form ((feed_type, channel_title, channel_link), updates) is
1.85 - returned.
1.86 - """
1.87 -
1.88 - feed_updates = []
1.89 -
1.90 - # Obtain the resource, using a cached version if appropriate.
1.91 -
1.92 - max_cache_age = int(getattr(request.cfg, "moin_share_max_cache_age", "300"))
1.93 - data = getCachedResource(request, feed_url, "MoinShare", "wiki", max_cache_age)
1.94 - if not data:
1.95 - raise FeedMissingError
1.96 -
1.97 - # Interpret the cached feed.
1.98 -
1.99 - feed = StringIO(data)
1.100 - _url, content_type, _encoding, _metadata = getCachedResourceMetadata(feed)
1.101 -
1.102 - if content_type not in ("application/atom+xml", "application/rss+xml", "application/xml"):
1.103 - raise FeedContentTypeError
1.104 -
1.105 - try:
1.106 - # Parse each node from the feed.
1.107 -
1.108 - channel_title = channel_link = None
1.109 -
1.110 - feed_type = None
1.111 - update = None
1.112 - in_source = False
1.113 -
1.114 - events = xml.dom.pulldom.parse(feed)
1.115 -
1.116 - for event, value in events:
1.117 -
1.118 - if not in_source and event == xml.dom.pulldom.START_ELEMENT:
1.119 - tagname = value.localName
1.120 -
1.121 - # Detect the feed type and items.
1.122 -
1.123 - if tagname == "feed" and value.namespaceURI == ATOM_NS:
1.124 - feed_type = "atom"
1.125 -
1.126 - elif tagname == "rss":
1.127 - feed_type = "rss"
1.128 -
1.129 - # Detect items.
1.130 -
1.131 - elif feed_type == "rss" and tagname == "item" or \
1.132 - feed_type == "atom" and tagname == "entry":
1.133 -
1.134 - update = Update()
1.135 -
1.136 - # Detect source declarations.
1.137 -
1.138 - elif feed_type == "atom" and tagname == "source":
1.139 - in_source = True
1.140 -
1.141 - # Handle item elements.
1.142 -
1.143 - elif tagname == "title":
1.144 - events.expandNode(value)
1.145 - if update:
1.146 - update.title = text(value)
1.147 - else:
1.148 - channel_title = text(value)
1.149 -
1.150 - elif tagname == "link":
1.151 - events.expandNode(value)
1.152 - if update:
1.153 - update.link = linktext(value, feed_type)
1.154 - else:
1.155 - channel_link = linktext(value, feed_type)
1.156 -
1.157 - elif show_content and (
1.158 - feed_type == "atom" and tagname in ("content", "summary") or
1.159 - feed_type == "rss" and tagname == "description"):
1.160 -
1.161 - events.expandNode(value)
1.162 -
1.163 - # Obtain content where requested or, failing that, a
1.164 - # summary.
1.165 -
1.166 - if update and (need_content(show_content, tagname) or tagname == "summary" and not update.content):
1.167 - if feed_type == "atom":
1.168 - update.content_type = value.getAttribute("type") or "text"
1.169 -
1.170 - # Normalise the content types and extract the
1.171 - # content.
1.172 -
1.173 - if update.content_type in ("xhtml", "application/xhtml+xml", "application/xml"):
1.174 - update.content = children(value)
1.175 - update.content_type = "application/xhtml+xml"
1.176 - elif update.content_type in ("html", "text/html"):
1.177 - update.content = text(value)
1.178 - update.content_type = "text/html"
1.179 - else:
1.180 - update.content = text(value)
1.181 - update.content_type = "text/plain"
1.182 - else:
1.183 - update.content_type = "text/html"
1.184 - update.content = text(value)
1.185 -
1.186 - elif feed_type == "atom" and tagname == "updated" or \
1.187 - feed_type == "rss" and tagname == "pubDate":
1.188 -
1.189 - events.expandNode(value)
1.190 -
1.191 - if update:
1.192 - if feed_type == "atom":
1.193 - value = getDateTimeFromISO8601(text(value))
1.194 - else:
1.195 - value = DateTime(parsedate(text(value)))
1.196 - update.updated = value
1.197 -
1.198 - elif event == xml.dom.pulldom.END_ELEMENT:
1.199 - tagname = value.localName
1.200 -
1.201 - if feed_type == "rss" and tagname == "item" or \
1.202 - feed_type == "atom" and tagname == "entry":
1.203 -
1.204 - feed_updates.append(update)
1.205 -
1.206 - update = None
1.207 -
1.208 - elif feed_type == "atom" and tagname == "source":
1.209 - in_source = False
1.210 -
1.211 - finally:
1.212 - feed.close()
1.213 -
1.214 - return (feed_type, channel_title, channel_link), feed_updates
1.215
1.216 # The macro itself.
1.217