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.You are not allowed to attach a file to this page.