fix
[wolnelektury.git] / src / catalogue / api / views.py
index 335a301..932f9cd 100644 (file)
@@ -1,13 +1,16 @@
-# 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
@@ -18,6 +21,7 @@ from catalogue.models import Book, Collection, Tag, Fragment, BookMedia
 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
@@ -26,13 +30,31 @@ from . import serializers
 book_tag_categories = ['author', 'epoch', 'kind', 'genre']
 
 
+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(ListAPIView):
-    queryset = Collection.objects.all()
+    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
@@ -45,7 +67,7 @@ class BookList(ListAPIView):
     serializer_class = serializers.BookListSerializer
 
     def get(self, request, filename=None, **kwargs):
-        if filename and 'count' not in request.query_params:
+        if filename and not kwargs.get('tags') and 'count' not in request.query_params:
             try:
                 with open(os.path.join(settings.MEDIA_ROOT, 'api', '%s.%s' % (filename, request.accepted_renderer.format)), 'rb') as f:
                     content = f.read()
@@ -135,6 +157,9 @@ class BookList(ListAPIView):
         name = request.POST.get('name', '')
         part_name = request.POST.get('part_name', '')
 
+        project_description = request.POST.get('project_description', '')
+        project_icon = request.POST.get('project_icon', '')
+
         _rest, slug = request.POST['book'].rstrip('/').rsplit('/', 1)
         book = Book.objects.get(slug=slug)
 
@@ -146,6 +171,8 @@ class BookList(ListAPIView):
         bm.name = name
         bm.part_name = part_name
         bm.index = index
+        bm.project_description = project_description
+        bm.project_icon = project_icon
         bm.file.save(None, request.data['file'], save=False)
         bm.save(parts_count=parts_count)
 
@@ -284,14 +311,69 @@ class TagCategoryView(ListAPIView):
 
 
 class TagView(RetrieveAPIView):
+    permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
     serializer_class = serializers.TagDetailSerializer
-
+    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'))
+        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()
+        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'.