8687.15.17
by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/. |
1 |
# Copyright 2009 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
3 |
|
4 |
"""Announcement feed (syndication) views."""
|
|
5 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
6 |
# This module has been chosen to be the example for how to implement a new
|
7 |
# feed class. While the two interfaces `IFeed` and `IFeedEntry` are heavily
|
|
8 |
# documented, additional documentation has been added to this module to
|
|
9 |
# clearly demonstrate the concepts required to implement a feed rather than
|
|
10 |
# simply referencing the interfaces.
|
|
11 |
||
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
12 |
__metaclass__ = type |
13 |
||
14 |
__all__ = [ |
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
15 |
'LaunchpadAnnouncementsFeed', |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
16 |
'TargetAnnouncementsFeed', |
17 |
]
|
|
18 |
||
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
19 |
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
20 |
from zope.component import getUtility |
21 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
22 |
from canonical.launchpad.interfaces.launchpad import IFeedsApplication |
23 |
from canonical.launchpad.webapp import ( |
|
24 |
canonical_url, |
|
25 |
urlappend, |
|
26 |
)
|
|
27 |
from canonical.lazr.feed import ( |
|
28 |
FeedBase, |
|
29 |
FeedEntry, |
|
30 |
FeedPerson, |
|
31 |
FeedTypedData, |
|
32 |
)
|
|
33 |
from lp.app.browser.stringformatter import FormattersAPI |
|
10326.1.3
by Henning Eggers
Fixed too long lines. |
34 |
from lp.registry.interfaces.announcement import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
35 |
IAnnouncementSet, |
36 |
IHasAnnouncements, |
|
37 |
)
|
|
7675.110.3
by Curtis Hovey
Ran the migration script to move registry code to lp.registry. |
38 |
from lp.registry.interfaces.distribution import IDistribution |
39 |
from lp.registry.interfaces.product import IProduct |
|
10326.1.2
by Henning Eggers
Renamed project interfaces module to projectgroup. |
40 |
from lp.registry.interfaces.projectgroup import IProjectGroup |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
41 |
|
42 |
||
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
43 |
class AnnouncementsFeedBase(FeedBase): |
44 |
"""Abstract class for announcement feeds."""
|
|
45 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
46 |
# Every feed must have a feed name. This name will be used to construct
|
47 |
# the final element in the URL for the feed with the extension for one of
|
|
48 |
# the supported feed types appended. So announcement feeds will end with
|
|
49 |
# 'announcements.atom' or 'announcements.html'.
|
|
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
50 |
feedname = "announcements" |
51 |
||
52 |
@property
|
|
5427.5.10
by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency. |
53 |
def link_alternate(self): |
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
54 |
"""See `IFeed`."""
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
55 |
# Return the human-readable alternate URL for this feed. For example:
|
56 |
# https://launchpad.net/ubuntu/+announcements
|
|
5204.6.18
by Brad Crittenden
Removed normalizedUrl and us urlappend instead. |
57 |
return urlappend(canonical_url(self.context, rootsite="mainsite"), |
58 |
"+announcements") |
|
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
59 |
|
60 |
def itemToFeedEntry(self, announcement): |
|
61 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
62 |
# Given an instance of an announcement, create a FeedEntry out of it
|
63 |
# and return.
|
|
64 |
||
65 |
# The title for the FeedEntry is an IFeedTypedData instance and may be
|
|
66 |
# plain text or html.
|
|
67 |
title = self._entryTitle(announcement) |
|
68 |
# The link_alternate for the entry is the human-readable alternate URL
|
|
69 |
# for the entry. For example:
|
|
70 |
# http://launchpad.net/ubuntu/+announcment/12
|
|
5427.5.10
by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency. |
71 |
entry_link_alternate = "%s%s" % ( |
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
72 |
canonical_url(announcement.target, rootsite=self.rootsite), |
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
73 |
"/+announcement/%d" % announcement.id) |
74 |
# The content of the entry is the text displayed as the body in the
|
|
75 |
# feed reader. For announcements it is plain text but it must be
|
|
76 |
# escaped to account for any special characters the user may have
|
|
77 |
# entered, such as '&' and '<' because it will be embedded in the XML
|
|
78 |
# document.
|
|
5475.2.1
by Edwin Grubbs
Fixed bug 175780 |
79 |
formatted_summary = FormattersAPI(announcement.summary).text_to_html() |
5204.6.35
by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries. |
80 |
content = FeedTypedData(formatted_summary, |
81 |
content_type="html", |
|
82 |
root_url=self.root_url) |
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
83 |
# The entry for an announcement has distinct dates for created,
|
84 |
# updated, and published. For some data, the created and published
|
|
85 |
# dates will be the same. The announcements also only have a singe
|
|
86 |
# author.
|
|
5958.2.10
by Brad Crittenden
Changes from JamesH review. |
87 |
|
88 |
entry_id = 'tag:launchpad.net,%s:/+announcement/%d' % ( |
|
89 |
announcement.date_created.date().isoformat(), |
|
90 |
announcement.id) |
|
91 |
entry = FeedEntry( |
|
5958.2.1
by Brad Crittenden
Changes to make feeds database access more efficient. |
92 |
title=title, |
93 |
link_alternate=entry_link_alternate, |
|
94 |
date_created=announcement.date_created, |
|
95 |
date_updated=announcement.date_updated, |
|
96 |
date_published=announcement.date_announced, |
|
97 |
authors=[FeedPerson(announcement.registrant, |
|
98 |
rootsite="mainsite")], |
|
5958.2.10
by Brad Crittenden
Changes from JamesH review. |
99 |
content=content, |
100 |
id_=entry_id) |
|
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
101 |
return entry |
102 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
103 |
def _entryTitle(self, announcement): |
104 |
"""Return the title for the announcement.
|
|
105 |
||
106 |
Override in each base class.
|
|
107 |
"""
|
|
108 |
raise NotImplementedError |
|
109 |
||
110 |
||
111 |
class LaunchpadAnnouncementsFeed(AnnouncementsFeedBase): |
|
5151.1.49
by Mark Shuttleworth
Additional review feedback and test fixes |
112 |
"""Publish an Atom feed of all public announcements in Launchpad."""
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
113 |
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
114 |
# The `usedfor` property identifies the class associated with this feed
|
115 |
# class. It is used by the `IFeedsDirective` in
|
|
116 |
# launchpad/webapp/metazcml.py to provide a mapping from the supported
|
|
117 |
# feed types to this class. It is a more maintainable method than simply
|
|
118 |
# listing each mapping in the zcml. The only zcml change is to add this
|
|
119 |
# class to the list of classes in the `browser:feeds` stanza of
|
|
120 |
# launchpad/zcml/feeds.zcml.
|
|
5151.1.14
by Mark Shuttleworth
Publish single Atom feed with all announcements hosted in LP |
121 |
usedfor = IFeedsApplication |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
122 |
|
5958.2.7
by Brad Crittenden
Changed getItemsWorker to _getItemsWorker. |
123 |
def _getItemsWorker(self): |
5958.2.4
by Brad Crittenden
Added caching to the getItems method for Feeds. |
124 |
"""Create the list of items.
|
125 |
||
126 |
Called by getItems which may cache the results.
|
|
127 |
"""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
128 |
# Return a list of items that will be the entries in the feed. Each
|
129 |
# item shall be an instance of `IFeedEntry`.
|
|
130 |
||
5151.1.49
by Mark Shuttleworth
Additional review feedback and test fixes |
131 |
# The quantity is defined in FeedBase or config file.
|
9041.1.1
by William Grant
IHasAnnouncements.announcements() -> getAnnouncements() |
132 |
items = getUtility(IAnnouncementSet).getAnnouncements( |
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
133 |
limit=self.quantity) |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
134 |
# Convert the items into their feed entry representation.
|
135 |
items = [self.itemToFeedEntry(item) for item in items] |
|
136 |
return items |
|
137 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
138 |
def _entryTitle(self, announcement): |
139 |
"""Return an `IFeedTypedData` instance for the feed title."""
|
|
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
140 |
return FeedTypedData('[%s] %s' % ( |
141 |
announcement.target.name, announcement.title)) |
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
142 |
|
143 |
@property
|
|
144 |
def title(self): |
|
145 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
146 |
# The textual representation of the title for the feed.
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
147 |
return "Announcements published via Launchpad" |
148 |
||
149 |
@property
|
|
150 |
def logo(self): |
|
151 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
152 |
# The logo is an image representing the feed. Since this feed is for
|
153 |
# all announcements in Launchpad, return the Launchpad logo.
|
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
154 |
url = '/@@/launchpad-logo' |
155 |
return self.site_url + url |
|
156 |
||
157 |
@property
|
|
158 |
def icon(self): |
|
159 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
160 |
# The icon is an icon representing the feed. Since this feed is for
|
161 |
# all announcements in Launchpad, return the Launchpad icon.
|
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
162 |
url = '/@@/launchpad' |
163 |
return self.site_url + url |
|
164 |
||
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
165 |
|
166 |
class TargetAnnouncementsFeed(AnnouncementsFeedBase): |
|
167 |
"""Publish an Atom feed of all announcements.
|
|
168 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
169 |
Used for any class that implements IHasAnnouncements such as project,
|
170 |
product, or distribution.
|
|
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
171 |
"""
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
172 |
# This view is used for any class implementing `IHasAnnouncments`.
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
173 |
usedfor = IHasAnnouncements |
174 |
||
5958.2.7
by Brad Crittenden
Changed getItemsWorker to _getItemsWorker. |
175 |
def _getItemsWorker(self): |
5958.2.4
by Brad Crittenden
Added caching to the getItems method for Feeds. |
176 |
"""Create the list of items.
|
177 |
||
178 |
Called by getItems which may cache the results.
|
|
179 |
"""
|
|
5151.1.49
by Mark Shuttleworth
Additional review feedback and test fixes |
180 |
# The quantity is defined in FeedBase or config file.
|
9041.1.1
by William Grant
IHasAnnouncements.announcements() -> getAnnouncements() |
181 |
items = self.context.getAnnouncements(limit=self.quantity) |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
182 |
# Convert the items into their feed entry representation.
|
183 |
items = [self.itemToFeedEntry(item) for item in items] |
|
184 |
return items |
|
185 |
||
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
186 |
def _entryTitle(self, announcement): |
5427.5.1
by Brad Crittenden
Correcting feed elements for announcements and refactoring. |
187 |
return FeedTypedData(announcement.title) |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
188 |
|
189 |
@property
|
|
190 |
def title(self): |
|
191 |
"""See `IFeed`."""
|
|
192 |
return "%s Announcements" % self.context.displayname |
|
193 |
||
194 |
@property
|
|
195 |
def logo(self): |
|
196 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
197 |
# The logo is different depending upon the context we are displaying.
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
198 |
if self.context.logo is not None: |
5151.1.32
by Mark Shuttleworth
Clean up unused imports in Announcements |
199 |
return self.context.logo.getURL() |
10326.1.1
by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*. |
200 |
elif IProjectGroup.providedBy(self.context): |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
201 |
url = '/@@/project-logo' |
202 |
elif IProduct.providedBy(self.context): |
|
203 |
url = '/@@/product-logo' |
|
204 |
elif IDistribution.providedBy(self.context): |
|
205 |
url = '/@@/distribution-logo' |
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
206 |
else: |
207 |
raise AssertionError( |
|
208 |
"Context for TargetsAnnouncementsFeed does not provide an "
|
|
209 |
"expected interface.") |
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
210 |
return self.site_url + url |
211 |
||
212 |
@property
|
|
213 |
def icon(self): |
|
214 |
"""See `IFeed`."""
|
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
215 |
# The icon is customized based upon the context.
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
216 |
if self.context.icon is not None: |
5151.1.32
by Mark Shuttleworth
Clean up unused imports in Announcements |
217 |
return self.context.icon.getURL() |
10326.1.1
by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*. |
218 |
elif IProjectGroup.providedBy(self.context): |
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
219 |
url = '/@@/project' |
220 |
elif IProduct.providedBy(self.context): |
|
221 |
url = '/@@/product' |
|
222 |
elif IDistribution.providedBy(self.context): |
|
223 |
url = '/@@/distribution' |
|
5427.5.11
by Brad Crittenden
Added additional documentation for feeds. |
224 |
else: |
225 |
raise AssertionError( |
|
226 |
"Context for TargetsAnnouncementsFeed does not provide an "
|
|
227 |
"expected interface.") |
|
5151.1.10
by Mark Shuttleworth
Lay foundations for announcement feeds |
228 |
return self.site_url + url |