Content warnings.
[wolnelektury.git] / src / catalogue / templatetags / catalogue_tags.py
index 9f5c04b..25976db 100644 (file)
@@ -1,23 +1,22 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from random import randint, random
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from random import randint, random
-from urlparse import urlparse
+from urllib.parse import urlparse
 from django.contrib.contenttypes.models import ContentType
 
 from django.conf import settings
 from django import template
 from django.template import Node, Variable, Template, Context
 from django.contrib.contenttypes.models import ContentType
 
 from django.conf import settings
 from django import template
 from django.template import Node, Variable, Template, Context
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django.utils.cache import add_never_cache_headers
 from django.utils.cache import add_never_cache_headers
+from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext as _
 
 from django.utils.translation import ugettext as _
 
-from ssify import ssi_variable
-
 from catalogue.helpers import get_audiobook_tags
 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
 from catalogue.constants import LICENSES
 from catalogue.helpers import get_audiobook_tags
 from catalogue.models import Book, BookMedia, Fragment, Tag, Source
 from catalogue.constants import LICENSES
+from club.models import Membership
 from picture.models import Picture
 
 register = template.Library()
 from picture.models import Picture
 
 register = template.Library()
@@ -43,8 +42,8 @@ def html_title_from_tags(tags):
     if len(tags) < 2:
         return title_from_tags(tags)
     template = Template("{{ category }}: <a href='{{ tag.get_absolute_url }}'>{{ tag.name }}</a>")
     if len(tags) < 2:
         return title_from_tags(tags)
     template = Template("{{ category }}: <a href='{{ tag.get_absolute_url }}'>{{ tag.name }}</a>")
-    return capfirst(",<br/>".join(
-        template.render(Context({'tag': tag, 'category': _(tag.category)})) for tag in tags))
+    return mark_safe(capfirst(",<br/>".join(
+        template.render(Context({'tag': tag, 'category': _(tag.category)})) for tag in tags)))
 
 
 def simple_title(tags):
 
 
 def simple_title(tags):
@@ -56,7 +55,7 @@ def simple_title(tags):
 
 @register.simple_tag
 def book_title(book, html_links=False):
 
 @register.simple_tag
 def book_title(book, html_links=False):
-    return book.pretty_title(html_links)
+    return mark_safe(book.pretty_title(html_links))
 
 
 @register.simple_tag
 
 
 @register.simple_tag
@@ -82,48 +81,48 @@ def title_from_tags(tags):
 
     self = split_tags(tags)
 
 
     self = split_tags(tags)
 
-    title = u''
+    title = ''
 
     # Specjalny przypadek oglądania wszystkich lektur na danej półce
     if len(self) == 1 and 'set' in self:
 
     # Specjalny przypadek oglądania wszystkich lektur na danej półce
     if len(self) == 1 and 'set' in self:
-        return u'Półka %s' % self['set']
+        return 'Półka %s' % self['set']
 
     # Specjalny przypadek "Twórczość w pozytywizmie", wtedy gdy tylko epoka
     # jest wybrana przez użytkownika
     if 'epoch' in self and len(self) == 1:
 
     # Specjalny przypadek "Twórczość w pozytywizmie", wtedy gdy tylko epoka
     # jest wybrana przez użytkownika
     if 'epoch' in self and len(self) == 1:
-        text = u'Twórczość w %s' % flection.get_case(unicode(self['epoch']), u'miejscownik')
+        text = 'Twórczość w %s' % flection.get_case(str(self['epoch']), 'miejscownik')
         return capfirst(text)
 
     # Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
     # są tylko rodzaj literacki i autor
     if 'kind' in self and 'author' in self and len(self) == 2:
         return capfirst(text)
 
     # Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
     # są tylko rodzaj literacki i autor
     if 'kind' in self and 'author' in self and len(self) == 2:
-        text = u'%s w twórczości %s' % (
-            unicode(self['kind']), flection.get_case(unicode(self['author']), u'dopełniacz'))
+        text = '%s w twórczości %s' % (
+            str(self['kind']), flection.get_case(str(self['author']), 'dopełniacz'))
         return capfirst(text)
 
     # Przypadki ogólniejsze
     if 'theme' in self:
         return capfirst(text)
 
     # Przypadki ogólniejsze
     if 'theme' in self:
-        title += u'Motyw %s' % unicode(self['theme'])
+        title += 'Motyw %s' % str(self['theme'])
 
     if 'genre' in self:
         if 'theme' in self:
 
     if 'genre' in self:
         if 'theme' in self:
-            title += u' w %s' % flection.get_case(unicode(self['genre']), u'miejscownik')
+            title += ' w %s' % flection.get_case(str(self['genre']), 'miejscownik')
         else:
         else:
-            title += unicode(self['genre'])
+            title += str(self['genre'])
 
     if 'kind' in self or 'author' in self or 'epoch' in self:
         if 'genre' in self or 'theme' in self:
             if 'kind' in self:
 
     if 'kind' in self or 'author' in self or 'epoch' in self:
         if 'genre' in self or 'theme' in self:
             if 'kind' in self:
