~launchpad-pqm/launchpad/devel

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).
5204.6.2 by Brad Crittenden
non-working checkpoint
3
4
"""Branch feed (syndication) views."""
5
6
__metaclass__ = type
7
8
__all__ = [
9
    'BranchFeed',
5204.6.21 by Brad Crittenden
Added branch feed for a person.
10
    'PersonBranchFeed',
6736.3.1 by Tim Penhey
Initial work.
11
    'PersonRevisionFeed',
5204.6.5 by Brad Crittenden
unworking checkpoint
12
    'ProductBranchFeed',
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
13
    'ProductRevisionFeed',
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
14
    'ProjectBranchFeed',
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
15
    'ProjectRevisionFeed',
5204.6.2 by Brad Crittenden
non-working checkpoint
16
    ]
17
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
18
from storm.locals import (
19
    Asc,
20
    Desc,
21
    )
22
from z3c.ptcompat import ViewPageTemplateFile
6194.3.1 by Paul Hummer
Fixed bug-192010 - The branches now sort correctly in a feed
23
from zope.component import getUtility
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
24
from zope.interface import implements
5204.6.2 by Brad Crittenden
non-working checkpoint
25
from zope.security.interfaces import Unauthorized
26
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
27
from lp.code.browser.branch import BranchView
28
from lp.code.interfaces.branch import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
29
    DEFAULT_BRANCH_STATUS_IN_LISTING,
30
    IBranch,
31
    )
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
32
from lp.code.interfaces.branchcollection import IAllBranches
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
33
from lp.code.interfaces.revisioncache import IRevisionCache
7675.110.3 by Curtis Hovey
Ran the migration script to move registry code to lp.registry.
34
from lp.registry.interfaces.person import IPerson
35
from lp.registry.interfaces.product import IProduct
10326.1.2 by Henning Eggers
Renamed project interfaces module to projectgroup.
36
from lp.registry.interfaces.projectgroup import IProjectGroup
14606.4.2 by William Grant
merge canonical.lazr.feed into lp.services.feeds.
37
from lp.services.config import config
38
from lp.services.feeds.feed import (
39
    FeedBase,
40
    FeedEntry,
41
    FeedPerson,
42
    FeedTypedData,
43
    MINUTES,
44
    )
14606.4.3 by William Grant
Move the interfaces across too.
45
from lp.services.feeds.interfaces.feed import IFeedPerson
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
46
from lp.services.propertycache import cachedproperty
14606.4.2 by William Grant
merge canonical.lazr.feed into lp.services.feeds.
47
from lp.services.webapp import (
48
    canonical_url,
49
    LaunchpadView,
50
    urlparse,
51
    )
52
from lp.services.webapp.interfaces import ILaunchpadRoot
5204.6.2 by Brad Crittenden
non-working checkpoint
53
54
8931.2.3 by Tim Penhey
More tests.
55
def revision_feed_id(revision):
56
    """Return a consistent id for a revision to use as an id."""
57
    return "tag:launchpad.net,%s:/revision/%s" % (
58
        revision.revision_date.date().isoformat(), revision.revision_id)
59
60
5204.6.21 by Brad Crittenden
Added branch feed for a person.
61
class BranchFeedEntry(FeedEntry):
62
    """See `IFeedEntry`."""
63
    def construct_id(self):
64
        url_path = urlparse(self.link_alternate)[2]
65
        return 'tag:launchpad.net,%s:/code%s' % (
66
            self.date_created.date().isoformat(),
67
            url_path)
68
69
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
70
class BranchFeedContentView(BranchView):
71
    """View for branch feed contents."""
5204.6.2 by Brad Crittenden
non-working checkpoint
72
5204.6.27 by Brad Crittenden
Added tests for single branch feed.
73
    def __init__(self, context, request, feed,
74
                 template='templates/branch.pt'):
5204.6.2 by Brad Crittenden
non-working checkpoint
75
        super(BranchFeedContentView, self).__init__(context, request)
76
        self.feed = feed
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
77
        self.template_ = template
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
78
5204.6.2 by Brad Crittenden
non-working checkpoint
79
    def render(self):
80
        """Render the view."""
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
81
        return ViewPageTemplateFile(self.template_)(self)
5204.6.2 by Brad Crittenden
non-working checkpoint
82
83
84
class BranchFeedBase(FeedBase):
85
    """Abstract class for branch feeds."""
