Obey length limits for wikidata import.
[redakcja.git] / src / wiki / views.py
index 26f031b..3e6fedb 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from datetime import datetime
+import json
 import os
 import logging
 from time import mktime
@@ -11,18 +12,20 @@ from django.apps import apps
 from django.conf import settings
 from django.urls import reverse
 from django import http
-from django.http import Http404, HttpResponseForbidden
+from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseBadRequest
 from django.middleware.gzip import GZipMiddleware
 from django.utils.decorators import decorator_from_middleware
 from django.utils.formats import localize
 from django.utils.translation import gettext as _
 from django.views.decorators.http import require_POST, require_GET
 from django.shortcuts import get_object_or_404, render
+from django_gravatar.helpers import get_gravatar_url
 from sorl.thumbnail import get_thumbnail
 
 from documents.models import Book, Chunk
 import sources.models
 from . import nice_diff
+from team.models import Presence
 from wiki import forms
 from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
                 ajax_require_permission)
@@ -38,6 +41,10 @@ logger = logging.getLogger("fnp.wiki")
 MAX_LAST_DOCS = 10
 
 
+class HttpResponseLengthRequired(HttpResponse):
+    status_code = 411
+
+
 @never_cache
 def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
     try:
@@ -129,6 +136,20 @@ def text(request, chunk_id):
         return HttpResponseForbidden("Not authorized.")
 
     if request.method == 'POST':
+        # Check length to reject broken request.
+        try:
+            expected_cl = int(request.META['CONTENT_LENGTH'])
+        except:
+            return HttpResponseLengthRequired(json.dumps(
+                {"__message": _("Content length required.")}
+            ))
+        # 411 if missing
+        cl = len(request.body)
+        if cl != expected_cl:
+            return HttpResponseBadRequest(json.dumps(
+                {"__message": _("Wrong content length, request probably interrupted.")}
+            ))
+
         form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
         if form.is_valid():
             if request.user.is_authenticated:
@@ -252,15 +273,21 @@ def gallery(request, directory):
 
 
 @never_cache
-def scans_list(request, pk):
-    bs = get_object_or_404(sources.models.BookSource, pk=pk)
+def scans_list(request, pks):
+    pks = pks.split(',')
+    bss = [
+        get_object_or_404(sources.models.BookSource, pk=pk)
+        for pk in pks
+    ]
     def map_to_url(filename):
         return quote(("%s/%s" % (settings.MEDIA_URL, filename)))
-    images = [
-        {
-            "url": map_to_url(f),
-        } for f in bs.get_view_files()
-    ]
+    images = []
+    for bs in bss:
+        images.extend([
+            {
+                "url": map_to_url(f),
+            } for f in bs.get_view_files()
+        ])
     return JSONResponse(images)
 
 
@@ -292,12 +319,34 @@ def diff(request, chunk_id):
 
 @never_cache
 def revision(request, chunk_id):
+    if not request.session.session_key:
+        return HttpResponseForbidden("Not authorized.")
     doc = get_object_or_404(Chunk, pk=chunk_id)
     if not doc.book.accessible(request):
         return HttpResponseForbidden("Not authorized.")
-    Presence = apps.get_model('team', 'Presence')
-    Presence.report(request.user, doc, request.GET.get('a') == 'true')
-    return http.HttpResponse(str(doc.revision()))
+
+    Presence.report(
+        request.user, request.session.session_key,
+        doc,
+        request.GET.get('a') == 'true'
+    )
+
+    # Temporary compat for unreloaded clients.
+    if not request.GET.get('new'):
+        return http.HttpResponse(str(doc.revision()))
+
+    return JSONResponse({
+        'rev': doc.revision(),
+        'people': list([
+            {
+                'name': (p.user.first_name + ' ' + p.user.last_name) if p.user is not None else '?',
+                'gravatar': get_gravatar_url(p.user.email if p.user is not None else '-', size=26),
+                'since': p.since.strftime('%H:%M'),
+                'active': p.active,
+            }
+            for p in Presence.get_current(request.session.session_key, doc)
+        ]),
+    })
 
 
 @never_cache
@@ -321,7 +370,7 @@ def history(request, chunk_id):
                 "description": change.description,
                 "author": change.author_str(),
                 "date": localize(change.created_at),
-                "publishable": _("Publishable") + "\n" if change.publishable else "",
+                "publishable": change.publishable,
                 "tag": ',\n'.join(str(tag) for tag in change.tags.all()),
                 "published": _("Published") + ": " + \
                     localize(change.publish_log.order_by('-book_record__timestamp')[0].book_record.timestamp) \