1
2 """TurboFeed controllers for RSS/Atom feeds handling."""
3 __docformat__ = 'restructuredtext'
4
5 __all__ = [
6 'FeedController',
7 ]
8
9 __version__ = '1.1'
10
11 import logging
12
13 import turbogears
14
15 from util import xml_stylesheet, absolute_url
16
17
18 log = logging.getLogger('turbofeeds.controllers')
19
20
22 """Controller for generating feeds in multiple formats.
23
24 Must be subclassed and a ``get_feed_data`` method provided that returns
25 a dict with the feed info (see constructor doc strings) and an ``entries``
26 member, which should be a list filled with dicts each representing a feed
27 entry (see docstring for the default implementation).
28 """
29
30 formats = ["atom1.0", "atom0.3", "rss2.0"]
31
32
33 - def __init__(self, default="atom1.0", base_url='/feed', **feed_params):
34 """Constructor - Should be called with ``super()`` if overwritten.
35
36 The ``default`` arguments sets the default feed format when the
37 feed base URL is requested. Currently supported values are
38 ``atom1_0`` (the default), ``atom0_3`` and ''rss1_0``.
39
40 The ``base_url`` sets the URL where the feed controller is mounted
41 to the CherryPy object tree. The default is '/feed'. This is used
42 to construct the full feed URL for the ``link`` element in the feed,
43 if it is not overwritten by the ``link`` keyword argument.
44
45 Any extra keyword arguments will be assigned to the ``feed_params``
46 attribute and added to the feed data everytime a feed is requested.
47 This can be used to set the feed info in the direct child elements
48 of the ``feed`` (Atom) resp. ``channel`` root element of the feed.
49 Possible names include:
50
51 * author (dict with ``name``, ``email`` and ``uri`` members)
52 * categories (list of strings, used by Atom 1.0 / RSS only)
53 * generator (string)
54 * updated (datetime)
55 * icon (URL, used by Atom 1.0 format only)
56 * id (string/URL, used by Atom formats only)
57 * logo (URL, used by Atom 1.0 / RSS format only)
58 * lang (string, used by RSS format only)
59 * link (URL)
60 * rights (string)
61 * subtitle (string)
62 * stylesheet (URL or dict with members ``href`` and ``type`` or a
63 calable returning either, used place a matching xml-stylesheet
64 processing instruction at the top of the feed XML)
65 * title (string)
66
67 For up-to-date information about supported elements and values, please
68 refer to the templates for the different feed formats in the
69 ``templates`` sub-package.
70 """
71
72 super(FeedController, self).__init__()
73 assert default in self.formats
74 self.default = default
75 self.base_url = base_url
76 self.feed_params = feed_params
77
78
79 @turbogears.expose()
80 - def index(self, *args, **kwargs):
81 """Redirects to the default feed format rendering method."""
82
83 turbogears.redirect(turbogears.url("%s" % self.default), kwargs)
84
85 @turbogears.expose(template="turbofeeds.templates.atom0_3",
86 format="xml", content_type="application/atom+xml")
87 - def atom0_3(self, *args, **kwargs):
88 """Renders Atom 0.3 XML feed."""
89
90 feed = self.init_feed()
91 feed.update(self.get_feed_data(*args, **kwargs))
92 self.format_dates(feed, 3339)
93 feed.setdefault('link', self.get_feed_url("atom0_3", *args, **kwargs))
94 log.debug(feed)
95 return feed
96
97 @turbogears.expose(template="turbofeeds.templates.atom1_0",
98 format="xml", content_type="application/atom+xml")
99 - def atom1_0(self, *args, **kwargs):
100 """Renders Atom 1.0 XML feed."""
101
102 feed = self.init_feed()
103 feed.update(self.get_feed_data(*args, **kwargs))
104 self.format_dates(feed, 3339)
105 feed.setdefault('link', self.get_feed_url("atom1_0", *args, **kwargs))
106 log.debug(feed)
107 return feed
108
109 @turbogears.expose(template="turbofeeds.templates.rss2_0",
110 format="xml", content_type="application/rss+xml")
112 """Renders RSS 2.0 XML feed."""
113
114 feed = self.init_feed()
115 feed.update(self.get_feed_data(*args, **kwargs))
116 self.format_dates(feed, 822)
117 feed.setdefault('link', self.get_feed_url("rss2_0", *args, **kwargs))
118 log.debug(feed)
119 return feed
120
121
123 """Converts datatime object to RFC 3339 string representation."""
124
125 date = date.strftime("%Y-%m-%dT%H:%M:%SZ")
126 return date
127
129 """Converts datatime object to RFC 822 string representation."""
130
131 date = date.strftime("%a, %d %b %Y %H:%M:%S GMT")
132 return date
133
150
152 """Returns dict with feed info and a list of feed entries.
153
154 This method must be overwritten by a subclass.
155
156 It should return a dictionary with members for the feed info (see the
157 constructor doc string) and a member ``entries``, which is a list with
158 dict items for each feed entry. Supported members in each dict include:
159
160 * author (dict with ``name``, ``email`` and ``uri`` members)
161 * categories (list of strings, used by Atom 1.0 / RSS only)
162 * content (string or dict with ``type`` and ``value`` members,
163 Atom formats only)
164 * updated (datetime, Atom only)
165 * id (string/URL, Atom only)
166 * link (URL)
167 * published (datetime)
168 * rights (string, Atom 1.0 only)
169 * summary (string)
170 * title (string)
171
172 For up-to-date information about supported elements and values, please
173 refer to the templates for the different feed formats in the
174 ``templates`` sub-package.
175 """
176
177 raise NotImplementedError
178
179 - def get_feed_url(self, version='atom1_0', *args, **kwargs):
193
195 """Initializes feed data with the kwargs given to the constructor."""
196
197 feed = {
198 'xml_stylesheet': xml_stylesheet,
199 'generator': 'TurboFeeds FeedController version %s' % __version__
200 }
201 feed.update(self.feed_params)
202 return feed
203