-                title += u' w %s ' % flection.get_case(unicode(self['kind']), u'miejscownik')
+                title += ' w %s ' % flection.get_case(str(self['kind']), 'miejscownik')
             else:
             else:
-                title += u' w twórczości '
+                title += ' w twórczości '
         else:
         else:
-            title += u'%s ' % unicode(self.get('kind', u'twórczość'))
+            title += '%s ' % str(self.get('kind', 'twórczość'))
 
     if 'author' in self:
 
     if 'author' in self:
-        title += flection.get_case(unicode(self['author']), u'dopełniacz')
+        title += flection.get_case(str(self['author']), 'dopełniacz')
     elif 'epoch' in self:
     elif 'epoch' in self:
-        title += flection.get_case(unicode(self['epoch']), u'dopełniacz')
+        title += flection.get_case(str(self['epoch']), 'dopełniacz')
 
     return capfirst(title)
 
 
     return capfirst(title)
 
@@ -135,7 +134,7 @@ def book_tree(book_list, books_by_parent):
         ) for book in book_list)
 
     if text:
         ) for book in book_list)
 
     if text:
-        return "<ol>%s</ol>" % text
+        return mark_safe("<ol>%s</ol>" % text)
     else:
         return ''
 
     else:
         return ''
 
@@ -148,14 +147,14 @@ def audiobook_tree(book_list, books_by_parent):
     ) for book in book_list)
 
     if text:
     ) for book in book_list)
 
     if text:
-        return "<ol>%s</ol>" % text
+        return mark_safe("<ol>%s</ol>" % text)
     else:
         return ''
 
 
 @register.simple_tag
 def book_tree_texml(book_list, books_by_parent, depth=1):
     else:
         return ''
 
 
 @register.simple_tag
 def book_tree_texml(book_list, books_by_parent, depth=1):
-    return "".join("""
+    return mark_safe("".join("""
             <cmd name='hspace'><parm>%(depth)dem</parm></cmd>%(title)s
             <spec cat='align' /><cmd name="note"><parm>%(audiences)s</parm></cmd>
             <spec cat='align' /><cmd name="note"><parm>%(audiobook)s</parm></cmd>
             <cmd name='hspace'><parm>%(depth)dem</parm></cmd>%(title)s
             <spec cat='align' /><cmd name="note"><parm>%(audiences)s</parm></cmd>
             <spec cat='align' /><cmd name="note"><parm>%(audiobook)s</parm></cmd>
@@ -167,7 +166,7 @@ def book_tree_texml(book_list, books_by_parent, depth=1):
                 "audiences": ", ".join(book.audiences_pl()),
                 "audiobook": "audiobook" if book.has_media('mp3') else "",
                 "children": book_tree_texml(books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
                 "audiences": ", ".join(book.audiences_pl()),
                 "audiobook": "audiobook" if book.has_media('mp3') else "",
                 "children": book_tree_texml(books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
-            } for book in book_list)
+            } for book in book_list))
 
 
 @register.simple_tag
 
 
 @register.simple_tag
