Embargo links hidden.
authorRadek Czajka <rczajka@rczajka.pl>
Wed, 26 Jun 2019 09:07:10 +0000 (11:07 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Wed, 26 Jun 2019 09:07:10 +0000 (11:07 +0200)
src/catalogue/admin.py
src/catalogue/api/fields.py
src/catalogue/api/serializers.py
src/catalogue/api/views.py
src/catalogue/migrations/0026_book_preview_key.py [new file with mode: 0644]
src/catalogue/models/book.py
src/catalogue/models/bookmedia.py
src/catalogue/urls.py
src/catalogue/views.py

index 93a265a..44b6fb7 100644 (file)
@@ -1,4 +1,3 @@
-# -*- 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.
 #
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -30,8 +29,11 @@ class MediaInline(admin.TabularInline):
     extra = 0
 
 
     extra = 0
 
 
-class BookAdmin(TaggableModelAdmin):
-    tag_model = Tag
+#FIXME: Taggable admin is broken.
+#class BookAdmin(TaggableModelAdmin):
+class BookAdmin(admin.ModelAdmin):
+    #tag_model = Tag
+    #form = TaggableModelForm
 
     list_display = ('title', 'slug', 'created_at', 'has_epub_file', 'has_html_file', 'has_description',)
     search_fields = ('title',)
 
     list_display = ('title', 'slug', 'created_at', 'has_epub_file', 'has_html_file', 'has_description',)
     search_fields = ('title',)
@@ -39,20 +41,11 @@ class BookAdmin(TaggableModelAdmin):
 
     inlines = [MediaInline]
 
 
     inlines = [MediaInline]
 
-    def change_view(self, request, object_id, extra_context=None):
-        if 'advanced' not in request.GET:
-            self.form = forms.ModelForm
-            self.fields = ('title', 'description', 'wiki_link', 'recommended')
-            self.readonly_fields = ('title',)
-        else:
-            self.form = TaggableModelForm
-            self.fields = None
-            self.readonly_fields = ()
-        return super(BookAdmin, self).change_view(request, object_id, extra_context=extra_context)
 
 
-
-class FragmentAdmin(TaggableModelAdmin):
-    tag_model = Tag
+#FIXME: Taggable admin is broken.
+#class FragmentAdmin(TaggableModelAdmin):
+class FragmentAdmin(admin.ModelAdmin):
+    #tag_model = Tag
 
     list_display = ('book', 'anchor',)
     ordering = ('book', 'anchor',)
 
     list_display = ('book', 'anchor',)
     ordering = ('book', 'anchor',)
index cab9475..48c0095 100644 (file)
@@ -1,9 +1,10 @@
-# -*- 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 rest_framework import serializers
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from rest_framework import serializers
+from api.fields import AbsoluteURLField
 from catalogue.models import Book
 from catalogue.models import Book
+from club.models import Membership
 
 
 class BookLiked(serializers.ReadOnlyField):
 
 
 class BookLiked(serializers.ReadOnlyField):
@@ -19,3 +20,12 @@ class BookLiked(serializers.ReadOnlyField):
                 request.liked_books = None
         if request.liked_books is not None:
             return value in request.liked_books
                 request.liked_books = None
         if request.liked_books is not None:
             return value in request.liked_books
+
+
+class EmbargoURLField(AbsoluteURLField):
+    def to_representation(self, value):
+        request = self.context['request']
+        if Membership.is_active_for(request.user):
+            return super().to_representation(value)
+        else:
+            return None
index a5069e3..79bb58b 100644 (file)
@@ -1,11 +1,10 @@
-# -*- 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 rest_framework import serializers
 from api.fields import AbsoluteURLField, LegacyMixin, ThumbnailField
 from catalogue.models import Book, Collection, Tag, BookMedia, Fragment
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from rest_framework import serializers
 from api.fields import AbsoluteURLField, LegacyMixin, ThumbnailField
 from catalogue.models import Book, Collection, Tag, BookMedia, Fragment
-from .fields import BookLiked
+from .fields import BookLiked, EmbargoURLField
 
 
 class TagSerializer(serializers.ModelSerializer):
 
 
 class TagSerializer(serializers.ModelSerializer):
@@ -68,7 +67,7 @@ class FilterBookListSerializer(BookListSerializer):
 
 
 class MediaSerializer(LegacyMixin, serializers.ModelSerializer):
 
 
 class MediaSerializer(LegacyMixin, serializers.ModelSerializer):
-    url = serializers.FileField(source='file')
+    url = EmbargoURLField(source='file_url')
 
     class Meta:
         model = BookMedia
 
     class Meta:
         model = BookMedia
@@ -88,13 +87,13 @@ class BookDetailSerializer(LegacyMixin, serializers.ModelSerializer):
     parent = BookSerializer()
     children = BookSerializer(many=True)
 
     parent = BookSerializer()
     children = BookSerializer(many=True)
 
-    xml = AbsoluteURLField(source='xml_url')
-    html = AbsoluteURLField(source='html_url')
-    txt = AbsoluteURLField(source='txt_url')
-    fb2 = AbsoluteURLField(source='fb2_url')
-    epub = AbsoluteURLField(source='epub_url')
-    mobi = AbsoluteURLField(source='mobi_url')
-    pdf = AbsoluteURLField(source='pdf_url')
+    xml = EmbargoURLField(source='xml_url')
+    html = EmbargoURLField(source='html_url')
+    txt = EmbargoURLField(source='txt_url')
+    fb2 = EmbargoURLField(source='fb2_url')
+    epub = EmbargoURLField(source='epub_url')
+    mobi = EmbargoURLField(source='mobi_url')
+    pdf = EmbargoURLField(source='pdf_url')
     media = MediaSerializer(many=True)
     cover_thumb = ThumbnailField('139x193', source='cover')
     simple_thumb = serializers.FileField(source='cover_api_thumb')
     media = MediaSerializer(many=True)
     cover_thumb = ThumbnailField('139x193', source='cover')
     simple_thumb = serializers.FileField(source='cover_api_thumb')
@@ -120,11 +119,11 @@ class BookPreviewSerializer(BookDetailSerializer):
 
 
 class EbookSerializer(BookListSerializer):
 
 
 class EbookSerializer(BookListSerializer):
-    txt = AbsoluteURLField(source='txt_url')
-    fb2 = AbsoluteURLField(source='fb2_url')
-    epub = AbsoluteURLField(source='epub_url')
-    mobi = AbsoluteURLField(source='mobi_url')
-    pdf = AbsoluteURLField(source='pdf_url')
+    txt = EmbargoURLField(source='txt_url')
+    fb2 = EmbargoURLField(source='fb2_url')
+    epub = EmbargoURLField(source='epub_url')
+    mobi = EmbargoURLField(source='mobi_url')
+    pdf = EmbargoURLField(source='pdf_url')
 
     class Meta:
         model = Book
 
     class Meta:
         model = Book
index ee9f074..c986950 100644 (file)
@@ -142,15 +142,23 @@ class BookDetail(RetrieveAPIView):
     serializer_class = serializers.BookDetailSerializer
 
 
     serializer_class = serializers.BookDetailSerializer
 
 
+@vary_on_auth  # Because of embargo links.
 class EbookList(BookList):
     serializer_class = serializers.EbookSerializer
 
 
 @vary_on_auth  # Because of 'liked'.
 class Preview(ListAPIView):
 class EbookList(BookList):
     serializer_class = serializers.EbookSerializer
 
 
 @vary_on_auth  # Because of 'liked'.
 class Preview(ListAPIView):
-    queryset = Book.objects.filter(preview=True)
+    #queryset = Book.objects.filter(preview=True)
     serializer_class = serializers.BookPreviewSerializer
 
     serializer_class = serializers.BookPreviewSerializer
 
+    def get_queryset(self):
+        qs = Book.objects.filter(preview=True)
+        # FIXME: temporary workaround for a problem with iOS app.
+        if 'Darwin' in self.request.META['HTTP_USER_AGENT']:
+            qs = qs.none()
+        return qs
+
 
 @vary_on_auth  # Because of 'liked'.
 class FilterBookList(ListAPIView):
 
 @vary_on_auth  # Because of 'liked'.
 class FilterBookList(ListAPIView):
diff --git a/src/catalogue/migrations/0026_book_preview_key.py b/src/catalogue/migrations/0026_book_preview_key.py
new file mode 100644 (file)
index 0000000..9e0335a
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-06-26 08:33
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0025_merge'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='book',
+            name='preview_key',
+            field=models.CharField(blank=True, max_length=32, null=True),
+        ),
+    ]
index c0d47f9..61d20fd 100644 (file)
@@ -26,7 +26,7 @@ from newtagging import managers
 from catalogue import constants
 from catalogue.fields import EbookField
 from catalogue.models import Tag, Fragment, BookMedia
 from catalogue import constants
 from catalogue.fields import EbookField
 from catalogue.models import Tag, Fragment, BookMedia