86
87
    # max_age is in seconds
88
    max_age = config.launchpad.max_branch_feed_cache_minutes * MINUTES
89
90
    rootsite = "code"
91
92
    @property
93
    def logo(self):
94
        """See `IFeed`."""
95
        return "%s/@@/branch" % self.site_url
96
97
    def _getRawItems(self):
98
        """Get the raw set of items for the feed."""
99
        raise NotImplementedError
100
101
    def getPublicRawItems(self):
102
        """Private branchess are not to be shown in feeds.
103
104
        The list of branches is screened to ensure no private branches are
105
        returned.
106
        """
107
        return [branch
108
                for branch in self._getRawItems()
109
                if not branch.private]
110
5958.2.7 by Brad Crittenden
Changed getItemsWorker to _getItemsWorker.
111
    def _getItemsWorker(self):
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
112
        """Create the list of items.
113
114
        Called by getItems which may cache the results.
115
        """
5204.6.2 by Brad Crittenden
non-working checkpoint
116
        items = self.getPublicRawItems()
117
        # Convert the items into their feed entry representation.
118
        items = [self.itemToFeedEntry(item) for item in items]
119
        return items
120
121
    def itemToFeedEntry(self, branch):
122
        """See `IFeed`."""
5204.6.21 by Brad Crittenden
Added branch feed for a person.
123
        title = FeedTypedData(branch.displayname)
5204.6.2 by Brad Crittenden
non-working checkpoint
124
        url = canonical_url(branch, rootsite=self.rootsite)
5204.6.5 by Brad Crittenden
unworking checkpoint
125
        content_view = BranchFeedContentView(branch, self.request, self)
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
126
        content = content_view.render()
127
        content_data = FeedTypedData(content=content,
128
                                     content_type="html",
129
                                     root_url=self.root_url)
5204.6.21 by Brad Crittenden
Added branch feed for a person.
130
        entry = BranchFeedEntry(title=title,
131
                                link_alternate=url,
132
                                date_created=branch.date_created,
133
                                date_updated=branch.date_last_modified,
134
                                date_published=branch.date_created,
6916.1.1 by Curtis Hovey
Fixed comment formats to fidn missing persons, dates, and some bugs.
135
                                # XXX bac 2008-01-10: if author and owner are
136
                                # different perhaps we should use them both?
5204.6.21 by Brad Crittenden
Added branch feed for a person.
137
                                authors=[FeedPerson(branch.owner,
138
                                                    self.rootsite)],
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
139
                                content=content_data)
5204.6.2 by Brad Crittenden
non-working checkpoint
140
        return entry
141
142
5204.6.21 by Brad Crittenden
Added branch feed for a person.
143
class BranchListingFeed(BranchFeedBase):
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
144
    """Feed for all branches on a product or project."""
5204.6.5 by Brad Crittenden
unworking checkpoint
145
146
    feedname = "branches"
147
148
    @property
149
    def title(self):
150
        """See `IFeed`."""
151
        return "Branches for %s" % self.context.displayname
152
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
153
    def _getCollection(self):
154
        """Return the collection that `BranchListingFeed_getRawItems` uses."""
155
        raise NotImplementedError(self._getCollection)
156
6266.2.1 by Tim Penhey
Refactor the many getBranchesForXXX into a single getBranchesForContext.
157
    def _getRawItems(self):
158
        """See `BranchFeedBase._getRawItems`.
159
160
        Return the branches for this context sorted by date_created in
161
        descending order.
6736.3.19 by Tim Penhey
Updates following review.
162
163
        Only `self.quantity` revisions are returned.
6266.2.1 by Tim Penhey
Refactor the many getBranchesForXXX into a single getBranchesForContext.
164
        """
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
165
        from lp.code.model.branch import Branch
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
166
        collection = self._getCollection().visibleByUser(
167
            None).withLifecycleStatus(*DEFAULT_BRANCH_STATUS_IN_LISTING)
12504.1.3 by Robert Collins
Reject reversion of this branch on trunk.
168
        branches = collection.getBranches(eager_load=False)
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
169
        branches.order_by(
170
            Desc(Branch.date_last_modified),
7675.171.8 by Tim Penhey
Make the feed use the target_suffix instead of product.name.
171
            Asc(Branch.target_suffix),
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
172
            Desc(Branch.lifecycle_status),
173
            Asc(Branch.name))
