~launchpad-pqm/launchpad/devel

4982.2.1 by Brad Crittenden
Working ZCML feeds directive
1
= Feeds components =
2
4898.3.65 by Elliot Murphy
More test cleanup on feeds doctest.
3
`feeds` is a ZCML directive for handling feeds requests.  The
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
4
directive requires minimal ZCML configuration and relies on
5
implementing view classes defining the `usedfor` and `name` class
6
variables.
7
8
Feeds are defined on the `FeedsLayer` layer.
9
4898.3.65 by Elliot Murphy
More test cleanup on feeds doctest.
10
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
11
== Demonstrating a feeds class ==
12
13
In order to demonstrate a feed class, we need to create an interface
14
for the thing comprising the feed.  Rather than use a standard
15
Launchpad interface, we'll define one here.
16
17
    >>> from zope.interface import Interface, Attribute, implements
14557.1.11 by Curtis Hovey
Moved feeds to lp.feeds.
18
    >>> from lp.services.feeds.tests.helper import (
5062.2.2 by Brad Crittenden
Changes due to review.
19
    ...     IThing, Thing, ThingFeedView)
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
20
21
22
== ZCML for browser:feeds ==
23
4898.3.65 by Elliot Murphy
More test cleanup on feeds doctest.
24
The zcml `browser:feeds` directive describes a feed view.
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
25
26
    >>> from zope.configuration import xmlconfig
27
    >>> zcmlcontext = xmlconfig.string("""
28
    ... <configure xmlns:browser="http://namespaces.zope.org/browser">
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
29
    ...   <include package="lp.services.webapp" file="meta.zcml" />
8521.3.10 by Gary Poster
address test failures found in last ec2test run
30
    ...   <include package="zope.app.zcmlfiles" file="meta.zcml" />
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
31
    ...   <browser:feeds
14557.1.11 by Curtis Hovey
Moved feeds to lp.feeds.
32
    ...       module="lp.services.feeds.tests.helper"
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
33
    ...       classes="ThingFeedView"
34
    ...       />
35
    ... </configure>
36
    ... """)
37
38
Get the view from a `thing` on 'thing-feed' for a request.
39
40
    >>> from zope.component import getMultiAdapter
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
41
    >>> from lp.services.webapp.servers import LaunchpadTestRequest
42
    >>> from lp.services.webapp.testing import verifyObject
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
43
44
    >>> request = LaunchpadTestRequest()
45
46
To successfully get a view, the lookup must be on an object that is an
5062.2.2 by Brad Crittenden
Changes due to review.
47
`IThing` and the view name must be one that is supported.  If the
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
48
view is not found a ComponentLookupError is raised.  Also, the request
49
must be in the FeedsLayer.
50
51
The request we just created is not in the FeedsLayer, so the view will
52
not be found.
53
54
    >>> thing = Thing('thing 1')
55
    >>> verifyObject(IThing, thing)
56
    True
57
    >>> feed_view = getMultiAdapter((thing, request), name='thing-feed.xml')
58
    Traceback (most recent call last):
59
      ...
60
    ComponentLookupError: ...
61
62
Set the layer on the request for all subsequent uses.
63
14600.1.8 by Curtis Hovey
Move c.l.layers to lp.
64
    >>> from lp.layers import setFirstLayer, FeedsLayer
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
65
    >>> setFirstLayer(request, FeedsLayer)
66
67
If the context object is not an IThing then the view will not be
68
found.
69
70
    >>> thing = object()
71
    >>> verifyObject(IThing, thing)
72
    Traceback (most recent call last):
73
      ...
74
    DoesNotImplement: ...
75
    >>> feed_view = getMultiAdapter((thing, request), name='thing-feed.atom')
76
    Traceback (most recent call last):
77
      ...
78
    ComponentLookupError: ...
79
80
If the name is not one of the supported names the view will not be
81
found.
82
83
    >>> thing = Thing('thing 1')
84
    >>> verifyObject(IThing, thing)
85
    True
86
    >>> feed_view = getMultiAdapter((thing, request), name='thing-feed.xml')
87
    Traceback (most recent call last):
88
      ...
89
    ComponentLookupError: ...
90
91
If the thing is an IThing and the name is supported the view will be
92
found, indicated by the absence of a ComponentLookupError.
93
94
    >>> thing = Thing('thing 1')
