-# 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 Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
#
import json
import os.path
+from urllib.request import urlopen
from django.conf import settings
+from django.core.files.base import ContentFile
from django.http import Http404, HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
-from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404
+from rest_framework.generics import (ListAPIView, RetrieveAPIView,
+ RetrieveUpdateAPIView, get_object_or_404)
from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
from rest_framework.response import Response
from rest_framework import status
from catalogue.models.tag import prefetch_relations
from club.models import Membership
from club.permissions import IsClubMember
+from sortify import sortify
from wolnelektury.utils import re_escape
from .helpers import books_after, order_books
from . import serializers
book_tag_categories = ['author', 'epoch', 'kind', 'genre']
-class CollectionList(ListAPIView):
+class LegacyListAPIView(ListAPIView):
+ pagination_class = None
+
+
+class CreateOnPutMixin:
+ '''
+ Creates a new model instance when PUTting a nonexistent resource.
+ '''
+ def get_object(self):
+ try:
+ return super().get_object()
+ except Http404:
+ if self.request.method == 'PUT':
+ lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
+ return self.get_queryset().model(**{
+ self.lookup_field: self.kwargs[lookup_url_kwarg]
+ })
+ else:
+ raise
+
+
+class CollectionList(LegacyListAPIView):
queryset = Collection.objects.filter(listed=True)
serializer_class = serializers.CollectionListSerializer
@vary_on_auth # Because of 'liked'.
-class CollectionDetail(RetrieveAPIView):
+class CollectionDetail(CreateOnPutMixin, RetrieveUpdateAPIView):
+ permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
queryset = Collection.objects.all()
lookup_field = 'slug'
serializer_class = serializers.CollectionSerializer
@vary_on_auth # Because of 'liked'.
-class BookList(ListAPIView):
+class BookList(LegacyListAPIView):
permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
queryset = Book.objects.none() # Required for DjangoModelPermissions
serializer_class = serializers.BookListSerializer
return Response({}, status=status.HTTP_201_CREATED)
+class BookList2(ListAPIView):
+ permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
+ queryset = Book.objects.none() # Required for DjangoModelPermissions
+ serializer_class = serializers.BookSerializer2
+
+ def get_queryset(self):
+ books = Book.objects.all()
+ books = books.filter(findable=True)
+ books = order_books(books, True)
+
+ return books
+
+
@vary_on_auth # Because of 'liked'.
class BookDetail(RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = serializers.BookDetailSerializer
+class BookDetail2(RetrieveAPIView):
+ queryset = Book.objects.all()
+ lookup_field = 'slug'
+ serializer_class = serializers.BookSerializer2
+
+
@vary_on_auth # Because of embargo links.
class EbookList(BookList):
serializer_class = serializers.EbookSerializer
@method_decorator(never_cache, name='dispatch')
-class Preview(ListAPIView):
+class Preview(LegacyListAPIView):
#queryset = Book.objects.filter(preview=True)
serializer_class = serializers.BookPreviewSerializer
@vary_on_auth # Because of 'liked'.
-class FilterBookList(ListAPIView):
+class FilterBookList(LegacyListAPIView):
serializer_class = serializers.FilterBookListSerializer
def parse_bool(self, s):
return HttpResponse(self.get_object().get_media('epub'))
-class TagCategoryView(ListAPIView):
+class TagCategoryView(LegacyListAPIView):
serializer_class = serializers.TagSerializer
def get_queryset(self):
category = self.kwargs['category']
tags = Tag.objects.filter(category=category).exclude(items=None).order_by('slug')
- if self.request.query_params.get('book_only') == 'true':
- tags = tags.filter(for_books=True)
- if self.request.GET.get('picture_only') == 'true':
- tags = filter(for_pictures=True)
after = self.request.query_params.get('after')
count = self.request.query_params.get('count')
queryset = Tag.objects.all()
def get_object(self):
- return get_object_or_404(
- Tag,
- category=self.kwargs['category'],
- slug=self.kwargs['slug']
- )
+ try:
+ return get_object_or_404(
+ Tag,
+ category=self.kwargs['category'],
+ slug=self.kwargs['slug']
+ )
+ except Http404:
+ if self.request.method == 'POST':
+ return Tag(
+ category=self.kwargs['category'],
+ slug=self.kwargs['slug']
+ )
+ else:
+ raise
def post(self, request, **kwargs):
data = json.loads(request.POST.get('data'))
- desc = data['description_pl']
+ fields = {
+ "name_pl": "name_pl",
+ "description_pl": "description_pl",
+ "plural": "plural",
+ "is_epoch_specific": "genre_epoch_specific",
+ "collective_noun": "collective_noun",
+ "adjective_feminine_singular": "adjective_feminine_singular",
+ "adjective_nonmasculine_plural": "adjective_nonmasculine_plural",
+ "genitive": "genitive",
+ "collective_noun": "collective_noun",
+ "gazeta_link": "gazeta_link",
+ "culturepl_link": "culturepl_link",
+ "wiki_link_pl": "wiki_link_pl",
+ "photo_attribution": "photo_attribution",
+ }
obj = self.get_object()
- obj.description_pl = desc
- obj.save(update_fields=['description_pl'], quick=True)
+ updated = set()
+ for data_field, model_field in fields.items():
+ value = data.get(data_field)
+ if value:
+ if obj.category == 'author' and model_field == 'name_pl':
+ obj.sort_key = sortify(value.lower())
+ updated.add('sort_key')
+ value = ' '.join(reversed([t.strip() for t in value.split(',', 1)]))
+ setattr(obj, model_field, value)
+ updated.add(model_field)
+ if data.get('photo'):
+ response = urlopen(data['photo'])
+ ext = response.headers.get('Content-Type', '').rsplit('/', 1)[-1]
+ obj.photo.save(
+ "{}.{}".format(self.kwargs['slug'], ext),
+ ContentFile(response.read()),
+ save=False,
+ )
+ updated.add('photo')
+
+ if obj.pk:
+ obj.save(update_fields=updated, quick=True)
+ else:
+ obj.save()
return Response({})
@vary_on_auth # Because of 'liked'.
-class FragmentList(ListAPIView):
+class FragmentList(LegacyListAPIView):
serializer_class = serializers.FragmentSerializer
def get_queryset(self):