174
        branches.config(limit=self.quantity)
175
        return list(branches)
6266.2.1 by Tim Penhey
Refactor the many getBranchesForXXX into a single getBranchesForContext.
176
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
177
5204.6.21 by Brad Crittenden
Added branch feed for a person.
178
class ProductBranchFeed(BranchListingFeed):
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
179
    """Feed for all branches on a product."""
180
181
    usedfor = IProduct
6194.3.1 by Paul Hummer
Fixed bug-192010 - The branches now sort correctly in a feed
182
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
183
    def _getCollection(self):
184
        return getUtility(IAllBranches).inProduct(self.context)
185
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
186
5204.6.21 by Brad Crittenden
Added branch feed for a person.
187
class ProjectBranchFeed(BranchListingFeed):
5204.6.6 by Brad Crittenden
Refactored project and product branch feeds.
188
    """Feed for all branches on a product."""
189
10326.1.1 by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*.
190
    usedfor = IProjectGroup
6194.3.1 by Paul Hummer
Fixed bug-192010 - The branches now sort correctly in a feed
191
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
192
    def _getCollection(self):
193
        return getUtility(IAllBranches).inProject(self.context)
194
5204.6.21 by Brad Crittenden
Added branch feed for a person.
195
196
class PersonBranchFeed(BranchListingFeed):
197
    """Feed for a person's branches."""
198
199
    usedfor = IPerson
6194.3.1 by Paul Hummer
Fixed bug-192010 - The branches now sort correctly in a feed
200
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
201
    def _getCollection(self):
7860.1.2 by Jonathan Lange
Make the person branch feed only show branches owned by the person.
202
        return getUtility(IAllBranches).ownedBy(self.context)
7860.1.1 by Jonathan Lange
Make the branch feed view use collections.
203
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
204
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
205
class RevisionFeedContentView(LaunchpadView):
6736.3.12 by Tim Penhey
Updates following review.
206
    """View for a revision feed contents."""
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
207
208
    def __init__(self, context, request, feed):
209
        super(RevisionFeedContentView, self).__init__(context, request)
210
        self.feed = feed
211
6736.3.6 by Tim Penhey
Update the look of the feed.
212
    @cachedproperty
213
    def branch(self):
214
        return self.context.getBranch()
215
216
    @cachedproperty
217
    def revno(self):
218
        return self.branch.getBranchRevision(revision=self.context).sequence
219
220
    @property
221
    def product(self):
222
        return self.branch.product
223
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
224
    def render(self):
225
        """Render the view."""
226
        return ViewPageTemplateFile('templates/revision.pt')(self)
227
6736.3.6 by Tim Penhey
Update the look of the feed.
228
    @property
229
    def title(self):
230
        if self.revno is None:
231
            revno = ""
232
        else:
233
            revno = "r%s " % self.revno
234
        log_lines = self.context.log_body.split('\n')
235
        first_line = log_lines[0]
236
        if len(first_line) < 60 and len(log_lines) == 1:
237
            logline = first_line
238
        else:
239
            logline = first_line[:60] + '...'
240
        return "[%(branch)s] %(revno)s %(logline)s" % {
6736.3.9 by Tim Penhey
Fix the tests.
241
            'branch': self.branch.name,
6736.3.6 by Tim Penhey
Update the look of the feed.
242
            'revno': revno,
243
            'logline': logline}
244
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
245
6736.3.1 by Tim Penhey
Initial work.
246
class RevisionListingFeed(FeedBase):
247
    """Abstract class for revision feeds."""
248
249
    # max_age is in seconds
250
    max_age = config.launchpad.max_revision_feed_cache_minutes * MINUTES
251
252
    rootsite = "code"
253
    feedname = "revisions"
254
255
    @property
256
    def logo(self):
257
        """See `IFeed`."""
258
        return "%s/@@/branch" % self.site_url
259
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
260
    def _getRevisionCache(self):
261
        """Return the revision cache limited to the revision context."""
8786.2.1 by Tim Penhey
Updates following review.
262
        raise NotImplementedError(self._getRevisionCache)
6736.3.1 by Tim Penhey
Initial work.
263
264
    def _getItemsWorker(self):
265
        """Create the list of items.
266
267
        Called by getItems which may cache the results.
268
        """
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
269
        cache = self._getRevisionCache()
8931.2.1 by Tim Penhey
Skip revisions that are no longer associated with branches.
270
        revisions = cache.public().getRevisions()
