Browse Source

small perf improv at viewmodels, further work on bulk thread delete

Rafał Pitoń 8 years ago
parent
commit
43e183b805

+ 13 - 28
frontend/src/components/threads/moderation/controls.js

@@ -170,40 +170,25 @@ export default class extends React.Component {
       return;
     }
 
-    const errors = [];
-    const countdown = new Countdown(() => {
-      if (errors.length) {
-        modal.show(<ErrorsModal errors={errors} />);
-      } else {
-        snackbar.success(gettext("Selected threads were deleted."));
-      }
-
-      // unfreeze non-deleted threads
-      this.props.threads.forEach((thread) => {
-        this.props.freezeThread(thread.id);
-      });
-
-      // reduce selection to non-deleted threads
-      store.dispatch(select.all(this.props.threads.map(function(item) {
-        return item.id;
-      })));
-    }, this.props.threads.length);
-
-    this.props.threads.forEach((thread) => {
+    this.props.threads.map((thread) => {
       this.props.freezeThread(thread.id);
+    });
+
+    const ids = this.props.threads.map((thread) => { return thread.id; });
 
-      ajax.delete(thread.api.index).then((data) => {
+    ajax.delete(thread.api.index, ids).then((data) => {
+      this.props.threads.map((thread) => {
         this.props.freezeThread(thread.id);
         this.props.deleteThread(thread);
-        countdown.count();
-      }, (rejection) => {
-        errors.push({
-          thread: thread,
-          errors: [rejection.detail]
-        });
+      });
 
-        countdown.count();
+      snackbar.success(gettext("Selected threads were deleted."));
+    }, (errors) => {
+      this.props.threads.map((thread) => {
+        this.props.freezeThread(thread.id);
       });
+
+      modal.show(<ErrorsModal errors={errors} />);
     });
   };
   /* jshint ignore:end */

File diff suppressed because it is too large
+ 42009 - 24
misago/static/misago/js/misago.js


+ 1 - 1
misago/threads/api/postendpoints/delete.py

@@ -13,7 +13,7 @@ from misago.threads.permissions import exclude_invisible_posts
 DELETE_LIMIT = settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL
 
 
-def delete_single(request, thread, post):
+def delete_post(request, thread, post):
     if post.is_event:
         allow_delete_event(request.user, post)
     else:

+ 76 - 0
misago/threads/api/threadendpoints/delete.py

@@ -0,0 +1,76 @@
+from rest_framework.response import Response
+
+from django.core.exceptions import PermissionDenied
+from django.db import transaction
+from django.utils.six import text_type
+from django.utils.translation import ugettext as _
+from django.utils.translation import ungettext
+
+from misago.conf import settings
+from misago.threads.moderation import threads as moderation
+
+
+DELETE_LIMIT = settings.MISAGO_THREADS_PER_PAGE + settings.MISAGO_THREADS_TAIL
+
+
+@transaction.atomic
+def delete_thread(request, thread):
+    allow_delete_thread(request.user, thread)
+    moderation.delete_thread(request.user, thread)
+
+    return Response({})
+
+
+def delete_bulk(request, viewmodel):
+    threads = clean_threads_for_delete(request, viewmodel)
+    raise Exception(threads)
+
+    errors = []
+    for thread in threads:
+        try:
+            allow_delete_thread(request.user, thread)
+        except PermissionDenied as e:
+            errors.append({
+                'thread': {
+                    'id': thread.id,
+                    'title': thread.title
+                },
+                'error': text_type(e)
+            })
+
+    if errors:
+        return Response(errors, status_code=403)
+
+    for thread in delete:
+        with transaction.atomic():
+            moderation.delete_thread(request.user, thread)
+
+    return Response({})
+
+
+def clean_threads_for_delete(request, viewmodel):
+    try:
+        threads_ids = list(map(int, request.data or []))
+    except (ValueError, TypeError):
+        raise PermissionDenied(_("One or more thread ids received were invalid."))
+
+    if not threads_ids:
+        raise PermissionDenied(_("You have to specify at least one thread to delete."))
+    elif len(threads_ids) > DELETE_LIMIT:
+        message = ungettext(
+            "No more than %(limit)s thread can be deleted at single time.",
+            "No more than %(limit)s threads can be deleted at single time.",
+            DELETE_LIMIT,
+        )
+        raise PermissionDenied(message % {'limit': DELETE_LIMIT})
+
+    threads = [viewmodel(request, pk) for pk in threads_ids]
+    if len(threads) != len(threads_ids):
+        raise PermissionDenied(_("One or more threads to delete could not be found."))
+
+    return threads
+
+
+def allow_delete_thread(user, thread):
+    if thread.acl.get('can_delete') != 2:
+        raise PermissionDenied(_("You don't have permission to delete this thread."))

+ 21 - 31
misago/threads/api/threadposts.py

@@ -14,7 +14,7 @@ from misago.threads.serializers import AttachmentSerializer, PostSerializer
 from misago.threads.viewmodels import ForumThread, PrivateThread, ThreadPost, ThreadPosts
 from misago.users.online.utils import make_users_status_aware
 
-from .postendpoints.delete import delete_single, delete_bulk
+from .postendpoints.delete import delete_bulk, delete_post
 from .postendpoints.edits import get_edit_endpoint, revert_post_endpoint
 from .postendpoints.likes import likes_list_endpoint
 from .postendpoints.merge import posts_merge_endpoint
@@ -31,27 +31,13 @@ class ViewSet(viewsets.ViewSet):
     posts = ThreadPosts
     post_ = ThreadPost
 
-    def get_thread(
-            self,
-            request,
-            pk,
-            read_aware=True,
-            subscription_aware=True,
-    ):
+    def get_thread(self, request, pk, path_aware=False, read_aware=False, subscription_aware=False):
         return self.thread(
             request,
             get_int_or_404(pk),
-            None,
-            read_aware,
-            subscription_aware,
-        )
-
-    def get_thread_for_update(self, request, pk):
-        return self.get_thread(
-            request,
-            pk,
-            read_aware=False,
-            subscription_aware=False,
+            path_aware=path_aware,
+            read_aware=read_aware,
+            subscription_aware=subscription_aware,
         )
 
     def get_posts(self, request, thread, page):
@@ -65,7 +51,13 @@ class ViewSet(viewsets.ViewSet):
         if page == 1:
             page = 0  # api allows explicit first page
 
-        thread = self.get_thread(request, thread_pk)
+        thread = self.get_thread(
+            request,
+            thread_pk,
+            path_aware=True,
+            read_aware=True,
+            subscription_aware=True,
+        )
         posts = self.get_posts(request, thread, page)
 
         data = thread.get_frontend_context()
@@ -171,7 +163,7 @@ class ViewSet(viewsets.ViewSet):
 
         if pk:
             post = self.get_post(request, thread, pk).unwrap()
-            return delete_single(request, thread.unwrap(), post)
+            return delete_post(request, thread.unwrap(), post)
 
         return delete_bulk(request, thread.unwrap())
 
@@ -180,19 +172,19 @@ class ViewSet(viewsets.ViewSet):
     def read(self, request, thread_pk, pk):
         request.user.lock()
 
-        thread = self.get_thread(request, thread_pk).unwrap()
+        thread = self.get_thread(
+            request,
+            thread_pk,
+            read_aware=True,
+            subscription_aware=True,
+        ).unwrap()
         post = self.get_post(request, thread, pk).unwrap()
 
         return post_read_endpoint(request, thread, post)
 
     @detail_route(methods=['get'], url_path='editor')
     def post_editor(self, request, thread_pk, pk):
-        thread = self.get_thread(
-            request,
-            thread_pk,
-            read_aware=False,
-            subscription_aware=False,
-        )
+        thread = self.get_thread(request, thread_pk)
         post = self.get_post(request, thread, pk).unwrap()
 
         allow_edit_post(request.user, post)
@@ -217,9 +209,7 @@ class ViewSet(viewsets.ViewSet):
 
     @list_route(methods=['get'], url_path='editor')
     def reply_editor(self, request, thread_pk):
-        thread = self.get_thread(
-            request, thread_pk, read_aware=False, subscription_aware=False
-        ).unwrap()
+        thread = self.get_thread(request, thread_pk).unwrap()
         allow_reply_thread(request.user, thread)
 
         if 'reply' in request.query_params:

+ 25 - 17
misago/threads/api/threads.py

@@ -11,9 +11,11 @@ from misago.core.shortcuts import get_int_or_404
 from misago.threads.models import Post, Thread
 from misago.threads.moderation import threads as moderation
 from misago.threads.permissions import allow_use_private_threads
-from misago.threads.viewmodels import ForumThread, PrivateThread
+from misago.threads.viewmodels import (ForumThread, PrivateThread,
+    ThreadsRootCategory, PrivateThreadsCategory)
 
 from .postingendpoint import PostingEndpoint
+from .threadendpoints.delete import delete_bulk, delete_thread
 from .threadendpoints.editor import thread_start_editor
 from .threadendpoints.list import private_threads_list_endpoint, threads_list_endpoint
 from .threadendpoints.merge import thread_merge_endpoint, threads_merge_endpoint
@@ -24,17 +26,24 @@ from .threadendpoints.read import read_private_threads, read_threads
 class ViewSet(viewsets.ViewSet):
     thread = None
 
-    def get_thread(self, request, pk, read_aware=True, subscription_aware=True):
+    def get_thread(self, request, pk, path_aware=False, read_aware=False, subscription_aware=False):
         return self.thread(
             request,
             get_int_or_404(pk),
-            None,
-            read_aware,
-            subscription_aware,
+            path_aware=path_aware,
+            read_aware=read_aware,
+            subscription_aware=subscription_aware,
         )
 
     def retrieve(self, request, pk):
-        thread = self.get_thread(request, pk)
+        thread = self.get_thread(
+            request,
+            pk,
+            path_aware=True,
+            read_aware=True,
+            subscription_aware=True,
+        )
+
         return Response(thread.get_frontend_context())
 
     @transaction.atomic
@@ -43,19 +52,17 @@ class ViewSet(viewsets.ViewSet):
         thread = self.get_thread(request, pk).unwrap()
         return thread_patch_endpoint(request, thread)
 
-    @transaction.atomic
-    def destroy(self, request, pk):
-        request.user.lock()
-        thread = self.get_thread(request, pk)
+    def delete(self, request, pk=None):
+        if pk:
+            thread = self.get_thread(request, pk).unwrap()
+            return delete_thread(request, thread)
 
-        if thread.acl.get('can_hide') == 2:
-            moderation.delete_thread(request.user, thread)
-            return Response({'detail': 'ok'})
-        else:
-            raise PermissionDenied(_("You don't have permission to delete this thread."))
+        category = self.category(request)
+        return delete_bulk(request, self.thread)
 
 
 class ThreadViewSet(ViewSet):
+    category = ThreadsRootCategory
     thread = ForumThread
 
     def list(self, request):
@@ -106,10 +113,11 @@ class ThreadViewSet(ViewSet):
     @transaction.atomic
     def read(self, request):
         read_threads(request.user, request.GET.get('category'))
-        return Response({'detail': 'ok'})
+        return Response({})
 
 
 class PrivateThreadViewSet(ViewSet):
+    category = PrivateThreadsCategory
     thread = PrivateThread
 
     def list(self, request):
@@ -152,4 +160,4 @@ class PrivateThreadViewSet(ViewSet):
     def read(self, request):
         allow_use_private_threads(request.user)
         read_private_threads(request.user)
-        return Response({'detail': 'ok'})
+        return Response({})

+ 3 - 1
misago/threads/viewmodels/thread.py

@@ -34,13 +34,15 @@ class ViewModel(BaseViewModel):
             request,
             pk,
             slug=None,
+            path_aware=False,
             read_aware=False,
             subscription_aware=False,
             poll_votes_aware=False
     ):
         model = self.get_thread(request, pk, slug)
 
-        model.path = self.get_thread_path(model.category)
+        if path_aware:
+            model.path = self.get_thread_path(model.category)
 
         add_acl(request.user, model.category)
         add_acl(request.user, model)

+ 7 - 1
misago/threads/views/thread.py

@@ -23,7 +23,13 @@ class ThreadBase(View):
 
     def get_thread(self, request, pk, slug):
         return self.thread(
-            request, pk, slug, read_aware=True, subscription_aware=True, poll_votes_aware=True
+            request,
+            pk,
+            slug,
+            path_aware=True,
+            read_aware=True,
+            subscription_aware=True,
+            poll_votes_aware=True,
         )
 
     def get_posts(self, request, thread, page):

+ 1 - 1
misago/users/api/users.py

@@ -238,7 +238,7 @@ class UserViewSet(viewsets.GenericViewSet):
 
                 profile.delete()
 
-        return Response({'detail': 'ok'})
+        return Response({})
 
     @detail_route(methods=['get'])
     def followers(self, request, pk=None):

Some files were not shown because too many files changed in this diff