95
    >>> verifyObject(IThing, thing)
96
    True
4898.3.65 by Elliot Murphy
More test cleanup on feeds doctest.
97
    >>> for name in ['thing-feed.atom', 'thing-feed.html']:
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
98
    ...     feed_view = getMultiAdapter((thing, request), name=name)
4898.3.65 by Elliot Murphy
More test cleanup on feeds doctest.
99
    ...     print feed_view()
100
    a feed view on an IThing
101
    a feed view on an IThing
102
5132.2.1 by Edwin Grubbs
Added configuration options and tests.
103
== Feeds Configuration Options ==
104
105
The max_feed_cache_minutes and the max_bug_feed_cache_minutes
106
configurations are provided to allow overriding the Expires
107
and Cache-Control headers so that the feeds will be cached longer
108
by browsers and the reverse-proxy in front of the feeds servers.
109
14605.1.1 by Curtis Hovey
Moved canonical.config to lp.services.
110
    >>> from lp.services.config import config
14606.4.2 by William Grant
merge canonical.lazr.feed into lp.services.feeds.
111
    >>> from lp.services.feeds.feed import FeedBase
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
112
    >>> from lp.bugs.feed.bug import BugsFeedBase
5132.2.1 by Edwin Grubbs
Added configuration options and tests.
113
    >>> config.launchpad.max_feed_cache_minutes
114
    60
115
    >>> config.launchpad.max_bug_feed_cache_minutes
116
    30
117
    >>> FeedBase.max_age
118
    3600
119
    >>> BugsFeedBase.max_age
120
    1800
5132.2.3 by Edwin Grubbs
Made changes recommended by jamesh.
121
    >>> config.launchpad.max_feed_cache_minutes * 60 == FeedBase.max_age
5132.2.1 by Edwin Grubbs
Added configuration options and tests.
122
    True
5132.2.3 by Edwin Grubbs
Made changes recommended by jamesh.
123
    >>> (config.launchpad.max_bug_feed_cache_minutes * 60
124
    ...  == BugsFeedBase.max_age)
5132.2.1 by Edwin Grubbs
Added configuration options and tests.
125
    True
4982.2.1 by Brad Crittenden
Working ZCML feeds directive
126
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
127
128
== FeedTypedData class ==
129
130
The FeedTypedData class performs the appropriate escaping for data
131
with a type of "text", "html", or "xhtml". The template is responsible
132
for setting the "type" attribute in the <title> or <content> element. If
133
no content type is specified, then "text" is assumed.
134
135
Since plain text and html are not valid xml, "<", ">", and "&" must
136
be escaped using xml entities such as "&lt;".
137
14606.4.2 by William Grant
merge canonical.lazr.feed into lp.services.feeds.
138
    >>> from lp.services.feeds.feed import FeedTypedData
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
139
    >>> text = FeedTypedData("<b> and &nbsp; and &amp;")
140
    >>> text.content
141
    '&lt;b&gt; and &amp;nbsp; and &amp;amp;'
142
    >>> text2 = FeedTypedData("<b> and &nbsp; and &amp;", content_type="text")
143
    >>> text2.content
144
    '&lt;b&gt; and &amp;nbsp; and &amp;amp;'
145
    >>> html = FeedTypedData("<b> and &nbsp; and &amp;", content_type="html")
146
    >>> html.content
147
    '&lt;b&gt; and &amp;nbsp; and &amp;amp;'
148
149
Since xhtml is valid xml, the "<" and ">" characters do not need to be
150
escaped. However, xhtml supports many more entities than xml, and Internet
151
Explorer 7 does not allow a DTD to be specified in feeds which prevents the
152
xhtml entities from easily being registered in the xml document. Therefore,
153
the xhtml entities will be converted to valid UTF-8. Since some simplistic
5429.1.2 by Edwin Grubbs
Changed templates and HasAnnouncementsView to use the FeedsMixin
154
feed readers may expect ascii, we prefer using "html" over "xhtml", however,
155
we are testing xhtml encoding here in case we need it in the future.
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
156
157
    >>> xhtml = FeedTypedData("<b> and &nbsp; and &amp;</b><hr/>",
158
    ...               content_type="xhtml")
159
    >>> xhtml.content
