Attachment 'IRSS.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - Inline RSS generator
   4 
   5     Copyright (c) 2003 by Conectiva, Inc.
   6     Copyright (c) 2000-2002 by Jürgen Hermann <jh@web.de>
   7     All rights reserved, see COPYING for details.
   8 
   9     Written by Gustavo Niemeyer <niemeyer@conectiva.com>, based on
  10     code from RecentChanges.
  11 
  12     This macro allows one to use a moin page as an RSS feed. To
  13     define an RSS feed, enclose the items you want to provide in the
  14     comment tags "##irss start" and "##irss stop", like this:
  15     
  16     ----
  17 
  18     This won't be considered
  19 
  20     ##irss start
  21 
  22     == Newest item ==
  23     This will be the newest RSS item.
  24 
  25     == Older item ==
  26     This is another RSS item.
  27 
  28     ##irss stop
  29 
  30     This won't be considered as well.
  31 
  32     ----
  33 
  34     Then, you can link to that feed in from any moin page using
  35     [[IRSS([PageName])]]. If linking from the same page, the pagename
  36     parameter is optional.
  37 
  38     You can also have more than one feed in the same page. To do that,
  39     use "##irss start yourfeedname", and link to it with
  40     [[IRSS([PageName], yourfeedname)]] (you can use an empty first
  41     parameter, if the feed is in the same page). Using the feedname in
  42     the stop tag, like "##irss stop yourfeedname", is optional and necessary
  43     only if you want to have one feed "contained" inside another one.
  44 
  45     The following tags can also be used between the start and stop tags
  46     to configure the feed.
  47 
  48     ##irss topic <rss topic>
  49 
  50         This will set the "title" of the whole RSS feed. Only the first
  51         topic found is considered. The default topic is the page name.
  52 
  53     ##irss descr <rss description>
  54 
  55         This will set the "description" of the whole RSS feed. Only the
  56         first description found is considered. The default description
  57         is "News from <pagename>".
  58 
  59     ##irss reverse 
  60 
  61         The default behavior is to have the newer items at the top of
  62         the list. If used, this tag will reverse this behavior.
  63 
  64     ##irss link <url>
  65 
  66         This tag should be used just after a Moin title, and will
  67         make the RSS item title link to that URL. The default behavior
  68         is to link the RSS item title to the Moin title in the page
  69         where the feed is defined.
  70 """
  71 
  72 import re, string, sys, StringIO, new, sha
  73 import pprint
  74 from MoinMoin import config, util, wikiutil, wikimacro
  75 from MoinMoin.Page import Page
  76 from MoinMoin.wikixml.util import RssGenerator
  77 from MoinMoin.parser.wiki import Parser
  78 from MoinMoin.formatter.text_html import Formatter
  79 
  80 def execute(pagename, request):
  81 
  82     page = Page(request, pagename)
  83 
  84     # Send a null page to make the page set its internal data (ugh!)
  85     out = StringIO.StringIO()
  86     backup = sys.stdout, request.write
  87     sys.stdout, request.write = out, out.write
  88     page.send_page(request, content_only=1)
  89     sys.stdout, request.write = backup
  90 
  91     # get params
  92     items_limit = 100
  93     try:
  94         max_items = int(request.form['items'].value)
  95         max_items = min(max_items, items_limit) # not more than `items_limit`
  96     except (KeyError, ValueError):
  97         # not more than 15 items in a RSS file by default
  98         max_items = 15
  99 
 100     # prepare output
 101     out = StringIO.StringIO()
 102     handler = RssGenerator(out)
 103 
 104     # get data
 105     interwiki = request.getBaseURL()
 106     if interwiki[-1] != "/": interwiki = interwiki + "/"
 107 
 108     logo = re.search(r'src="([^"]*)"', request.cfg.logo_string)
 109     if logo: logo = request.getQualifiedURL(logo.group(1))
 110 
 111     title_re = re.compile(r"^(={1,5}) (?P<title>.*) \1$")
 112     start_re = re.compile(r"^##irss\s+start(?:\s+(?P<name>\S+))?\s*$")
 113     stop_re = re.compile(r"^##irss stop(?:\s+(?P<name>\S+))?\s*$")
 114     topic_re = re.compile(r"^##irss\s+topic\s+(?P<topic>.*)$")
 115     descr_re = re.compile(r"^##irss\s+descr\s+(?P<descr>.*)$")
 116     reverse_re = re.compile(r"^##irss\s+reverse\s*$")
 117     link_re = re.compile(r"^##irss\s+link\s+(?P<link>.*)$")
 118     feeding = 0
 119     counter = 0
 120     rssdata = []
 121     rsstopic = None
 122     rssdescr = None
 123     rssreverse = 0
 124     class RssData:
 125         def __init__(self, title):
 126             self.title = title
 127             self.link = None
 128             self.lines = []
 129     if not request.user.may.read(pagename):
 130         rssdata.append(RssData("You are not allowed to view that page."))
 131     else:
 132         rssname = None
 133         titles = {}
 134         body = IncludeParser(request, page).parse(page.get_raw_body())
 135         for line in body.splitlines():
 136             m = start_re.match(line)
 137             if m:
 138                 if feeding:
 139                     continue
 140                 name = m.group("name")
 141                 try:
 142                     if name != request.form["name"].value: continue
 143                 except KeyError:
 144                     if name: continue
 145                 rssname = name
 146                 feeding = 1
 147                 continue
 148 
 149             m = title_re.match(line)
 150             if m:
 151                 if counter >= max_items:
 152                     break
 153                 # Based on parser/wiki.py
 154                 item_title = m.group("title")
 155                 try:
 156                     titles[item_title] += 1
 157                     unique_id = '-%d' % titles[item.title]
 158                 except KeyError:
 159                     titles[item_title] = 1
 160                     unique_id = ''
 161                 if not feeding:
 162                     continue
 163                 item = RssData(item_title)
 164                 rssdata.append(item)
 165                 item.link = "%s%s#head-%s%s" % \
 166                             (interwiki, wikiutil.quoteWikinameURL(pagename),
 167                              sha.new(item.title).hexdigest(), unique_id)
 168                 counter += 1
 169                 continue
 170 
 171             if not feeding:
 172                 continue
 173 
 174             m = stop_re.match(line)
 175             if m:
 176                 name = m.group("name")
 177                 if name and name != rssname:
 178                     continue
 179                 feeding = 0
 180                 continue
 181 
 182             m = topic_re.match(line)
 183             if m:
 184                 if not rsstopic:
 185                     rsstopic = m.group("topic").strip()
 186                 continue
 187 
 188             m = descr_re.match(line)
 189             if m:
 190                 if not rssdescr:
 191                     rssdescr = m.group("descr").strip()
 192                 continue
 193 
 194             m = link_re.match(line)
 195             if m:
 196                 if rssdata:
 197                     rssdata[-1].link = m.group("link")
 198                 continue
 199 
 200             m = reverse_re.match(line)
 201             if m:
 202                 rssreverse = 1
 203                 continue
 204 
 205             if rssdata and not line.startswith("##"):
 206                 rssdata[-1].lines.append(line)
 207 
 208     if not rsstopic:
 209         rsstopic = pagename
 210     if not rssdescr:
 211         rssdescr = "News from " + pagename
 212     if rssreverse:
 213         rssdata.reverse()
 214 
 215     # start SAX stream
 216     handler.startDocument()
 217 
 218     # emit channel description
 219     handler.startNode('channel', {
 220         (handler.xmlns['rdf'], 'about'): request.getBaseURL(),
 221     })
 222     handler.simpleNode('title', rsstopic)
 223     handler.simpleNode('link', interwiki +
 224                                wikiutil.quoteWikinameURL(pagename))
 225     handler.simpleNode('description', rssdescr)
 226     if logo:
 227         handler.simpleNode('image', None, {
 228             (handler.xmlns['rdf'], 'resource'): logo,
 229         })
 230     if request.cfg.interwikiname:
 231         handler.simpleNode(('wiki', 'interwiki'), request.cfg.interwikiname)
 232 
 233     handler.endNode('channel')
 234 
 235     # emit logo data
 236     if logo:
 237         handler.startNode('image', attr={
 238             (handler.xmlns['rdf'], 'about'): logo,
 239         })
 240         handler.simpleNode('title', rsstopic)
 241         handler.simpleNode('link', interwiki + 
 242                                    wikiutil.quoteWikinameURL(pagename))
 243         handler.simpleNode('url', logo)
 244         handler.endNode('image')
 245 
 246     # emit items
 247     for item in rssdata:
 248         handler.startNode('item')
 249         handler.simpleNode('title', item.title)
 250         handler.simpleNode('link', item.link)
 251 
 252         #handler.simpleNode(('dc', 'date'), util.W3CDate(item.time))
 253 
 254         desc_text = "\n".join(item.lines)
 255         if desc_text:
 256             parsed = StringIO.StringIO()
 257             request_write = request.write
 258             request.write = parsed.write
 259             Parser(desc_text, request).format(page.formatter)
 260             request.write = request_write
 261             handler.simpleNode('description', parsed.getvalue())
 262         handler.endNode('item')
 263 
 264     # end SAX stream
 265     handler.endDocument()
 266 
 267     # send the generated XML document
 268     request.http_headers(["Content-Type: text/xml; charset=%s" % config.charset]
 269                          + request.nocache)
 270     sys.stderr.write(out.getvalue().encode('utf-8'))
 271     sys.stderr.write("whatever works\n")
 272     request.write(out.getvalue())
 273     request.finish()
 274     request.no_closing_html_code = 1
 275 
 276 class IncludeParser:
 277     def __init__(self, request, page):
 278         self.parser = Parser("", request)
 279         # Format the empty string, making it set its internal data (ugh!).
 280         out = StringIO.StringIO()
 281         backup = sys.stdout, request.write
 282         sys.stdout, request.write = out, out.write
 283         self.parser.format(page.formatter)
 284         sys.stdout, request.write = backup
 285         self.include_re = re.compile("\[\[Include(?:\(.*?\))?\]\]")
 286         self.macro = wikimacro.Macro(self.parser)
 287 
 288         # This is really deep and cool black magic. Basically, it creates
 289         # a local copy of the function macro.Include.execute that behaves
 290         # exactly like the original one, but with a different "Page"
 291         # global. This allows us to follow changes in the Include macro
 292         # without much trouble.
 293         from MoinMoin.macro.Include import execute
 294         func_globals = {}
 295         func_globals.update(execute.func_globals)
 296         class IncludePage(Page):
 297             incparser = self
 298             def send_page(self, request, msg=None, **keywords):
 299                 request.write(self.incparser._parse(self.get_raw_body()))
 300         func_globals["Page"] = IncludePage
 301         self.execute = new.function(execute.func_code, func_globals,
 302                                     execute.func_name,
 303                                     execute.func_defaults)
 304 
 305     def _macro_repl(self, match):
 306         word = match.group(0)
 307         macro_name = word[2:-2]
 308         args = None
 309         if string.count(macro_name, "("):
 310             macro_name, args = string.split(macro_name, '(', 1)
 311             args = args[:-1]
 312         return self.execute(self.macro, args)
 313 
 314     def _parse(self, body):
 315         return self.include_re.sub(self._macro_repl, body)
 316 
 317     def parse(self, body):
 318         # Turn on some special settings, like disabling the "edit" link.
 319         #item = self.macro.form["action"]
 320         #pprint.pprint(self.macro.form, sys.stderr)
 321         #item_value = item.value
 322         #item.value = "print"
 323         body = self._parse(body)
 324         #item.value = item_value
 325         return body
 326 
 327 # vim:ts=4:sw=4:et

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2005-02-20 18:34:56, 10.8 KB) [[attachment:IRSS.py]]
  • [get | view] (2007-06-11 15:52:55, 0.9 KB) [[attachment:irss-moin-1.5.8.patch]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.