Fixed #9800 -- Allow "isPermaLink" attribute in <guid> element of an RSS item.

Thanks @rtnpro for the patch!
This commit is contained in:
Simon Charette 2013-02-06 05:25:35 -05:00
parent 2390fe3f4f
commit 5449240c54
6 changed files with 74 additions and 4 deletions

View File

@ -184,6 +184,8 @@ class Feed(object):
link = link, link = link,
description = description, description = description,
unique_id = self.__get_dynamic_attr('item_guid', item, link), unique_id = self.__get_dynamic_attr('item_guid', item, link),
unique_id_is_permalink = self.__get_dynamic_attr(
'item_guid_is_permalink', item),
enclosure = enc, enclosure = enc,
pubdate = pubdate, pubdate = pubdate,
author_name = author_name, author_name = author_name,

View File

@ -113,8 +113,8 @@ class SyndicationFeed(object):
def add_item(self, title, link, description, author_email=None, def add_item(self, title, link, description, author_email=None,
author_name=None, author_link=None, pubdate=None, comments=None, author_name=None, author_link=None, pubdate=None, comments=None,
unique_id=None, enclosure=None, categories=(), item_copyright=None, unique_id=None, unique_id_is_permalink=None, enclosure=None,
ttl=None, **kwargs): categories=(), item_copyright=None, ttl=None, **kwargs):
""" """
Adds an item to the feed. All args are expected to be Python Unicode Adds an item to the feed. All args are expected to be Python Unicode
objects except pubdate, which is a datetime.datetime object, and objects except pubdate, which is a datetime.datetime object, and
@ -136,6 +136,7 @@ class SyndicationFeed(object):
'pubdate': pubdate, 'pubdate': pubdate,
'comments': to_unicode(comments), 'comments': to_unicode(comments),
'unique_id': to_unicode(unique_id), 'unique_id': to_unicode(unique_id),
'unique_id_is_permalink': unique_id_is_permalink,
'enclosure': enclosure, 'enclosure': enclosure,
'categories': categories or (), 'categories': categories or (),
'item_copyright': to_unicode(item_copyright), 'item_copyright': to_unicode(item_copyright),
@ -280,7 +281,11 @@ class Rss201rev2Feed(RssFeed):
if item['comments'] is not None: if item['comments'] is not None:
handler.addQuickElement("comments", item['comments']) handler.addQuickElement("comments", item['comments'])
if item['unique_id'] is not None: if item['unique_id'] is not None:
handler.addQuickElement("guid", item['unique_id']) guid_attrs = {}
if isinstance(item.get('unique_id_is_permalink'), bool):
guid_attrs['isPermaLink'] = str(
item['unique_id_is_permalink']).lower()
handler.addQuickElement("guid", item['unique_id'], guid_attrs)
if item['ttl'] is not None: if item['ttl'] is not None:
handler.addQuickElement("ttl", item['ttl']) handler.addQuickElement("ttl", item['ttl'])

View File

@ -624,6 +624,18 @@ This example illustrates all possible attributes and methods for a
Takes an item, as return by items(), and returns the item's ID. Takes an item, as return by items(), and returns the item's ID.
""" """
# ITEM_GUID_IS_PERMALINK -- The following method is optional. If
# provided, it sets the 'isPermaLink' attribute of an item's
# GUID element. This method is used only when 'item_guid' is
# specified.
def item_guid_is_permalink(self, obj):
"""
Takes an item, as returned by items(), and returns a boolean.
"""
item_guid_is_permalink = False # Hard coded value
# ITEM AUTHOR NAME -- One of the following three is optional. The # ITEM AUTHOR NAME -- One of the following three is optional. The
# framework looks for them in this order. # framework looks for them in this order.

View File

@ -42,6 +42,19 @@ class TestRss2Feed(views.Feed):
item_copyright = 'Copyright (c) 2007, Sally Smith' item_copyright = 'Copyright (c) 2007, Sally Smith'
class TestRss2FeedWithGuidIsPermaLinkTrue(TestRss2Feed):
def item_guid_is_permalink(self, item):
return True
class TestRss2FeedWithGuidIsPermaLinkFalse(TestRss2Feed):
def item_guid(self, item):
return str(item.pk)
def item_guid_is_permalink(self, item):
return False
class TestRss091Feed(TestRss2Feed): class TestRss091Feed(TestRss2Feed):
feed_type = feedgenerator.RssUserland091Feed feed_type = feedgenerator.RssUserland091Feed

View File

@ -103,9 +103,43 @@ class SyndicationFeedTest(FeedTestCase):
'author': 'test@example.com (Sally Smith)', 'author': 'test@example.com (Sally Smith)',
}) })
self.assertCategories(items[0], ['python', 'testing']) self.assertCategories(items[0], ['python', 'testing'])
for item in items: for item in items:
self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'category', 'pubDate', 'author']) self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'category', 'pubDate', 'author'])
# Assert that <guid> does not have any 'isPermaLink' attribute
self.assertIsNone(item.getElementsByTagName(
'guid')[0].attributes.get('isPermaLink'))
def test_rss2_feed_guid_permalink_false(self):
"""
Test if the 'isPermaLink' attribute of <guid> element of an item
in the RSS feed is 'false'.
"""
response = self.client.get(
'/syndication/rss2/guid_ispermalink_false/')
doc = minidom.parseString(response.content)
chan = doc.getElementsByTagName(
'rss')[0].getElementsByTagName('channel')[0]
items = chan.getElementsByTagName('item')
for item in items:
self.assertEqual(
item.getElementsByTagName('guid')[0].attributes.get(
'isPermaLink').value, "false")
def test_rss2_feed_guid_permalink_true(self):
"""
Test if the 'isPermaLink' attribute of <guid> element of an item
in the RSS feed is 'true'.
"""
response = self.client.get(
'/syndication/rss2/guid_ispermalink_true/')
doc = minidom.parseString(response.content)
chan = doc.getElementsByTagName(
'rss')[0].getElementsByTagName('channel')[0]
items = chan.getElementsByTagName('item')
for item in items:
self.assertEqual(
item.getElementsByTagName('guid')[0].attributes.get(
'isPermaLink').value, "true")
def test_rss091_feed(self): def test_rss091_feed(self):
""" """

View File

@ -8,6 +8,10 @@ from . import feeds
urlpatterns = patterns('django.contrib.syndication.views', urlpatterns = patterns('django.contrib.syndication.views',
(r'^syndication/complex/(?P<foo>.*)/$', feeds.ComplexFeed()), (r'^syndication/complex/(?P<foo>.*)/$', feeds.ComplexFeed()),
(r'^syndication/rss2/$', feeds.TestRss2Feed()), (r'^syndication/rss2/$', feeds.TestRss2Feed()),
(r'^syndication/rss2/guid_ispermalink_true/$',
feeds.TestRss2FeedWithGuidIsPermaLinkTrue()),
(r'^syndication/rss2/guid_ispermalink_false/$',
feeds.TestRss2FeedWithGuidIsPermaLinkFalse()),
(r'^syndication/rss091/$', feeds.TestRss091Feed()), (r'^syndication/rss091/$', feeds.TestRss091Feed()),
(r'^syndication/no_pubdate/$', feeds.TestNoPubdateFeed()), (r'^syndication/no_pubdate/$', feeds.TestNoPubdateFeed()),
(r'^syndication/atom/$', feeds.TestAtomFeed()), (r'^syndication/atom/$', feeds.TestAtomFeed()),