From c15b511c1e9ebb06050f40e98ec554ba6c26cb1b Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Wed, 26 Jun 2019 11:07:10 +0200 Subject: [PATCH] Embargo links hidden. --- src/catalogue/admin.py | 25 ++++++---------- src/catalogue/api/fields.py | 12 +++++++- src/catalogue/api/serializers.py | 29 +++++++++---------- src/catalogue/api/views.py | 10 ++++++- .../migrations/0026_book_preview_key.py | 20 +++++++++++++ src/catalogue/models/book.py | 8 +++-- src/catalogue/models/bookmedia.py | 3 ++ src/catalogue/urls.py | 2 +- src/catalogue/views.py | 6 ++-- 9 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 src/catalogue/migrations/0026_book_preview_key.py diff --git a/src/catalogue/admin.py b/src/catalogue/admin.py index 93a265a98..44b6fb76f 100644 --- a/src/catalogue/admin.py +++ b/src/catalogue/admin.py @@ -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',) diff --git a/src/catalogue/api/fields.py b/src/catalogue/api/fields.py index cab947592..48c00958a 100644 --- a/src/catalogue/api/fields.py +++ b/src/catalogue/api/fields.py @@ -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 diff --git a/src/catalogue/api/serializers.py b/src/catalogue/api/serializers.py index a5069e3bd..79bb58b6c 100644 --- a/src/catalogue/api/serializers.py +++ b/src/catalogue/api/serializers.py @@ -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 diff --git a/src/catalogue/api/views.py b/src/catalogue/api/views.py index ee9f07403..c98695094 100644 --- a/src/catalogue/api/views.py +++ b/src/catalogue/api/views.py @@ -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 index 000000000..9e0335af5 --- /dev/null +++ b/src/catalogue/migrations/0026_book_preview_key.py @@ -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), + ), + ] diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py index c0d47f9e2..61d20fdc6 100644 --- a/src/catalogue/models/book.py +++ b/src/catalogue/models/book.py @@ -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: diff --git a/src/catalogue/models/bookmedia.py b/src/catalogue/models/bookmedia.py index ec37849d2..4532c862e 100644 --- a/src/catalogue/models/bookmedia.py +++ b/src/catalogue/models/bookmedia.py @@ -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 diff --git a/src/catalogue/urls.py b/src/catalogue/urls.py index 458f336f1..63134f5b3 100644 --- a/src/catalogue/urls.py +++ b/src/catalogue/urls.py @@ -57,7 +57,7 @@ urlpatterns = [ url(r'^audiobooki/(?Pmp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'), - url(r'^pobierz/(?P%s).(?P[a-z0-9]*)$' % SLUG, views.embargo_link, name='embargo_link'), + url(r'^pobierz/(?P.*)/(?P%s).(?P[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'), diff --git a/src/catalogue/views.py b/src/catalogue/views.py index 6299cd19d..632eff868 100644 --- a/src/catalogue/views.py +++ b/src/catalogue/views.py @@ -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_]) -- 2.20.1