Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

views.py 9.1KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from inspect import getattr_static, unwrap
  2. from django.contrib.sites.shortcuts import get_current_site
  3. from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
  4. from django.http import Http404, HttpResponse
  5. from django.template import TemplateDoesNotExist, loader
  6. from django.utils import feedgenerator
  7. from django.utils.encoding import iri_to_uri
  8. from django.utils.html import escape
  9. from django.utils.http import http_date
  10. from django.utils.timezone import get_default_timezone, is_naive, make_aware
  11. from django.utils.translation import get_language
  12. def add_domain(domain, url, secure=False):
  13. protocol = "https" if secure else "http"
  14. if url.startswith("//"):
  15. # Support network-path reference (see #16753) - RSS requires a protocol
  16. url = "%s:%s" % (protocol, url)
  17. elif not url.startswith(("http://", "https://", "mailto:")):
  18. url = iri_to_uri("%s://%s%s" % (protocol, domain, url))
  19. return url
  20. class FeedDoesNotExist(ObjectDoesNotExist):
  21. pass
  22. class Feed:
  23. feed_type = feedgenerator.DefaultFeed
  24. title_template = None
  25. description_template = None
  26. language = None
  27. def __call__(self, request, *args, **kwargs):
  28. try:
  29. obj = self.get_object(request, *args, **kwargs)
  30. except ObjectDoesNotExist:
  31. raise Http404("Feed object does not exist.")
  32. feedgen = self.get_feed(obj, request)
  33. response = HttpResponse(content_type=feedgen.content_type)
  34. if hasattr(self, "item_pubdate") or hasattr(self, "item_updateddate"):
  35. # if item_pubdate or item_updateddate is defined for the feed, set
  36. # header so as ConditionalGetMiddleware is able to send 304 NOT MODIFIED
  37. response.headers["Last-Modified"] = http_date(
  38. feedgen.latest_post_date().timestamp()
  39. )
  40. feedgen.write(response, "utf-8")
  41. return response
  42. def item_title(self, item):
  43. # Titles should be double escaped by default (see #6533)
  44. return escape(str(item))
  45. def item_description(self, item):
  46. return str(item)
  47. def item_link(self, item):
  48. try:
  49. return item.get_absolute_url()
  50. except AttributeError:
  51. raise ImproperlyConfigured(
  52. "Give your %s class a get_absolute_url() method, or define an "
  53. "item_link() method in your Feed class." % item.__class__.__name__
  54. )
  55. def item_enclosures(self, item):
  56. enc_url = self._get_dynamic_attr("item_enclosure_url", item)
  57. if enc_url:
  58. enc = feedgenerator.Enclosure(
  59. url=str(enc_url),
  60. length=str(self._get_dynamic_attr("item_enclosure_length", item)),
  61. mime_type=str(self._get_dynamic_attr("item_enclosure_mime_type", item)),
  62. )
  63. return [enc]
  64. return []
  65. def _get_dynamic_attr(self, attname, obj, default=None):
  66. try:
  67. attr = getattr(self, attname)
  68. except AttributeError:
  69. return default
  70. if callable(attr):
  71. # Check co_argcount rather than try/excepting the function and
  72. # catching the TypeError, because something inside the function
  73. # may raise the TypeError. This technique is more accurate.
  74. func = unwrap(attr)
  75. try:
  76. code = func.__code__
  77. except AttributeError:
  78. func = unwrap(attr.__call__)
  79. code = func.__code__
  80. # If function doesn't have arguments and it is not a static method,
  81. # it was decorated without using @functools.wraps.
  82. if not code.co_argcount and not isinstance(
  83. getattr_static(self, func.__name__, None), staticmethod
  84. ):
  85. raise ImproperlyConfigured(
  86. f"Feed method {attname!r} decorated by {func.__name__!r} needs to "
  87. f"use @functools.wraps."
  88. )
  89. if code.co_argcount == 2: # one argument is 'self'
  90. return attr(obj)
  91. else:
  92. return attr()
  93. return attr
  94. def feed_extra_kwargs(self, obj):
  95. """
  96. Return an extra keyword arguments dictionary that is used when
  97. initializing the feed generator.
  98. """
  99. return {}
  100. def item_extra_kwargs(self, item):
  101. """
  102. Return an extra keyword arguments dictionary that is used with
  103. the `add_item` call of the feed generator.
  104. """
  105. return {}
  106. def get_object(self, request, *args, **kwargs):
  107. return None
  108. def get_context_data(self, **kwargs):
  109. """
  110. Return a dictionary to use as extra context if either
  111. ``self.description_template`` or ``self.item_template`` are used.
  112. Default implementation preserves the old behavior
  113. of using {'obj': item, 'site': current_site} as the context.
  114. """
  115. return {"obj": kwargs.get("item"), "site": kwargs.get("site")}
  116. def get_feed(self, obj, request):
  117. """
  118. Return a feedgenerator.DefaultFeed object, fully populated, for
  119. this feed. Raise FeedDoesNotExist for invalid parameters.
  120. """
  121. current_site = get_current_site(request)
  122. link = self._get_dynamic_attr("link", obj)
  123. link = add_domain(current_site.domain, link, request.is_secure())
  124. feed = self.feed_type(
  125. title=self._get_dynamic_attr("title", obj),
  126. subtitle=self._get_dynamic_attr("subtitle", obj),
  127. link=link,
  128. description=self._get_dynamic_attr("description", obj),
  129. language=self.language or get_language(),
  130. feed_url=add_domain(
  131. current_site.domain,
  132. self._get_dynamic_attr("feed_url", obj) or request.path,
  133. request.is_secure(),
  134. ),
  135. author_name=self._get_dynamic_attr("author_name", obj),
  136. author_link=self._get_dynamic_attr("author_link", obj),
  137. author_email=self._get_dynamic_attr("author_email", obj),
  138. categories=self._get_dynamic_attr("categories", obj),
  139. feed_copyright=self._get_dynamic_attr("feed_copyright", obj),
  140. feed_guid=self._get_dynamic_attr("feed_guid", obj),
  141. ttl=self._get_dynamic_attr("ttl", obj),
  142. **self.feed_extra_kwargs(obj),
  143. )
  144. title_tmp = None
  145. if self.title_template is not None:
  146. try:
  147. title_tmp = loader.get_template(self.title_template)
  148. except TemplateDoesNotExist:
  149. pass
  150. description_tmp = None
  151. if self.description_template is not None:
  152. try:
  153. description_tmp = loader.get_template(self.description_template)
  154. except TemplateDoesNotExist:
  155. pass
  156. for item in self._get_dynamic_attr("items", obj):
  157. context = self.get_context_data(
  158. item=item, site=current_site, obj=obj, request=request
  159. )
  160. if title_tmp is not None:
  161. title = title_tmp.render(context, request)
  162. else:
  163. title = self._get_dynamic_attr("item_title", item)
  164. if description_tmp is not None:
  165. description = description_tmp.render(context, request)
  166. else:
  167. description = self._get_dynamic_attr("item_description", item)
  168. link = add_domain(
  169. current_site.domain,
  170. self._get_dynamic_attr("item_link", item),
  171. request.is_secure(),
  172. )
  173. enclosures = self._get_dynamic_attr("item_enclosures", item)
  174. author_name = self._get_dynamic_attr("item_author_name", item)
  175. if author_name is not None:
  176. author_email = self._get_dynamic_attr("item_author_email", item)
  177. author_link = self._get_dynamic_attr("item_author_link", item)
  178. else:
  179. author_email = author_link = None
  180. tz = get_default_timezone()
  181. pubdate = self._get_dynamic_attr("item_pubdate", item)
  182. if pubdate and is_naive(pubdate):
  183. pubdate = make_aware(pubdate, tz)
  184. updateddate = self._get_dynamic_attr("item_updateddate", item)
  185. if updateddate and is_naive(updateddate):
  186. updateddate = make_aware(updateddate, tz)
  187. feed.add_item(
  188. title=title,
  189. link=link,
  190. description=description,
  191. unique_id=self._get_dynamic_attr("item_guid", item, link),
  192. unique_id_is_permalink=self._get_dynamic_attr(
  193. "item_guid_is_permalink", item
  194. ),
  195. enclosures=enclosures,
  196. pubdate=pubdate,
  197. updateddate=updateddate,
  198. author_name=author_name,
  199. author_email=author_email,
  200. author_link=author_link,
  201. comments=self._get_dynamic_attr("item_comments", item),
  202. categories=self._get_dynamic_attr("item_categories", item),
  203. item_copyright=self._get_dynamic_attr("item_copyright", item),
  204. **self.item_extra_kwargs(item),
  205. )
  206. return feed