1. from functools import wraps
    
  2. 
    
  3. from django.contrib.syndication import views
    
  4. from django.utils import feedgenerator
    
  5. from django.utils.timezone import get_fixed_timezone
    
  6. 
    
  7. from .models import Article, Entry
    
  8. 
    
  9. 
    
  10. def wraps_decorator(f):
    
  11.     @wraps(f)
    
  12.     def wrapper(*args, **kwargs):
    
  13.         value = f(*args, **kwargs)
    
  14.         return f"{value} -- decorated by @wraps."
    
  15. 
    
  16.     return wrapper
    
  17. 
    
  18. 
    
  19. def common_decorator(f):
    
  20.     def wrapper(*args, **kwargs):
    
  21.         value = f(*args, **kwargs)
    
  22.         return f"{value} -- common decorated."
    
  23. 
    
  24.     return wrapper
    
  25. 
    
  26. 
    
  27. class TestRss2Feed(views.Feed):
    
  28.     title = "My blog"
    
  29.     description = "A more thorough description of my blog."
    
  30.     link = "/blog/"
    
  31.     feed_guid = "/foo/bar/1234"
    
  32.     author_name = "Sally Smith"
    
  33.     author_email = "[email protected]"
    
  34.     author_link = "http://www.example.com/"
    
  35.     categories = ("python", "django")
    
  36.     feed_copyright = "Copyright (c) 2007, Sally Smith"
    
  37.     ttl = 600
    
  38. 
    
  39.     def items(self):
    
  40.         return Entry.objects.all()
    
  41. 
    
  42.     def item_description(self, item):
    
  43.         return "Overridden description: %s" % item
    
  44. 
    
  45.     def item_pubdate(self, item):
    
  46.         return item.published
    
  47. 
    
  48.     def item_updateddate(self, item):
    
  49.         return item.updated
    
  50. 
    
  51.     def item_comments(self, item):
    
  52.         return "%scomments" % item.get_absolute_url()
    
  53. 
    
  54.     item_author_name = "Sally Smith"
    
  55.     item_author_email = "[email protected]"
    
  56.     item_author_link = "http://www.example.com/"
    
  57.     item_categories = ("python", "testing")
    
  58.     item_copyright = "Copyright (c) 2007, Sally Smith"
    
  59. 
    
  60. 
    
  61. class TestRss2FeedWithDecoratedMethod(TestRss2Feed):
    
  62.     class TimeToLive:
    
  63.         @wraps_decorator
    
  64.         def __call__(self):
    
  65.             return 800
    
  66. 
    
  67.     @staticmethod
    
  68.     @wraps_decorator
    
  69.     def feed_copyright():
    
  70.         return "Copyright (c) 2022, John Doe"
    
  71. 
    
  72.     ttl = TimeToLive()
    
  73. 
    
  74.     @staticmethod
    
  75.     def categories():
    
  76.         return ("javascript", "vue")
    
  77. 
    
  78.     @wraps_decorator
    
  79.     def title(self):
    
  80.         return "Overridden title"
    
  81. 
    
  82.     @wraps_decorator
    
  83.     def item_title(self, item):
    
  84.         return f"Overridden item title: {item.title}"
    
  85. 
    
  86.     @wraps_decorator
    
  87.     def description(self, obj):
    
  88.         return "Overridden description"
    
  89. 
    
  90.     @wraps_decorator
    
  91.     def item_description(self):
    
  92.         return "Overridden item description"
    
  93. 
    
  94. 
    
  95. class TestRss2FeedWithWrongDecoratedMethod(TestRss2Feed):
    
  96.     @common_decorator
    
  97.     def item_description(self, item):
    
  98.         return f"Overridden item description: {item.title}"
    
  99. 
    
  100. 
    
  101. class TestRss2FeedWithGuidIsPermaLinkTrue(TestRss2Feed):
    
  102.     def item_guid_is_permalink(self, item):
    
  103.         return True
    
  104. 
    
  105. 
    
  106. class TestRss2FeedWithGuidIsPermaLinkFalse(TestRss2Feed):
    
  107.     def item_guid(self, item):
    
  108.         return str(item.pk)
    
  109. 
    
  110.     def item_guid_is_permalink(self, item):
    
  111.         return False
    
  112. 
    
  113. 
    
  114. class TestRss091Feed(TestRss2Feed):
    
  115.     feed_type = feedgenerator.RssUserland091Feed
    
  116. 
    
  117. 
    
  118. class TestNoPubdateFeed(views.Feed):
    
  119.     title = "Test feed"
    
  120.     link = "/feed/"
    
  121. 
    
  122.     def items(self):
    
  123.         return Entry.objects.all()
    
  124. 
    
  125. 
    
  126. class TestAtomFeed(TestRss2Feed):
    
  127.     feed_type = feedgenerator.Atom1Feed
    
  128.     subtitle = TestRss2Feed.description
    
  129. 
    
  130. 
    
  131. class TestLatestFeed(TestRss2Feed):
    
  132.     """
    
  133.     A feed where the latest entry date is an `updated` element.
    
  134.     """
    
  135. 
    
  136.     feed_type = feedgenerator.Atom1Feed
    
  137.     subtitle = TestRss2Feed.description
    
  138. 
    
  139.     def items(self):
    
  140.         return Entry.objects.exclude(title="My last entry")
    
  141. 
    
  142. 
    
  143. class ArticlesFeed(TestRss2Feed):
    
  144.     """
    
  145.     A feed to test no link being defined. Articles have no get_absolute_url()
    
  146.     method, and item_link() is not defined.
    
  147.     """
    
  148. 
    
  149.     def items(self):
    
  150.         return Article.objects.all()
    
  151. 
    
  152. 
    
  153. class TestSingleEnclosureRSSFeed(TestRss2Feed):
    
  154.     """
    
  155.     A feed to test that RSS feeds work with a single enclosure.
    
  156.     """
    
  157. 
    
  158.     def item_enclosure_url(self, item):
    
  159.         return "http://example.com"
    
  160. 
    
  161.     def item_enclosure_size(self, item):
    
  162.         return 0
    
  163. 
    
  164.     def item_mime_type(self, item):
    
  165.         return "image/png"
    
  166. 
    
  167. 
    
  168. class TestMultipleEnclosureRSSFeed(TestRss2Feed):
    
  169.     """
    
  170.     A feed to test that RSS feeds raise an exception with multiple enclosures.
    
  171.     """
    
  172. 
    
  173.     def item_enclosures(self, item):
    
  174.         return [
    
  175.             feedgenerator.Enclosure("http://example.com/hello.png", 0, "image/png"),
    
  176.             feedgenerator.Enclosure("http://example.com/goodbye.png", 0, "image/png"),
    
  177.         ]
    
  178. 
    
  179. 
    
  180. class TemplateFeed(TestRss2Feed):
    
  181.     """
    
  182.     A feed to test defining item titles and descriptions with templates.
    
  183.     """
    
  184. 
    
  185.     title_template = "syndication/title.html"
    
  186.     description_template = "syndication/description.html"
    
  187. 
    
  188.     # Defining a template overrides any item_title definition
    
  189.     def item_title(self):
    
  190.         return "Not in a template"
    
  191. 
    
  192. 
    
  193. class TemplateContextFeed(TestRss2Feed):
    
  194.     """
    
  195.     A feed to test custom context data in templates for title or description.
    
  196.     """
    
  197. 
    
  198.     title_template = "syndication/title_context.html"
    
  199.     description_template = "syndication/description_context.html"
    
  200. 
    
  201.     def get_context_data(self, **kwargs):
    
  202.         context = super().get_context_data(**kwargs)
    
  203.         context["foo"] = "bar"
    
  204.         return context
    
  205. 
    
  206. 
    
  207. class TestLanguageFeed(TestRss2Feed):
    
  208.     language = "de"
    
  209. 
    
  210. 
    
  211. class TestGetObjectFeed(TestRss2Feed):
    
  212.     def get_object(self, request, entry_id):
    
  213.         return Entry.objects.get(pk=entry_id)
    
  214. 
    
  215.     def items(self, obj):
    
  216.         return Article.objects.filter(entry=obj)
    
  217. 
    
  218.     def item_link(self, item):
    
  219.         return "%sarticle/%s/" % (item.entry.get_absolute_url(), item.pk)
    
  220. 
    
  221.     def item_comments(self, item):
    
  222.         return "%scomments" % self.item_link(item)
    
  223. 
    
  224.     def item_description(self, item):
    
  225.         return "Article description: %s" % item.title
    
  226. 
    
  227.     def item_title(self, item):
    
  228.         return "Title: %s" % item.title
    
  229. 
    
  230. 
    
  231. class NaiveDatesFeed(TestAtomFeed):
    
  232.     """
    
  233.     A feed with naive (non-timezone-aware) dates.
    
  234.     """
    
  235. 
    
  236.     def item_pubdate(self, item):
    
  237.         return item.published
    
  238. 
    
  239. 
    
  240. class TZAwareDatesFeed(TestAtomFeed):
    
  241.     """
    
  242.     A feed with timezone-aware dates.
    
  243.     """
    
  244. 
    
  245.     def item_pubdate(self, item):
    
  246.         # Provide a weird offset so that the test can know it's getting this
    
  247.         # specific offset and not accidentally getting on from
    
  248.         # settings.TIME_ZONE.
    
  249.         return item.published.replace(tzinfo=get_fixed_timezone(42))
    
  250. 
    
  251. 
    
  252. class TestFeedUrlFeed(TestAtomFeed):
    
  253.     feed_url = "http://example.com/customfeedurl/"
    
  254. 
    
  255. 
    
  256. class MyCustomAtom1Feed(feedgenerator.Atom1Feed):
    
  257.     """
    
  258.     Test of a custom feed generator class.
    
  259.     """
    
  260. 
    
  261.     def root_attributes(self):
    
  262.         attrs = super().root_attributes()
    
  263.         attrs["django"] = "rocks"
    
  264.         return attrs
    
  265. 
    
  266.     def add_root_elements(self, handler):
    
  267.         super().add_root_elements(handler)
    
  268.         handler.addQuickElement("spam", "eggs")
    
  269. 
    
  270.     def item_attributes(self, item):
    
  271.         attrs = super().item_attributes(item)
    
  272.         attrs["bacon"] = "yum"
    
  273.         return attrs
    
  274. 
    
  275.     def add_item_elements(self, handler, item):
    
  276.         super().add_item_elements(handler, item)
    
  277.         handler.addQuickElement("ministry", "silly walks")
    
  278. 
    
  279. 
    
  280. class TestCustomFeed(TestAtomFeed):
    
  281.     feed_type = MyCustomAtom1Feed
    
  282. 
    
  283. 
    
  284. class TestSingleEnclosureAtomFeed(TestAtomFeed):
    
  285.     """
    
  286.     A feed to test that Atom feeds work with a single enclosure.
    
  287.     """
    
  288. 
    
  289.     def item_enclosure_url(self, item):
    
  290.         return "http://example.com"
    
  291. 
    
  292.     def item_enclosure_size(self, item):
    
  293.         return 0
    
  294. 
    
  295.     def item_mime_type(self, item):
    
  296.         return "image/png"
    
  297. 
    
  298. 
    
  299. class TestMultipleEnclosureAtomFeed(TestAtomFeed):
    
  300.     """
    
  301.     A feed to test that Atom feeds work with multiple enclosures.
    
  302.     """
    
  303. 
    
  304.     def item_enclosures(self, item):
    
  305.         return [
    
  306.             feedgenerator.Enclosure("http://example.com/hello.png", "0", "image/png"),
    
  307.             feedgenerator.Enclosure("http://example.com/goodbye.png", "0", "image/png"),
    
  308.         ]