@@ -180,7 +179,7 @@ def book_tree_csv(author, book_list, books_by_parent, depth=1, max_depth=3, deli
         except ValueError:
             return s
 
         except ValueError:
             return s
 
-    return "".join("""%(author)s%(d)s%(preindent)s%(title)s%(d)s%(postindent)s%(audiences)s%(d)s%(audiobook)s
+    return mark_safe("".join("""%(author)s%(d)s%(preindent)s%(title)s%(d)s%(postindent)s%(audiences)s%(d)s%(audiobook)s
 %(children)s""" % {
                 "d": delimeter,
                 "preindent": delimeter * (depth - 1),
 %(children)s""" % {
                 "d": delimeter,
                 "preindent": delimeter * (depth - 1),
@@ -191,7 +190,7 @@ def book_tree_csv(author, book_list, books_by_parent, depth=1, max_depth=3, deli
                 "audiences": ", ".join(book.audiences_pl()),
                 "audiobook": "audiobook" if book.has_media('mp3') else "",
                 "children": book_tree_csv(author, books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
                 "audiences": ", ".join(book.audiences_pl()),
                 "audiobook": "audiobook" if book.has_media('mp3') else "",
                 "children": book_tree_csv(author, books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
-            } for book in book_list)
+            } for book in book_list))
 
 
 @register.simple_tag
 
 
 @register.simple_tag
@@ -276,7 +275,6 @@ class CatalogueURLNode(Node):
 
 # @register.inclusion_tag('catalogue/tag_list.html')
 def tag_list(tags, choices=None, category=None, list_type='books'):
 
 # @register.inclusion_tag('catalogue/tag_list.html')
 def tag_list(tags, choices=None, category=None, list_type='books'):
-    # print(tags, choices, category)
     if choices is None:
         choices = []
 
     if choices is None:
         choices = []
 
@@ -381,7 +379,7 @@ def related_books(context, instance, limit=6, random=1, taken=0):
         # Reserve one spot for an image.
         max_books -= 1
 
         # Reserve one spot for an image.
         max_books -= 1
 
-    books_qs = Book.objects.all()
+    books_qs = Book.objects.filter(findable=True)
     if not is_picture:
         books_qs = books_qs.exclude(common_slug=instance.common_slug).exclude(ancestor=instance)
     books = Book.tagged.related_to(instance, books_qs)[:max_books]
     if not is_picture:
         books_qs = books_qs.exclude(common_slug=instance.common_slug).exclude(ancestor=instance)
     books = Book.tagged.related_to(instance, books_qs)[:max_books]
@@ -414,7 +412,7 @@ def download_audio(book, daisy=True, mp3=True):
     if daisy and book.has_media('daisy'):
         for dsy in book.get_media('daisy'):
             links.append("<a href='%s'>%s</a>" % (dsy.file.url, BookMedia.formats['daisy'].name))
     if daisy and book.has_media('daisy'):
         for dsy in book.get_media('daisy'):
             links.append("<a href='%s'>%s</a>" % (dsy.file.url, BookMedia.formats['daisy'].name))
-    return "".join(links)
+    return mark_safe("".join(links))
 
 
 @register.inclusion_tag("catalogue/snippets/custom_pdf_link_li.html")
 
 
 @register.inclusion_tag("catalogue/snippets/custom_pdf_link_li.html")
@@ -455,35 +453,32 @@ def source_name(url):
     return source.name or netloc
 
 
     return source.name or netloc
 
 
-@ssi_variable(register, patch_response=[add_never_cache_headers])
-def catalogue_random_book(request, exclude_ids):
+@register.simple_tag
+def catalogue_random_book(exclude_ids):
     from .. import app_settings
     if random() < app_settings.RELATED_RANDOM_PICTURE_CHANCE:
         return None
     from .. import app_settings
     if random() < app_settings.RELATED_RANDOM_PICTURE_CHANCE:
         return None
-    queryset = Book.objects.exclude(pk__in=exclude_ids)
+    queryset = Book.objects.filter(findable=True).exclude(pk__in=exclude_ids)
     count = queryset.count()
     if count:
     count = queryset.count()
     if count:
-        return queryset[randint(0, count - 1)].pk
+        return queryset[randint(0, count - 1)]
     else:
         return None
 
 
     else:
         return None
 
 
-@ssi_variable(register, patch_response=[add_never_cache_headers])
-def choose_fragment(request, book_id=None, tag_ids=None, unless=False):
-    if unless:
-        return None
-
-    if book_id is not None:
-        fragment = Book.objects.get(pk=book_id).choose_fragment()
+@register.simple_tag
+def choose_fragment(book=None, tag_ids=None):
+    if book is not None:
+        fragment = book.choose_fragment()
     else:
         if tag_ids is not None:
             tags = Tag.objects.filter(pk__in=tag_ids)
     else:
         if tag_ids is not None:
             tags = Tag.objects.filter(pk__in=tag_ids)
-            fragments = Fragment.tagged.with_all(tags).order_by().only('id')
+            fragments = Fragment.tagged.with_all(tags).filter(book__findable=True).order_by().only('id')
         else:
         else:
-            fragments = Fragment.objects.all().order_by().only('id')
+            fragments = Fragment.objects.filter(book__findable=True).order_by().only('id')
         fragment_count = fragments.count()
         fragment = fragments[randint(0, fragment_count - 1)] if fragment_count else None
         fragment_count = fragments.count()
         fragment = fragments[randint(0, fragment_count - 1)] if fragment_count else None
-    return fragment.pk if fragment is not None else None
+    return fragment
 
 
 @register.filter
 
 
 @register.filter
@@ -491,3 +486,28 @@ def strip_tag(html, tag_name):
     # docelowo może być warto zainstalować BeautifulSoup do takich rzeczy
     import re
     return re.sub(r"<.?%s\b[^>]*>" % tag_name, "", html)
     # docelowo może być warto zainstalować BeautifulSoup do takich rzeczy
     import re
     return re.sub(r"<.?%s\b[^>]*>" % tag_name, "", html)
+
+
+@register.filter
+def status(book, user):
+    if not book.preview:
+        return 'open'
+    elif Membership.is_active_for(user):
+        return 'preview'
+    else:
+        return 'closed'
+
+
+@register.inclusion_tag('catalogue/snippets/content_warning.html')
+def content_warning(book):
+    warnings_def = {
+        'wulgaryzmy': _('vulgar language'),
+    }
+    warnings = book.get_extra_info_json().get('content_warnings')
+    warnings = sorted(
+        warnings_def.get(w, w)
+        for w in warnings
+    )
+    return {
+        "warnings": warnings
+    }