9010.4.4 by Guilherme Salgado
Fix a couple tests that broke because of the way the new BeautifulSoup handles references
160
    u'<b> and \xa0 and &</b><hr />'
5549.2.2 by Edwin Grubbs
Call validate_feed() in xx-branch-atom.txt and xx-announcements.txt
161
162
163
== validate_feed() helper function ==
164
165
Pagetests can use the validate_feed() function to perform all the
166
checks available at http://feedvalidator.org
167
168
Occasionally, there will be suggestions from feedvalidator that we will
169
ignore. For example, when the <title type="text"> element contains html,
170
we want to view the html as opposed to having the title text formatted.
5549.2.4 by Edwin Grubbs
feeds.txt wording fix
171
Therefore, we won't set type="html" for the title element.
5549.2.2 by Edwin Grubbs
Call validate_feed() in xx-branch-atom.txt and xx-announcements.txt
172
173
    >>> feed = """<?xml version="1.0" encoding="UTF-8"?>
174
    ... <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
175
    ... <id>one</id>
176
    ... <title>Bugs in Launchpad itself</title>
177
    ... <link rel="self" href="http://foo.bar" />
178
    ... <updated>2006-05-19T06:37:40.344941+00:00</updated>
179
    ... <entry>
180
    ...     <id>http://launchpad.dev/entry</id>
181
    ...     <author></author>
182
    ...     <updated>2007-05-19T06:37:40.344941+00:00</updated>
183
    ...     <title type="text">&lt;b&gt;hello&lt;/b&gt;</title>
184
    ...     <content type="html">&lt;b&gt;hello&lt;/b&gt;</content>
185
    ... </entry>
186
    ... </feed>"""
14557.1.11 by Curtis Hovey
Moved feeds to lp.feeds.
187
    >>> from lp.services.feeds.tests.helper import validate_feed
5549.2.2 by Edwin Grubbs
Call validate_feed() in xx-branch-atom.txt and xx-announcements.txt
188
    >>> validate_feed(feed, '/atom+xml', 'http://ubuntu.com')
189
    -------- Error: InvalidFullLink --------
190
    Backupcolumn: 7
191
    Backupline: 3
192
    Column: 7
193
    Element: id
194
    Line: 3
195
    Parent: feed
196
    Value: one
197
    =
198
      1: <?xml version="1.0" encoding="UTF-8"?>
199
      2: <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
200
      3: <id>one</id>
201
       : ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
202
      4: <title>Bugs in Launchpad itself</title>
203
      5: <link rel="self" href="http://foo.bar" />
204
    =
205
    -------- Error: MissingElement --------
206
    Backupcolumn: 12
207
    Backupline: 9
208
    Column: 12
209
    Element: name
210
    Line: 9
211
    Parent: author
212
    =
213
      7: <entry>
214
      8:     <id>http://launchpad.dev/entry</id>
215
      9:     <author></author>
216
       : ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217
     10:     <updated>2007-05-19T06:37:40.344941+00:00</updated>
218
     11:     <title type="text">&lt;b&gt;hello&lt;/b&gt;</title>
219
    =
220
    -------- Warning: UnexpectedContentType --------
221
    Contenttype: /atom+xml
222
    Type: Feeds
223
    -------- Warning: SelfDoesntMatchLocation --------
224
    Backupcolumn: 41
225
    Backupline: 5
226
    Column: 41
227
    Element: href
228
    Line: 5
229
    Parent: feed
230
    Location: http://ubuntu.com
231
    =
232
      3: <id>one</id>
233
      4: <title>Bugs in Launchpad itself</title>
234
      5: <link rel="self" href="http://foo.bar" />
235
       : ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
236
      6: <updated>2006-05-19T06:37:40.344941+00:00</updated>
237
      7: <entry>
238
    =
239
    -------- Warning: ContainsUndeclaredHTML --------
240
    Backupcolumn: 47
241
    Backupline: 11
242
    Column: 47
243
    Element: title
244
    Line: 11
245
    Parent: entry
246
    Value: b
247
    =
248
      9:     <author></author>
249
     10:     <updated>2007-05-19T06:37:40.344941+00:00</updated>
250
     11:     <title type="text">&lt;b&gt;hello&lt;/b&gt;</title>
251
       : ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
252
     12:     <content type="html">&lt;b&gt;hello&lt;/b&gt;</content>
253
     13: </entry>
254
    =