""" The rss2renderer is a renderer that will render your blog in RSS2 format. This allows you to have RSS2 syndication WITHOUT writing flavour templates for RSS2. The following are required properties in your config.py file: blog_title - the title of your blog blog_author - your name blog_email - your email address blog_description - the description of your blog blog_language - the language code for your blog blog_encoding - the encoding of your blog (defaults to utf-8) Optionally, you can specify: rss2_extension - the extension (defaults to "/index.rss2") that causes this renderer to be used Miscellaneous notes about this plugin: 1. the Content-Type we return is "application/xml" so your links should match 2. this doesn't handle comments 3. this plugin requires pyxml be installed FIXME - probably needs more information in this help portion! This code is placed in the public domain. Do with it as you will. Originally written by Blake Winton. Overhauled by Will Guaraldi. Just for clarity sake, Blake wrote the majority of the code, but Will is going to take responsibility to maintain the code going forward (unless Blake really wants to--doesn't bother Will either way). Revisions: 1.8 - (April 11, 2005) Changed instances of roughingit to the new web-site url. 1.7 - (March 9, 2005) Fixed problems with rss2renderer and conditionalhttp. 1.6 - (January 28, 2005) Fixed num_entries problem with PyBlosxom 1.2 (not yet released). 1.5 - (September 27, 2004) Fixed num_entries issue with PyBlosxom 1.1. 1.4 - (September 15, 2004) Fixed encoding issues (thanks Aslak!) 1.3 - (September 14, 2004) Fixed the content type (thanks Gabor!) Fixed spaces in the links (thanks Brett!) 1.2 - (September 13, 2004) Fixed minor issue causing invalidation (thanks Brett!) 1.1 - (September 12, 2004) Will's overhaul. 1.0 - Blake's original writing. """ __author__ = "Blake Winton and Will Guaraldi" __version__ = "1.8 (April 11, 2005)" __url__ = "http://www.bluesock.org/~willg/pyblosxom/" __description__ = "RSS2 renderer." from Pyblosxom.renderers.base import RendererBase from Pyblosxom.tools import Stripper from xml.dom.minidom import Document import urlparse class RSS2Renderer(RendererBase): """ This renderer is to create valid RSS2 documents without the need for a pyblosxom template. I mostly expect you to know what you are doing, before attempting this """ # How long you want the simple description to be desc_length = 20 # 20 words, at the most for me # Create the big html? , yes, then 1, else 0 create_entry = 1 entry_type = 'CDATA' # or 'escaped' - choose your poison # Namespaces for you to pick and choose namespaces = { 'admin': "http://webns.net/mvcb/", 'content': "http://purl.org/rss/1.0/modules/content/", 'creativeCommons': "http://backend.userland.com/creativeCommonsRssModule", 'dc': "http://purl.org/dc/elements/1.1/", 'rdf': "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 'html': "http://www.w3.org/1999/html", 'slash': "http://purl.org/rss/1.0/modules/slash/", } def _addText(self, element, text, baseElement): e = self._doc.createElement(element) e.appendChild(self._doc.createTextNode(text)) baseElement.appendChild(e) return e def _addCDATA(self, element, text, baseElement): e = self._doc.createElement(element) e.appendChild(self._doc.createCDATASection(text)) baseElement.appendChild(e) return e def _addElemAttr(self, element, attr, content, baseElement, text = None): e = self._doc.createElement(element) e.setAttribute(attr, content) if text: e.appendChild(self._doc.createTextNode(text)) baseElement.appendChild(e) return e def _addAttr(self, element, attr, content): element.setAttribute(attr, content) def _addNameSpaces(self, element, namespace_dict): for attr in namespace_dict: self._addAttr(element, 'xmlns:' + attr, namespace_dict[attr]) def _urlEncode(self, txt): # I'm doing it here because it's only a partial url-encoding txt = txt.replace(" ", "%20") return txt def _createChannel(self): # Start our RSS document here self._doc = Document() d = self._doc rss = d.createElement('rss') rss.setAttribute('version', '2.0') self._addNameSpaces(rss, self.namespaces) d.appendChild(rss) self._channel = d.createElement('channel') channel = self._channel rss.appendChild(channel) # Add details about our blog here self._addText('title', self._config['blog_title'], channel) self._addText('link', self._urlEncode(self._config['base_url']), channel) self._addText('description', self._config['blog_description'], channel) self._addText('language', self._config['blog_language'], channel) self._addText('ttl', '60', channel) self._addText('dc:creator', self._config['blog_author'], channel) self._addElemAttr('admin:generatorAgent', \ 'rdf:resource', \ 'http://pyblosxom.sourceforge.net/', \ channel) self._addElemAttr('admin:errorReportsTo', \ 'rdf:resource', \ "mailto:" + self._config.get("blog_email", "none"), \ channel) def _generateDesc(self, html): s = Stripper() s.feed(html) str = s.gettext() frag = str.split() if len(frag) > self.desc_length: frag = frag[:self.desc_length] frag.append('...') return ' '.join(frag) def _createItem(self, entry): burl = self._config['base_url'] d = urlparse.urlsplit(self._config['base_url']) domain = '%s://%s' % (d[0], d[1]) item = self._doc.createElement('item') self._channel.appendChild(item) self._addText('title', entry['title'], item) self._addElemAttr('guid', 'isPermaLink', 'false', item, entry['file_path']) url = urlparse.urljoin(burl + "/", entry["file_path"]) self._addText('link', self._urlEncode(url), item) # Text entry self._addText('description', self._generateDesc(entry['body']), item) if self.create_entry: if self.entry_type == 'CDATA': self._addCDATA('content:encoded', entry['body'], item) else: self._addText('content:encoded', entry['body'], item) # Metadata stuff # Category if entry['path'].strip(): # category or dc:subject, but NOT both self._addElemAttr('category', 'domain', domain, item, entry['path']) #self._addText('dc:subject', entry['path'], item) self._addText('dc:date', entry['w3cdate'], item) def render(self, header = 1): if self.rendered == 1: return self._data = self._request.getData() self._config = self._request.getConfiguration() self.addHeader('Content-Type', 'application/xml') self.showHeaders() self._createChannel() if self._config.get("num_entries", 0): max_entries = self._config["num_entries"] else: max_entries = 20 if self._content: if max_entries > len(self._content): num_entries = len(self._content) else: num_entries = max_entries for count in xrange(num_entries): self._createItem(self._content[count]) # We are now ready to present the xml # FIXME this is totally hokey, but if I pass the encoding into # toxml, then it tries to convert the data to the new encoding # and assumes the data is ascii (which is wrong). text = self._doc.toxml() if self._config.has_key("blog_encoding"): text = "" % self._config["blog_encoding"] + text[text.find("