~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
3
4
"""Base classes for feeds.
5
6
Supported feeds include Atom, Javascript, and HTML-snippets.
7
Future support may include feeds such as sparklines.
8
"""
9
10
__metaclass__ = type
11
12
__all__ = [
13
    'FeedBase',
14
    'FeedEntry',
4898.3.5 by Brad Crittenden
refactored FeedEntry
15
    'FeedPerson',
16
    'FeedTypedData',
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
17
    'MINUTES',
18
    ]
19
4898.3.21 by Brad Crittenden
Minor code cleanup.
20
import operator
5038.1.3 by Brad Crittenden
partial update to code in response to review and fixed config files.
21
import os
4898.5.5 by Edwin Grubbs
Added Expires, Last-Modified, and Cache-Control headers.
22
import time
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
23
from urlparse import urljoin
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
24
from xml.sax.saxutils import escape as xml_escape
4898.3.3 by Brad Crittenden
working checkpoing for bug feeds on products
25
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
26
from BeautifulSoup import BeautifulSoup
27
from z3c.ptcompat import ViewPageTemplateFile
28
from zope.component import getUtility
6061.16.1 by Curtis Hovey
Removed deprecation warning supression code. Updated ZCML, tests, and code to use
29
from zope.datetime import rfc1123_date
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
30
from zope.interface import implements
7849.16.54 by Sidnei da Silva
- Whitespace corrections and a merge accident
31
14605.1.1 by Curtis Hovey
Moved canonical.config to lp.services.
32
from lp.services.config import config
14606.4.3 by William Grant
Move the interfaces across too.
33
from lp.services.feeds.interfaces.feed import (
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
34
    IFeed,
35
    IFeedEntry,
36
    IFeedPerson,
37
    IFeedTypedData,
38
    UnsupportedFeedFormat,
39
    )
40
from lp.services.propertycache import cachedproperty
13333.5.3 by Jonathan Lange
Use the new utc_now()
41
from lp.services.utils import utc_now
14606.4.3 by William Grant
Move the interfaces across too.
42
from lp.services.webapp import (
43
    canonical_url,
44
    LaunchpadView,
45
    urlappend,
46
    urlparse,
47
    )
48
# XXX: bac 2007-09-20 bug=153795: modules in canonical.lazr should not import
49
# from canonical.launchpad, but we're doing it here as an expediency to get a
50
# working prototype.
51
from lp.services.webapp.interfaces import ILaunchpadRoot
52
from lp.services.webapp.vhosts import allvhosts
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
53
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
54
5062.2.2 by Brad Crittenden
Changes due to review.
55
SUPPORTED_FEEDS = ('.atom', '.html')
5427.5.2 by Brad Crittenden
Checkpoint. Non-working.
56
MINUTES = 60 # Seconds in a minute.
5062.2.2 by Brad Crittenden
Changes due to review.
57
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
58
5578.1.1 by Brad Crittenden
Feed refactoring. Removed unnecessary methods from FeedsBase. Use LaunchpadView.
59
class FeedBase(LaunchpadView):
5427.5.9 by Brad Crittenden
Added more documentation to the IFeed class. Begin making IFeedEntry, thought it is not used yet.
60
    """See `IFeed`.
61
62
    Base class for feeds.
63
    """
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
64
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
65
    implements(IFeed)
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
66
5132.2.1 by Edwin Grubbs
Added configuration options and tests.
67
    # convert to seconds
68
    max_age = config.launchpad.max_feed_cache_minutes * MINUTES
4898.3.16 by Brad Crittenden
Merged Edwin's changes, rearranged templates.
69
    quantity = 25
4898.3.5 by Brad Crittenden
refactored FeedEntry
70
    items = None
5399.3.1 by Elliot Murphy
Fix alternate link for feeds to point to the correct location rather
71
    rootsite = 'mainsite'
4898.3.16 by Brad Crittenden
Merged Edwin's changes, rearranged templates.
72
    template_files = {'atom': 'templates/feed-atom.pt',
73
                      'html': 'templates/feed-html.pt'}
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
74
75
    def __init__(self, context, request):
