From 5449240c548bb6877923791d02e800c6b25393f5 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Wed, 6 Feb 2013 05:25:35 -0500 Subject: [PATCH] Fixed #9800 -- Allow "isPermaLink" attribute in element of an RSS item. Thanks @rtnpro for the patch! --- django/contrib/syndication/views.py | 2 ++ django/utils/feedgenerator.py | 11 +++++-- docs/ref/contrib/syndication.txt | 12 ++++++++ tests/regressiontests/syndication/feeds.py | 13 ++++++++ tests/regressiontests/syndication/tests.py | 36 +++++++++++++++++++++- tests/regressiontests/syndication/urls.py | 4 +++ 6 files changed, 74 insertions(+), 4 deletions(-) diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py index 996b7dfb40..a80b9d1fae 100644 --- a/django/contrib/syndication/views.py +++ b/django/contrib/syndication/views.py @@ -184,6 +184,8 @@ class Feed(object): link = link, description = description, 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, pubdate = pubdate, author_name = author_name, diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py index f9126a6782..7eba842a89 100644 --- a/django/utils/feedgenerator.py +++ b/django/utils/feedgenerator.py @@ -113,8 +113,8 @@ class SyndicationFeed(object): def add_item(self, title, link, description, author_email=None, author_name=None, author_link=None, pubdate=None, comments=None, - unique_id=None, enclosure=None, categories=(), item_copyright=None, - ttl=None, **kwargs): + unique_id=None, unique_id_is_permalink=None, enclosure=None, + categories=(), item_copyright=None, ttl=None, **kwargs): """ Adds an item to the feed. All args are expected to be Python Unicode objects except pubdate, which is a datetime.datetime object, and @@ -136,6 +136,7 @@ class SyndicationFeed(object): 'pubdate': pubdate, 'comments': to_unicode(comments), 'unique_id': to_unicode(unique_id), + 'unique_id_is_permalink': unique_id_is_permalink, 'enclosure': enclosure, 'categories': categories or (), 'item_copyright': to_unicode(item_copyright), @@ -280,7 +281,11 @@ class Rss201rev2Feed(RssFeed): if item['comments'] is not None: handler.addQuickElement("comments", item['comments']) 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: handler.addQuickElement("ttl", item['ttl']) diff --git a/docs/ref/contrib/syndication.txt b/docs/ref/contrib/syndication.txt index 2955d7dad3..65aa7b57b4 100644 --- a/docs/ref/contrib/syndication.txt +++ b/docs/ref/contrib/syndication.txt @@ -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. """ + # 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 # framework looks for them in this order. diff --git a/tests/regressiontests/syndication/feeds.py b/tests/regressiontests/syndication/feeds.py index 04a67f4bdb..25757057b9 100644 --- a/tests/regressiontests/syndication/feeds.py +++ b/tests/regressiontests/syndication/feeds.py @@ -42,6 +42,19 @@ class TestRss2Feed(views.Feed): 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): feed_type = feedgenerator.RssUserland091Feed diff --git a/tests/regressiontests/syndication/tests.py b/tests/regressiontests/syndication/tests.py index 10413b4ddd..8885dc28c0 100644 --- a/tests/regressiontests/syndication/tests.py +++ b/tests/regressiontests/syndication/tests.py @@ -103,9 +103,43 @@ class SyndicationFeedTest(FeedTestCase): 'author': 'test@example.com (Sally Smith)', }) self.assertCategories(items[0], ['python', 'testing']) - for item in items: self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'category', 'pubDate', 'author']) + # Assert that 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 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 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): """ diff --git a/tests/regressiontests/syndication/urls.py b/tests/regressiontests/syndication/urls.py index 57f9d81a73..ec3c8cc596 100644 --- a/tests/regressiontests/syndication/urls.py +++ b/tests/regressiontests/syndication/urls.py @@ -8,6 +8,10 @@ from . import feeds urlpatterns = patterns('django.contrib.syndication.views', (r'^syndication/complex/(?P.*)/$', feeds.ComplexFeed()), (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/no_pubdate/$', feeds.TestNoPubdateFeed()), (r'^syndication/atom/$', feeds.TestAtomFeed()),