6736.3.1 by Tim Penhey
Initial work.
271
        # Convert the items into their feed entry representation.
8931.2.1 by Tim Penhey
Skip revisions that are no longer associated with branches.
272
        items = []
273
        for revision in revisions:
8931.2.2 by Tim Penhey
Updates following review.
274
            content_view = self._createView(revision)
275
            if content_view is not None:
276
                entry = self.createFeedEntry(content_view)
8931.2.1 by Tim Penhey
Skip revisions that are no longer associated with branches.
277
                items.append(entry)
278
            # If we've hit our limit, stop iterating the revisions.
279
            if len(items) >= self.quantity:
280
                break
6736.3.1 by Tim Penhey
Initial work.
281
        return items
282
8931.2.1 by Tim Penhey
Skip revisions that are no longer associated with branches.
283
    def _createView(self, revision):
284
        """Make a view for this revision.
285
286
        :return: A view class, or None.
287
        """
288
        content_view = RevisionFeedContentView(revision, self.request, self)
289
        # If there is no longer an associated branch for this, return None as
290
        # we don't want to show this revision.
291
        if content_view.branch is None:
292
            return None
293
        return content_view
294
295
    def createFeedEntry(self, content_view):
296
        """Create the FeedEntry for the specified view."""
297
        revision = content_view.context
8931.2.3 by Tim Penhey
More tests.
298
        id = revision_feed_id(revision)
6736.3.1 by Tim Penhey
Initial work.
299
        content = content_view.render()
300
        content_data = FeedTypedData(content=content,
301
                                     content_type="html",
302
                                     root_url=self.root_url)
6736.3.6 by Tim Penhey
Update the look of the feed.
303
        title = FeedTypedData(content_view.title)
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
304
        if revision.revision_author.person is None:
305
            authors = [
306
                RevisionPerson(revision.revision_author, self.rootsite)]
307
        else:
308
            authors = [
309
                FeedPerson(revision.revision_author.person, self.rootsite)]
310
311
        entry = FeedEntry(
312
            title=title,
313
            link_alternate=None,
314
            date_created=revision.revision_date,
315
            date_updated=revision.revision_date,
316
            date_published=revision.date_created,
317
            authors=authors,
6736.3.7 by Tim Penhey
Add the feed doctest.
318
            id_=id,
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
319
            content=content_data)
6736.3.1 by Tim Penhey
Initial work.
320
        return entry
321
322
323
class PersonRevisionFeed(RevisionListingFeed):
324
    """Feed for a person's revisions."""
325
326
    usedfor = IPerson
327
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
328
    @property
329
    def title(self):
330
        """See `IFeed`."""
331
        if self.context.is_team:
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
332
            return 'Latest Revisions by members of %s' % (
333
                self.context.displayname)
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
334
        else:
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
335
            return 'Latest Revisions by %s' % self.context.displayname
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
336
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
337
    def _getRevisionCache(self):
338
        """See `RevisionListingFeed`."""
339
        return getUtility(IRevisionCache).authoredBy(self.context)
6736.3.1 by Tim Penhey
Initial work.
340
341
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
342
class ProjectRevisionFeedBase(RevisionListingFeed):
343
    """Defines a common access method to get the revisions."""
344
345
    @property
346
    def title(self):
347
        """See `IFeed`."""
348
        return 'Latest Revisions for %s' % self.context.displayname
349
350
351
class ProductRevisionFeed(ProjectRevisionFeedBase):
352
    """Feed for a project's revisions."""
353
354
    usedfor = IProduct
355
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
356
    def _getRevisionCache(self):
357
        """See `RevisionListingFeed`."""
358
        return getUtility(IRevisionCache).inProduct(self.context)
6736.3.23 by Tim Penhey
Fix the feed's getRawItems method of revision acquisition.
359
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
360
361
class ProjectRevisionFeed(ProjectRevisionFeedBase):
362
    """Feed for a project's revisions."""
363
10326.1.1 by Henning Eggers
Mechanically renamed IProject* to IProjectGroup*.
364
    usedfor = IProjectGroup
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
365
8786.1.13 by Tim Penhey
Make the feeds use the revision cache.
366
    def _getRevisionCache(self):
367
        """See `RevisionListingFeed`."""
368
        return getUtility(IRevisionCache).inProject(self.context)
