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.
 #
@@ -30,8 +29,11 @@ class MediaInline(admin.TabularInline):
     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',)
@@ -39,20 +41,11 @@ class BookAdmin(TaggableModelAdmin):
 
     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',)
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
+from api.fields import AbsoluteURLField
 from catalogue.models import Book
+from club.models import Membership
 
 
 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
+
+
+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
-from .fields import BookLiked
+from .fields import BookLiked, EmbargoURLField
 
 
 class TagSerializer(serializers.ModelSerializer):
@@ -68,7 +67,7 @@ class FilterBookListSerializer(BookListSerializer):
 
 
 class MediaSerializer(LegacyMixin, serializers.ModelSerializer):
-    url = serializers.FileField(source='file')
+    url = EmbargoURLField(source='file_url')
 
     class Meta:
         model = BookMedia
@@ -88,13 +87,13 @@ class BookDetailSerializer(LegacyMixin, serializers.ModelSerializer):
     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')
@@ -120,11 +119,11 @@ class BookPreviewSerializer(BookDetailSerializer):
 
 
 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
index ee9f074..c986950 100644 (file)
@@ -142,15 +142,23 @@ class BookDetail(RetrieveAPIView):
     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):
-    queryset = Book.objects.filter(preview=True)
+    #queryset = Book.objects.filter(preview=True)
     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):
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.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
@@ -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)
+    preview_key = models.CharField(max_length=32, blank=True, null=True)
 
     # 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
 
+        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
@@ -284,7 +288,7 @@ class Book(models.Model):
         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:
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)
+
+    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'^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'),
index 6299cd1..632eff8 100644 (file)
@@ -356,15 +356,15 @@ def tag_info(request, tag_id):
 
 
 @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
+    if key != book.preview_key:
+        raise Http404
     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_])