5427.5.1 by Brad Crittenden
Correcting feed elements for announcements and refactoring.
76
        super(FeedBase, self).__init__(context, request)
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
77
        self.format = self.feed_format
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
78
        self.root_url = canonical_url(getUtility(ILaunchpadRoot),
79
                                      rootsite=self.rootsite)
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
80
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
81
    @property
82
    def title(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
83
        """See `IFeed`."""
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
84
        raise NotImplementedError
85
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
86
    @property
5427.5.10 by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency.
87
    def link_self(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
88
        """See `IFeed`."""
5549.2.2 by Edwin Grubbs
Call validate_feed() in xx-branch-atom.txt and xx-announcements.txt
89
90
        # The self link is the URL for this particular feed.  For example:
91
        # http://feeds.launchpad.net/ubuntu/announcments.atom
92
        path = "%s.%s" % (self.feedname, self.format)
93
        return urlappend(canonical_url(self.context, rootsite="feeds"),
94
                         path)
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
95
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
96
    @property
97
    def site_url(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
98
        """See `IFeed`."""
4898.3.32 by Brad Crittenden
working checkpoint for feeds
99
        return allvhosts.configs['mainsite'].rooturl[:-1]
5427.5.1 by Brad Crittenden
Correcting feed elements for announcements and refactoring.
100
5399.3.1 by Elliot Murphy
Fix alternate link for feeds to point to the correct location rather
101
    @property
5427.5.10 by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency.
102
    def link_alternate(self):
5399.3.1 by Elliot Murphy
Fix alternate link for feeds to point to the correct location rather
103
        """See `IFeed`."""
104
        return canonical_url(self.context, rootsite=self.rootsite)
4898.3.4 by Brad Crittenden
Working links with correct feed updated element. Added feed logo and entry authors.
105
5427.5.1 by Brad Crittenden
Correcting feed elements for announcements and refactoring.
106
    @property
5427.5.2 by Brad Crittenden
Checkpoint. Non-working.
107
    def feed_id(self):
5427.5.3 by Brad Crittenden
Corrected the creation of <id> tags for feeds and added tests.
108
        """See `IFeed`.
109
110
        Override this method if the context used does not create a
111
        meaningful id.
112
        """
113
        # Get the creation date, if available.  Otherwise use a fixed date, as
114
        # allowed by the RFC.
6194.3.2 by Paul Hummer
Removed hasattr, replacing it with getattr
115
        if getattr(self.context, 'datecreated', None) is not None:
5427.5.3 by Brad Crittenden
Corrected the creation of <id> tags for feeds and added tests.
116
            datecreated = self.context.datecreated.date().isoformat()
6194.3.2 by Paul Hummer
Removed hasattr, replacing it with getattr
117
        elif getattr(self.context, 'date_created', None) is not None:
5427.5.3 by Brad Crittenden
Corrected the creation of <id> tags for feeds and added tests.
118
            datecreated = self.context.date_created.date().isoformat()
119
        else:
120
            datecreated = "2008"
5427.5.10 by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency.
121
        url_path = urlparse(self.link_alternate)[2]
5427.5.2 by Brad Crittenden
Checkpoint. Non-working.
122
        if self.rootsite != 'mainsite':
5427.5.3 by Brad Crittenden
Corrected the creation of <id> tags for feeds and added tests.
123
            id_ = 'tag:launchpad.net,%s:/%s%s' % (
124
                datecreated,
5427.5.2 by Brad Crittenden
Checkpoint. Non-working.
125
                self.rootsite,
126
                url_path)
127
        else:
128
            id_ = 'tag:launchpad.net,%s:%s' % (
5427.5.3 by Brad Crittenden
Corrected the creation of <id> tags for feeds and added tests.
129
                datecreated,
5427.5.2 by Brad Crittenden
Checkpoint. Non-working.
130
                url_path)
5427.5.1 by Brad Crittenden
Correcting feed elements for announcements and refactoring.
131
        return id_
132
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
133
    def getItems(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
134
        """See `IFeed`."""
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
135
        if self.items is None:
5958.2.9 by Brad Crittenden
Fixed naming problem
136
            self.items = self._getItemsWorker()
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
137
        return self.items
138
5958.2.7 by Brad Crittenden
Changed getItemsWorker to _getItemsWorker.
139
    def _getItemsWorker(self):
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
140
        """Create the list of items.
141
5958.2.10 by Brad Crittenden
Changes from JamesH review.
142
        Called by getItems which may cache the results.  The caching is
143
        necessary since `getItems` is called multiple times in the course of
144
        constructing a single feed and pulling together the list of items is
145
        potentially expensive.
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
146
        """
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
147
        raise NotImplementedError
148
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
149
    @property
150
    def feed_format(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
151
        """See `IFeed`."""
6768.2.1 by Edwin Grubbs
URL that was raising UnsupportedFeedFormat oops now raises NotFound error.
152
        # If the full URL is http://feeds.launchpad.dev/announcements.atom/foo
153
        # getURL() will return http://feeds.launchpad.dev/announcements.atom
154
        # when traversing the feed, which will allow os.path.splitext()
155
        # to split off ".atom" correctly.
156
        path = self.request.getURL()
5038.1.3 by Brad Crittenden
partial update to code in response to review and fixed config files.
157
        extension = os.path.splitext(path)[1]
5062.2.3 by Brad Crittenden
Fixed show_columns, fixed test, removed unnecessary code, commented zcml, other review changes.
158
        if extension in SUPPORTED_FEEDS:
5038.1.3 by Brad Crittenden
partial update to code in response to review and fixed config files.
159
            return extension[1:]
4898.3.21 by Brad Crittenden
Minor code cleanup.
160
        else:
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
161
            raise UnsupportedFeedFormat('%s is not supported' % path)
4898.3.21 by Brad Crittenden
Minor code cleanup.
162
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
163
    @property
164
    def logo(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
165
        """See `IFeed`."""
4898.3.4 by Brad Crittenden
Working links with correct feed updated element. Added feed logo and entry authors.
166
        raise NotImplementedError
167
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
168
    @property
169
    def icon(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
170
        """See `IFeed`."""
5038.1.5 by Brad Crittenden
working checkpoint. added IFeed.
171
        return "%s/@@/launchpad" % self.site_url
4898.3.19 by Brad Crittenden
Added support for Atom feeds for a single bug and added tests for teams.
172
5038.1.4 by Brad Crittenden
checkpoint
173
    @cachedproperty
174
    def date_updated(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
175
        """See `IFeed`."""
5038.1.3 by Brad Crittenden
partial update to code in response to review and fixed config files.
176
        sorted_items = sorted(self.getItems(),
5151.1.11 by Mark Shuttleworth
Expose feeds on feeds.launchpad.net
177
                              key=operator.attrgetter('last_modified'),
5038.1.3 by Brad Crittenden
partial update to code in response to review and fixed config files.
178
                              reverse=True)
179
        if len(sorted_items) == 0:
5549.2.2 by Edwin Grubbs
Call validate_feed() in xx-branch-atom.txt and xx-announcements.txt
180
            # datetime.isoformat() doesn't place the necessary "+00:00"
181
            # for the feedvalidator's check of the iso8601 date format
182
            # unless a timezone is specified with tzinfo.
13333.5.3 by Jonathan Lange
Use the new utc_now()
183
            return utc_now()
5151.1.62 by Mark Shuttleworth
Fix feed dates to comply with RFC 4287
184
        last_modified = sorted_items[0].last_modified
185
        if last_modified is None:
5151.1.61 by Mark Shuttleworth
Require a date_updated for feed entries, complying with RFC 4287
186
            raise AssertionError, 'All feed entries require a date updated.'
5151.1.62 by Mark Shuttleworth
Fix feed dates to comply with RFC 4287
187
        return last_modified
4898.3.4 by Brad Crittenden
Working links with correct feed updated element. Added feed logo and entry authors.
188
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
189
    def render(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
190
        """See `IFeed`."""
4898.5.5 by Edwin Grubbs
Added Expires, Last-Modified, and Cache-Control headers.
191
        expires = rfc1123_date(time.time() + self.max_age)
5038.1.4 by Brad Crittenden
checkpoint
192
        if self.date_updated is not None:
5009.1.5 by Edwin Grubbs
Added tests to verify that the atom/html feeds do not work on the
193
            last_modified = rfc1123_date(
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
194
                time.mktime(self.date_updated.timetuple()))
5009.1.5 by Edwin Grubbs
Added tests to verify that the atom/html feeds do not work on the
195
        else:
196
            last_modified = rfc1123_date(time.time())
4898.5.5 by Edwin Grubbs
Added Expires, Last-Modified, and Cache-Control headers.
197
        response = self.request.response
198
        response.setHeader('Expires', expires)
199
        response.setHeader('Cache-Control', 'max-age=%d' % self.max_age)
200
        response.setHeader('X-Cache-Control', 'max-age=%d' % self.max_age)
201
        response.setHeader('Last-Modified', last_modified)
4898.5.7 by Edwin Grubbs
Fixed bug-html.txt tests.
202
203
        if self.format == 'atom':
204
            return self.renderAtom()
205
        elif self.format == 'html':
206
            return self.renderHTML()
207
        else:
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
208
            raise UnsupportedFeedFormat("Format %s is not supported" %
209
                                        self.format)
4898.5.7 by Edwin Grubbs
Fixed bug-html.txt tests.
210
211
    def renderAtom(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
212
        """See `IFeed`."""
5475.1.2 by Edwin Grubbs
Removed trailing whitespace.
213
        self.request.response.setHeader('content-type',
5475.1.1 by Edwin Grubbs
Fixes bug 181620. Set the content type for feeds to
214
                                        'application/atom+xml;charset=utf-8')
215
        template_file = ViewPageTemplateFile(self.template_files['atom'])
216
        result = template_file(self)
217
        # XXX EdwinGrubbs 2008-01-10 bug=181903
218
        # Zope3 requires the content-type to start with "text/" if
219
        # the result is a unicode object.
220
        return result.encode('utf-8')
4898.5.7 by Edwin Grubbs
Fixed bug-html.txt tests.
221
222
    def renderHTML(self):
5038.1.7 by Brad Crittenden
Fixed docstrings.
223
        """See `IFeed`."""
4898.5.7 by Edwin Grubbs
Fixed bug-html.txt tests.
224
        return ViewPageTemplateFile(self.template_files['html'])(self)
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
225
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
226
4898.3.2 by Brad Crittenden
feed via template. non-working checkpoint.
227
class FeedEntry:
5427.5.9 by Brad Crittenden
Added more documentation to the IFeed class. Begin making IFeedEntry, thought it is not used yet.
228
    """See `IFeedEntry`.
229
5427.5.10 by Brad Crittenden
Added more documentation to IFeed and created IFeedEntry. Renamed some attributes for clarity and consistency.
230
    An individual entry for a feed.
5427.5.9 by Brad Crittenden
Added more documentation to the IFeed class. Begin making IFeedEntry, thought it is not used yet.
231
    """
232
233
    implements(IFeedEntry)
234
4898.3.5 by Brad Crittenden
refactored FeedEntry
235
    def __init__(self,
236
                 title,
237
                 link_alternate,
5427.5.1 by Brad Crittenden
Correcting feed elements for announcements and refactoring.
238
                 date_created,
5151.1.61 by Mark Shuttleworth
Require a date_updated for feed entries, complying with RFC 4287
239
                 date_updated,
4898.3.5 by Brad Crittenden
refactored FeedEntry
240
                 date_published=None,
241
                 authors=None,
242
                 contributors=None,
243
                 content=None,
5204.6.21 by Brad Crittenden
Added branch feed for a person.
244
                 id_=None,
4898.3.5 by Brad Crittenden
refactored FeedEntry
245
                 generator=None,
246
                 logo=None,
247
                 icon=None):
248
        self.title = title
249
        self.link_alternate = link_alternate
250
        self.content = content
5204.6.21 by Brad Crittenden
Added branch feed for a person.
251
        self.date_created = date_created
5151.1.12 by Mark Shuttleworth
Show both date published and date updated in feeds
252
        self.date_updated = date_updated
4898.3.5 by Brad Crittenden
refactored FeedEntry
253
        self.date_published = date_published
5151.1.62 by Mark Shuttleworth
Fix feed dates to comply with RFC 4287
254
        if date_updated is None:
255
            raise AssertionError, 'date_updated is required by RFC 4287'
4898.3.5 by Brad Crittenden
refactored FeedEntry
256
        if authors is None:
257
            authors = []
258
        self.authors = authors
259
        self.contributors = contributors
5958.2.10 by Brad Crittenden
Changes from JamesH review.
260
        if id_ is None:
261
            self.id = self.construct_id()
262
        else:
263
            self.id = id_
5204.6.21 by Brad Crittenden
Added branch feed for a person.
264
265
    @property
266
    def last_modified(self):
267
        if self.date_published is not None:
268
            return max(self.date_published, self.date_updated)
269
        return self.date_updated
270
271
    def construct_id(self):
272
        url_path = urlparse(self.link_alternate)[2]
273
        return 'tag:launchpad.net,%s:%s' % (
274
            self.date_created.date().isoformat(),
5958.2.1 by Brad Crittenden
Changes to make feeds database access more efficient.
275
            url_path)
4898.3.5 by Brad Crittenden
refactored FeedEntry
276
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
277
4898.3.5 by Brad Crittenden
refactored FeedEntry
278
class FeedTypedData:
4898.3.21 by Brad Crittenden
Minor code cleanup.
279
    """Data for a feed that includes its type."""
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
280
281
    implements(IFeedTypedData)
282
4898.3.5 by Brad Crittenden
refactored FeedEntry
283
    content_types = ['text', 'html', 'xhtml']
5062.2.2 by Brad Crittenden
Changes due to review.
284
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
285
    def __init__(self, content, content_type='text', root_url=None):
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
286
        self._content = content
4898.3.5 by Brad Crittenden
refactored FeedEntry
287
        if content_type not in self.content_types:
5062.2.2 by Brad Crittenden
Changes due to review.
288
            raise UnsupportedFeedFormat("%s: is not valid" % content_type)
4898.3.5 by Brad Crittenden
refactored FeedEntry
289
        self.content_type = content_type
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
290
        self.root_url = root_url
4898.3.5 by Brad Crittenden
refactored FeedEntry
291
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
292
    @property
293
    def content(self):
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
294
        if (self.content_type in ('html', 'xhtml') and
295
            self.root_url is not None):
296
            # Unqualified hrefs must be qualified using the original subdomain
297
            # or they will try be served from http://feeds.launchpad.net,
298
            # which will not work.
299
            soup = BeautifulSoup(self._content)
300
            a_tags = soup.findAll('a')
301
            for a_tag in a_tags:
302
                if a_tag['href'].startswith('/'):
303
                    a_tag['href'] = urljoin(self.root_url, a_tag['href'])
304
            altered_content = unicode(soup)
305
        else:
306
            altered_content = self._content
307
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
308
        if self.content_type in ('text', 'html'):
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
309
            altered_content = xml_escape(altered_content)
5429.1.1 by Edwin Grubbs
Escaping html in atom feeds in order to get around IE7 not allowing
310
        elif self.content_type == 'xhtml':
5204.6.33 by Brad Crittenden
Use html not xhtml for branch feed content. Use BeautifulSoup to convert HTML entities.
311
            soup = BeautifulSoup(
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
312
                altered_content,
5204.6.33 by Brad Crittenden
Use html not xhtml for branch feed content. Use BeautifulSoup to convert HTML entities.
313
                convertEntities=BeautifulSoup.HTML_ENTITIES)
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
314
            altered_content = unicode(soup)
315
        return altered_content
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
316
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
317
4898.3.5 by Brad Crittenden
refactored FeedEntry
318
class FeedPerson:
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
319
    """See `IFeedPerson`.
4898.3.21 by Brad Crittenden
Minor code cleanup.
320
321
    If this class is consistently used we will not accidentally leak email
322
    addresses.
323
    """
5038.1.6 by Brad Crittenden
Added additional IFeed interfaces per code review.
324
325
    implements(IFeedPerson)
326
4898.3.32 by Brad Crittenden
working checkpoint for feeds
327
    def __init__(self, person, rootsite):
4898.3.5 by Brad Crittenden
refactored FeedEntry
328
        self.name = person.displayname
329
        # We don't want to disclose email addresses in public feeds.
330
        self.email = None
4898.3.32 by Brad Crittenden
working checkpoint for feeds
331
        self.uri = canonical_url(person, rootsite=rootsite)