Back to index

moin  1.9.0~rc2
rss_rc.py
Go to the documentation of this file.
00001 """
00002     RSS Handling
00003 
00004     If you do changes, please check if it still validates after your changes:
00005 
00006     http://feedvalidator.org/
00007 
00008     @copyright: 2006-2007 MoinMoin:ThomasWaldmann
00009     @license: GNU GPL, see COPYING for details.
00010 """
00011 import StringIO, re, time
00012 from MoinMoin import wikixml, config, wikiutil
00013 from MoinMoin.logfile import editlog
00014 from MoinMoin.util import timefuncs
00015 from MoinMoin.Page import Page
00016 from MoinMoin.wikixml.util import RssGenerator
00017 
00018 def full_url(request, page, querystr=None, anchor=None):
00019     url = page.url(request, anchor=anchor, querystr=querystr)
00020     url = wikiutil.escape(url)
00021     return request.getQualifiedURL(url)
00022 
00023 def execute(pagename, request):
00024     """ Send recent changes as an RSS document
00025     """
00026     if not wikixml.ok:
00027         request.mimetype = 'text/plain'
00028         request.write("rss_rc action is not supported because of missing pyxml module.")
00029         return
00030 
00031     cfg = request.cfg
00032 
00033     # get params
00034     items_limit = 100
00035     try:
00036         max_items = int(request.values['items'])
00037         max_items = min(max_items, items_limit) # not more than `items_limit`
00038     except (KeyError, ValueError):
00039         # not more than 15 items in a RSS file by default
00040         max_items = 15
00041     try:
00042         unique = int(request.values.get('unique', 0))
00043     except ValueError:
00044         unique = 0
00045     try:
00046         diffs = int(request.values.get('diffs', 0))
00047     except ValueError:
00048         diffs = 0
00049     ## ddiffs inserted by Ralf Zosel <ralf@zosel.com>, 04.12.2003
00050     try:
00051         ddiffs = int(request.values.get('ddiffs', 0))
00052     except ValueError:
00053         ddiffs = 0
00054 
00055     # get data
00056     log = editlog.EditLog(request)
00057     logdata = []
00058     counter = 0
00059     pages = {}
00060     lastmod = 0
00061     for line in log.reverse():
00062         if not request.user.may.read(line.pagename):
00063             continue
00064         if (not line.action.startswith('SAVE') or
00065             ((line.pagename in pages) and unique)): continue
00066         #if log.dayChanged() and log.daycount > _MAX_DAYS: break
00067         line.editor = line.getInterwikiEditorData(request)
00068         line.time = timefuncs.tmtuple(wikiutil.version2timestamp(line.ed_time_usecs)) # UTC
00069         logdata.append(line)
00070         pages[line.pagename] = None
00071 
00072         if not lastmod:
00073             lastmod = wikiutil.version2timestamp(line.ed_time_usecs)
00074 
00075         counter += 1
00076         if counter >= max_items:
00077             break
00078     del log
00079 
00080     timestamp = timefuncs.formathttpdate(lastmod)
00081     etag = "%d-%d-%d-%d-%d" % (lastmod, max_items, diffs, ddiffs, unique)
00082 
00083     # for 304, we look at if-modified-since and if-none-match headers,
00084     # one of them must match and the other is either not there or must match.
00085     if request.if_modified_since == timestamp:
00086         if request.if_none_match:
00087             if request.if_none_match == etag:
00088                 request.status_code = 304
00089         else:
00090             request.status_code = 304
00091     elif request.if_none_match == etag:
00092         if request.if_modified_since:
00093             if request.if_modified_since == timestamp:
00094                 request.status_code = 304
00095         else:
00096             request.status_code = 304
00097     else:
00098         # generate an Expires header, using whatever setting the admin
00099         # defined for suggested cache lifetime of the RecentChanges RSS doc
00100         expires = time.time() + cfg.rss_cache
00101 
00102         request.mime_type = 'text/xml'
00103         request.expires = expires
00104         request.last_modified = lastmod
00105         request.headers.add('Etag', etag)
00106 
00107         # send the generated XML document
00108         baseurl = request.url_root
00109 
00110         logo = re.search(r'src="([^"]*)"', cfg.logo_string)
00111         if logo:
00112             logo = request.getQualifiedURL(logo.group(1))
00113 
00114         # prepare output
00115         out = StringIO.StringIO()
00116         handler = RssGenerator(out)
00117 
00118         # start SAX stream
00119         handler.startDocument()
00120         handler._out.write(
00121             '<!--\n'
00122             '    Add an "items=nnn" URL parameter to get more than the default 15 items.\n'
00123             '    You cannot get more than %d items though.\n'
00124             '    \n'
00125             '    Add "unique=1" to get a list of changes where page names are unique,\n'
00126             '    i.e. where only the latest change of each page is reflected.\n'
00127             '    \n'
00128             '    Add "diffs=1" to add change diffs to the description of each items.\n'
00129             '    \n'
00130             '    Add "ddiffs=1" to link directly to the diff (good for FeedReader).\n'
00131             '    Current settings: items=%i, unique=%i, diffs=%i, ddiffs=%i'
00132             '-->\n' % (items_limit, max_items, unique, diffs, ddiffs)
00133             )
00134 
00135         # emit channel description
00136         handler.startNode('channel', {
00137             (handler.xmlns['rdf'], 'about'): request.url_root,
00138             })
00139         handler.simpleNode('title', cfg.sitename)
00140         page = Page(request, pagename)
00141         handler.simpleNode('link', full_url(request, page))
00142         handler.simpleNode('description', 'RecentChanges at %s' % cfg.sitename)
00143         if logo:
00144             handler.simpleNode('image', None, {
00145                 (handler.xmlns['rdf'], 'resource'): logo,
00146                 })
00147         if cfg.interwikiname:
00148             handler.simpleNode(('wiki', 'interwiki'), cfg.interwikiname)
00149 
00150         handler.startNode('items')
00151         handler.startNode(('rdf', 'Seq'))
00152         for item in logdata:
00153             anchor = "%04d%02d%02d%02d%02d%02d" % item.time[:6]
00154             page = Page(request, item.pagename)
00155             link = full_url(request, page, anchor=anchor)
00156             handler.simpleNode(('rdf', 'li'), None, attr={(handler.xmlns['rdf'], 'resource'): link, })
00157         handler.endNode(('rdf', 'Seq'))
00158         handler.endNode('items')
00159         handler.endNode('channel')
00160 
00161         # emit logo data
00162         if logo:
00163             handler.startNode('image', attr={
00164                 (handler.xmlns['rdf'], 'about'): logo,
00165                 })
00166             handler.simpleNode('title', cfg.sitename)
00167             handler.simpleNode('link', baseurl)
00168             handler.simpleNode('url', logo)
00169             handler.endNode('image')
00170 
00171         # emit items
00172         for item in logdata:
00173             page = Page(request, item.pagename)
00174             anchor = "%04d%02d%02d%02d%02d%02d" % item.time[:6]
00175             rdflink = full_url(request, page, anchor=anchor)
00176             handler.startNode('item', attr={(handler.xmlns['rdf'], 'about'): rdflink, })
00177 
00178             # general attributes
00179             handler.simpleNode('title', item.pagename)
00180             if ddiffs:
00181                 handler.simpleNode('link', full_url(request, page, querystr={'action': 'diff'}))
00182             else:
00183                 handler.simpleNode('link', full_url(request, page))
00184 
00185             handler.simpleNode(('dc', 'date'), timefuncs.W3CDate(item.time))
00186 
00187             # description
00188             desc_text = item.comment
00189             if diffs:
00190                 # TODO: rewrite / extend wikiutil.pagediff
00191                 # searching for the matching pages doesn't really belong here
00192                 revisions = page.getRevList()
00193 
00194                 rl = len(revisions)
00195                 for idx in range(rl):
00196                     rev = revisions[idx]
00197                     if rev <= item.rev:
00198                         if idx + 1 < rl:
00199                             lines = wikiutil.pagediff(request, item.pagename, revisions[idx+1], item.pagename, 0, ignorews=1)
00200                             if len(lines) > 20:
00201                                 lines = lines[:20] + ['...\n']
00202                             lines = '\n'.join(lines)
00203                             lines = wikiutil.escape(lines)
00204                             desc_text = '%s\n<pre>\n%s\n</pre>\n' % (desc_text, lines)
00205                         break
00206             if desc_text:
00207                 handler.simpleNode('description', desc_text)
00208 
00209             # contributor
00210             edattr = {}
00211             if cfg.show_hosts:
00212                 edattr[(handler.xmlns['wiki'], 'host')] = item.hostname
00213             if item.editor[0] == 'interwiki':
00214                 edname = "%s:%s" % item.editor[1]
00215                 ##edattr[(None, 'link')] = baseurl + wikiutil.quoteWikiname(edname)
00216             else: # 'ip'
00217                 edname = item.editor[1]
00218                 ##edattr[(None, 'link')] = link + "?action=info"
00219 
00220             # this edattr stuff, esp. None as first tuple element breaks things (tracebacks)
00221             # if you know how to do this right, please send us a patch
00222 
00223             handler.startNode(('dc', 'contributor'))
00224             handler.startNode(('rdf', 'Description'), attr=edattr)
00225             handler.simpleNode(('rdf', 'value'), edname)
00226             handler.endNode(('rdf', 'Description'))
00227             handler.endNode(('dc', 'contributor'))
00228 
00229             # wiki extensions
00230             handler.simpleNode(('wiki', 'version'), "%i" % (item.ed_time_usecs))
00231             handler.simpleNode(('wiki', 'status'), ('deleted', 'updated')[page.exists()])
00232             handler.simpleNode(('wiki', 'diff'), full_url(request, page, querystr={'action': 'diff'}))
00233             handler.simpleNode(('wiki', 'history'), full_url(request, page, querystr={'action': 'info'}))
00234             # handler.simpleNode(('wiki', 'importance'), ) # ( major | minor )
00235             # handler.simpleNode(('wiki', 'version'), ) # ( #PCDATA )
00236 
00237             handler.endNode('item')
00238 
00239         # end SAX stream
00240         handler.endDocument()
00241 
00242         request.write(out.getvalue())
00243