6736.3.23 by Tim Penhey
Fix the feed's getRawItems method of revision acquisition.
369
6736.3.16 by Tim Penhey
Add the revision feed for products and projects.
370
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
371
class RevisionPerson:
372
    """See `IFeedPerson`.
373
374
    Uses the `name_without_email` property for the display name.
375
    """
376
    implements(IFeedPerson)
377
378
    def __init__(self, person, rootsite):
379
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
380
        no_email = person.name_without_email
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
381
        if no_email:
382
            self.name = no_email
383
        else:
384
            self.name = person.name
385
        # We don't want to disclose email addresses in public feeds.
386
        self.email = None
387
        self.uri = None
388
6736.3.4 by Tim Penhey
Added tests for addition Revision and RevisionSet methods.
389
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
390
class BranchFeed(BranchFeedBase):
391
    """Feed for single branch.
392
393
    Unlike the other branch feeds, where the feed entries were the various
394
    branches for that object, the feed for a single branch has as entries the
395
    latest revisions for that branch.
396
    """
397
398
    usedfor = IBranch
399
    feedname = "branch"
400
401
    def initialize(self):
402
        """See `IFeed`."""
403
        # For a `BranchFeed` we must ensure that the branch is not private.
404
        super(BranchFeed, self).initialize()
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
405
        try:
406
            feed_allowed = not self.context.private
407
            if not feed_allowed:
408
                # We are logged in and can see the branch so redirect to the
409
                # branch index page.
13082.2.5 by Ian Booth
Code review fixes
410
                message_prefix = "This branch is private."
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
411
                redirect_url = canonical_url(self.context)
412
        except Unauthorized:
413
            # Branch cannot be seen so redirect to the code index page.
414
            feed_allowed = False
13082.2.5 by Ian Booth
Code review fixes
415
            message_prefix = "The requested branch is private."
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
416
            root = getUtility(ILaunchpadRoot)
417
            redirect_url = canonical_url(root, rootsite='code')
418
419
        if not feed_allowed:
420
            self.request.response.addErrorNotification(
13082.2.5 by Ian Booth
Code review fixes
421
                message_prefix +
422
                " Feeds do not serve private branches.")
13082.2.2 by Ian Booth
Redirect to a senisble page with an error message if attempt is made to get an rss feed for a private bug/branch
423
            self.request.response.redirect(redirect_url)
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
424
425
    @property
426
    def title(self):
427
        """See `IFeed`."""
428
        return "Latest Revisions for Branch %s" % self.context.displayname
429
430
    def _getRawItems(self):
431
        """Get the raw set of items for the feed.
432
433
        For a `BranchFeed` the items are the revisions for the branch.
434
        """
435
        branch = self.context
436
        return branch.latest_revisions(quantity=self.quantity)
437
5958.2.7 by Brad Crittenden
Changed getItemsWorker to _getItemsWorker.
438
    def _getItemsWorker(self):
5958.2.4 by Brad Crittenden
Added caching to the getItems method for Feeds.
439
        """Create the list of items.
440
441
        Called by getItems which may cache the results.
442
        """
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
443
        items = self._getRawItems()
444
        # Convert the items into their feed entry representation.
445
        items = [self.itemToFeedEntry(item) for item in items]
446
        return items
447
448
    def itemToFeedEntry(self, rev):
449
        """See `IFeed`."""
450
        title = FeedTypedData("Revision %d" % rev.sequence)
7622.1.2 by Michael Hudson
some progress
451
        url = self.context.codebrowse_url('revision', str(rev.sequence))
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
452
        content_view = BranchFeedContentView(rev, self.request, self,
453
                                             'templates/branch-revision.pt')
454
        content = FeedTypedData(content=content_view.render(),
5204.6.35 by Brad Crittenden
Correct relative hrefs in feeds. Removed duplicate branch entries.
455
                                content_type="html",
456
                                root_url=self.root_url)
5204.6.27 by Brad Crittenden
Added tests for single branch feed.
457
        entry = BranchFeedEntry(
458
            title=title,
459
            link_alternate=url,
460
            date_created=rev.revision.date_created,
461
            date_updated=rev.revision.revision_date,
462
            date_published=None,
463
            authors=[RevisionPerson(
464
                    rev.revision.revision_author,
465
                    self.rootsite)],
466
            content=content)
5204.6.26 by Brad Crittenden
Added feed for latest revisions on a given branch.
467
        return entry