-from catalogue.utils import create_zip, gallery_url, gallery_path, split_tags
+from catalogue.utils import create_zip, gallery_url, gallery_path, split_tags, get_random_hash
 from catalogue.models.tag import prefetched_relations
 from catalogue import app_settings
 from catalogue import tasks
 from catalogue.models.tag import prefetched_relations
 from catalogue import app_settings
 from catalogue import tasks
@@ -76,6 +76,7 @@ class Book(models.Model):
     audio_length = models.CharField(_('audio length'), blank=True, max_length=8)
     preview = models.BooleanField(_('preview'), default=False)
     preview_until = models.DateField(_('preview until'), blank=True, null=True)
     audio_length = models.CharField(_('audio length'), blank=True, max_length=8)
     preview = models.BooleanField(_('preview'), default=False)
     preview_until = models.DateField(_('preview until'), blank=True, null=True)
+    preview_key = models.CharField(max_length=32, blank=True, null=True)
 
     # files generated during publication
     cover = EbookField(
 
     # files generated during publication
     cover = EbookField(
@@ -199,6 +200,9 @@ class Book(models.Model):
         self.cached_author = self.tag_unicode('author')
         self.has_audience = 'audience' in self.extra_info
 
         self.cached_author = self.tag_unicode('author')
         self.has_audience = 'audience' in self.extra_info
 
+        if self.preview and not self.preview_key:
+            self.preview_key = get_random_hash(self.slug)[:32]
+
         ret = super(Book, self).save(force_insert, force_update, **kwargs)
 
         return ret
         ret = super(Book, self).save(force_insert, force_update, **kwargs)
 
         return ret
@@ -284,7 +288,7 @@ class Book(models.Model):
         media = self.get_media(format_)
         if media:
             if self.preview:
         media = self.get_media(format_)
         if media:
             if self.preview:
-                return reverse('embargo_link', kwargs={'slug': self.slug, 'format_': format_})
+                return reverse('embargo_link', kwargs={'key': self.preview_key, 'slug': self.slug, 'format_': format_})
             else:
                 return media.url
         else:
             else:
                 return media.url
         else:
index ec37849..4532c86 100644 (file)
@@ -154,3 +154,6 @@ class BookMedia(models.Model):
     @property
     def artist(self):
         return self.extra_info.get('artist_name', None)
     @property
     def artist(self):
         return self.extra_info.get('artist_name', None)
+
+    def file_url(self):
+        return self.file.url
index 458f336..63134f5 100644 (file)
@@ -57,7 +57,7 @@ urlpatterns = [
 
     url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
 
 
     url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
 
-    url(r'^pobierz/(?P<slug>%s).(?P<format_>[a-z0-9]*)$' % SLUG, views.embargo_link, name='embargo_link'),
+    url(r'^pobierz/(?P<key>.*)/(?P<slug>%s).(?P<format_>[a-z0-9]*)$' % SLUG, views.embargo_link, name='embargo_link'),
 
     # zip
     url(r'^zip/pdf\.zip$', views.download_zip, {'format': 'pdf', 'slug': None}, 'download_zip_pdf'),
 
     # zip
     url(r'^zip/pdf\.zip$', views.download_zip, {'format': 'pdf', 'slug': None}, 'download_zip_pdf'),
index 6299cd1..632eff8 100644 (file)
@@ -356,15 +356,15 @@ def tag_info(request, tag_id):
 
 
 @never_cache
 
 
 @never_cache
-def embargo_link(request, format_, slug):
+def embargo_link(request, key, format_, slug):
     book = get_object_or_404(Book, slug=slug)
     if format_ not in Book.formats:
         raise Http404
     book = get_object_or_404(Book, slug=slug)
     if format_ not in Book.formats:
         raise Http404
+    if key != book.preview_key:
+        raise Http404
     media_file = book.get_media(format_)
     if not book.preview:
         return HttpResponseRedirect(media_file.url)
     media_file = book.get_media(format_)
     if not book.preview:
         return HttpResponseRedirect(media_file.url)
-    if not Membership.is_active_for(request.user):
-        return HttpResponseRedirect(book.get_absolute_url())
     return HttpResponse(media_file, content_type=constants.EBOOK_CONTENT_TYPES[format_])
 
 
     return HttpResponse(media_file, content_type=constants.EBOOK_CONTENT_TYPES[format_])