From 74dfa537879f5720d94eb7b59bb650930ff903f7 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 28 Jan 2019 05:25:48 -0400 Subject: [PATCH] Update documents app Rename the DeletedDocument proxy model to a TrashedDocument. Rename the deleted_document views to trashed_document. Rename the document and deleted_document URL parameters to trashed_document. Update URL parameters to the '_id' form. Add keyword arguments. Update use of .filter_by_access(). Enclose trashed document restore method in a transaction. Sort arguments. Update app for compliance with MERCs 5 and 6. Add document page view tests. Add favorite document view tests. Movernize tests. Replace use of urlencode with furl. Update views to use ExternalObjectMixin. Refactor the document and version download views. Rename the DocumentDocumentTypeEditView to DocumentChangeTypeView. Move the trashed document views to their own module. Signed-off-by: Roberto Rosario --- mayan/apps/documents/admin.py | 20 +- mayan/apps/documents/api_views.py | 236 +++++- mayan/apps/documents/apps.py | 82 +- mayan/apps/documents/dashboard_widgets.py | 58 +- mayan/apps/documents/events.py | 20 +- .../documents/forms/document_type_forms.py | 2 +- mayan/apps/documents/icons.py | 28 +- mayan/apps/documents/links.py | 213 +++--- mayan/apps/documents/managers.py | 18 +- .../apps/documents/models/document_models.py | 26 +- .../documents/models/document_page_models.py | 10 +- .../documents/models/document_type_models.py | 22 +- .../models/document_version_models.py | 23 +- mayan/apps/documents/permissions.py | 48 +- mayan/apps/documents/queues.py | 44 +- mayan/apps/documents/serializers.py | 264 +++++-- mayan/apps/documents/settings.py | 2 +- mayan/apps/documents/signals.py | 4 +- mayan/apps/documents/statistics.py | 59 +- mayan/apps/documents/tasks.py | 10 +- mayan/apps/documents/tests/mixins.py | 36 +- mayan/apps/documents/tests/test_api.py | 60 +- .../tests/test_document_page_views.py | 49 +- .../tests/test_document_type_views.py | 30 +- .../tests/test_document_version_views.py | 112 ++- .../documents/tests/test_document_views.py | 255 +++--- .../tests/test_duplicated_document_views.py | 8 +- mayan/apps/documents/tests/test_events.py | 69 +- mayan/apps/documents/tests/test_links.py | 71 +- mayan/apps/documents/tests/test_models.py | 20 +- mayan/apps/documents/tests/test_search.py | 4 +- .../tests/test_trashed_document_views.py | 135 ++-- mayan/apps/documents/urls.py | 257 ++++--- mayan/apps/documents/views/__init__.py | 1 + .../documents/views/document_page_views.py | 131 ++-- .../documents/views/document_type_views.py | 53 +- .../documents/views/document_version_views.py | 158 ++-- mayan/apps/documents/views/document_views.py | 723 ++++++------------ mayan/apps/documents/views/misc_views.py | 6 +- .../documents/views/trashed_document_views.py | 162 ++++ 40 files changed, 1914 insertions(+), 1615 deletions(-) create mode 100644 mayan/apps/documents/views/trashed_document_views.py diff --git a/mayan/apps/documents/admin.py b/mayan/apps/documents/admin.py index 62d30c0e1f..8e779bd58a 100644 --- a/mayan/apps/documents/admin.py +++ b/mayan/apps/documents/admin.py @@ -3,8 +3,8 @@ from __future__ import unicode_literals from django.contrib import admin from .models import ( - DeletedDocument, Document, DocumentPage, DocumentType, - DocumentTypeFilename, DocumentVersion, DuplicatedDocument, RecentDocument + Document, DocumentPage, DocumentType, DocumentTypeFilename, DocumentVersion, + DuplicatedDocument, RecentDocument, TrashedDocument ) @@ -29,14 +29,6 @@ class DocumentVersionInline(admin.StackedInline): allow_add = True -@admin.register(DeletedDocument) -class DeletedDocumentAdmin(admin.ModelAdmin): - date_hierarchy = 'deleted_date_time' - list_filter = ('document_type',) - list_display = ('uuid', 'label', 'document_type', 'deleted_date_time') - readonly_fields = ('uuid', 'document_type') - - @admin.register(Document) class DocumentAdmin(admin.ModelAdmin): date_hierarchy = 'date_added' @@ -69,3 +61,11 @@ class RecentDocumentAdmin(admin.ModelAdmin): list_display_links = ('document', 'datetime_accessed') list_filter = ('user',) readonly_fields = ('user', 'document', 'datetime_accessed') + + +@admin.register(TrashedDocument) +class TrashedDocumentAdmin(admin.ModelAdmin): + date_hierarchy = 'deleted_date_time' + list_filter = ('document_type',) + list_display = ('uuid', 'label', 'document_type', 'deleted_date_time') + readonly_fields = ('uuid', 'document_type') diff --git a/mayan/apps/documents/api_views.py b/mayan/apps/documents/api_views.py index 215d803056..23b506f58f 100644 --- a/mayan/apps/documents/api_views.py +++ b/mayan/apps/documents/api_views.py @@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404 from django.views.decorators.cache import cache_control, patch_cache_control from django_downloadview import DownloadMixin, VirtualFile -from rest_framework import generics, status +from rest_framework import generics, status, viewsets from rest_framework.response import Response from mayan.apps.acls.models import AccessControlList @@ -17,12 +17,12 @@ from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter from mayan.apps.rest_api.permissions import MayanPermission from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT -from .models import Document, DocumentType, RecentDocument +from .models import Document, DocumentVersion, DocumentType, RecentDocument from .permissions import ( - permission_document_create, permission_document_delete, + permission_document_create, permission_trashed_document_delete, permission_document_download, permission_document_edit, permission_document_new_version, permission_document_properties_edit, - permission_document_restore, permission_document_trash, + permission_trashed_document_restore, permission_document_trash, permission_document_type_create, permission_document_type_delete, permission_document_type_edit, permission_document_type_view, permission_document_version_revert, permission_document_version_view, @@ -30,10 +30,11 @@ from .permissions import ( ) from .serializers import ( DeletedDocumentSerializer, DocumentPageSerializer, DocumentSerializer, - DocumentTypeSerializer, DocumentVersionSerializer, NewDocumentSerializer, - NewDocumentVersionSerializer, RecentDocumentSerializer, - WritableDocumentSerializer, WritableDocumentTypeSerializer, - WritableDocumentVersionSerializer + DocumentTypeSerializer, DocumentVersionSerializer,# NewDocumentSerializer, + #NewDocumentVersionSerializer, RecentDocumentSerializer, + RecentDocumentSerializer, + #WritableDocumentSerializer, WritableDocumentTypeSerializer, + #WritableDocumentVersionSerializer ) from .settings import settings_document_page_image_cache_time from .tasks import task_generate_document_page_image @@ -41,12 +42,172 @@ from .tasks import task_generate_document_page_image logger = logging.getLogger(__name__) + +from rest_framework.decorators import action, detail_route +from rest_framework.response import Response + + +class DocumentViewSet(viewsets.ModelViewSet): + lookup_field = 'pk' + lookup_url_kwarg = 'document_id' + queryset = Document.objects.all() + serializer_class = DocumentSerializer + + """ + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_id', + url_name='document-version-list', url_path='document_versions' + ) + def document_version_list(self, request, *args, **kwargs): + serializer = DocumentVersionSerializer( + instance=self.get_object().versions.all(), many=True, + context={'request': request} + ) + return Response(serializer.data) + """ + +class DocumentPageViewSet(viewsets.ModelViewSet): + lookup_field = 'pk' + lookup_url_kwarg = 'document_page_id' + serializer_class = DocumentPageSerializer + + def get_queryset(self): + return get_object_or_404( + klass=DocumentVersion, document_id=self.kwargs['document_id'], + pk=self.kwargs['document_version_id'] + ).pages.all() + + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_page_id', + url_name='image', url_path='image' + ) + @cache_control(private=True) + def document_page_image(self, request, *args, **kwargs): + """ + asdasd + """ + transformation_dict = { + 'kwargs': {}, + 'name': {} + } + transformation_list = [] + + querystring = furl() + querystring.args.update(self.request.GET) + querystring.args.update(self.request.POST) + + for key, value in querystring.args.items(): + if key.startswith('transformation_'): + literal, index, element = key.split('_') + transformation_dict[element][index] = value + + for order, identifier in transformation_dict['name'].items(): + if order in transformation_dict['kwargs'].keys(): + kwargs = {} + for kwargs_entry in transformation_dict['kwargs'][order].split(','): + key, value = kwargs_entry.split(':') + kwargs[key] = float(value) + + transformation_list.append({ + 'name': identifier, + 'kwargs': kwargs + }) + + width = request.GET.get('width') + height = request.GET.get('height') + zoom = request.GET.get('zoom') + + if zoom: + zoom = int(zoom) + + rotation = request.GET.get('rotation') + + if rotation: + rotation = int(rotation) + + task = task_generate_document_page_image.apply_async( + kwargs=dict( + document_page_id=self.get_object().pk, width=width, + height=height, zoom=zoom, rotation=rotation, + transformation_list=transformation_list + ) + ) + + cache_filename = task.get(timeout=DOCUMENT_IMAGE_TASK_TIMEOUT) + with self.get_object().cache_partition.get_file(filename=cache_filename).open() as file_object: + response = HttpResponse(file_object.read(), content_type='image') + if '_hash' in request.GET: + patch_cache_control( + response, + max_age=settings_document_page_image_cache_time.value + ) + return response + + + """ + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_page_id', + url_name='document-page-list', url_path='document_pages' + ) + def document_page_list(self, request, *args, **kwargs): + serializer = DocumentPageSerializer( + instance=self.get_object().versions.all(), many=True, + context={'request': request} + ) + return Response(serializer.data) + """ + +class DocumentTypeViewSet(viewsets.ModelViewSet): + lookup_field = 'pk' + lookup_url_kwarg = 'document_type_id' + queryset = DocumentType.objects.all() + serializer_class = DocumentTypeSerializer + + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_type_id', + url_name='document-list', url_path='documents' + ) + def document_list(self, request, *args, **kwargs): + serializer = DocumentSerializer( + instance=self.get_object().documents.all(), many=True, + context={'request': request} + ) + return Response(serializer.data) + + +class DocumentVersionViewSet(viewsets.ModelViewSet): + lookup_field = 'pk' + lookup_url_kwarg = 'document_version_id' + serializer_class = DocumentVersionSerializer + + """ + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_version_id', + url_name='document-pages-list', url_path='document_pages' + ) + def document_pages_list(self, request, *args, **kwargs): + serializer = DocumentPageSerializer( + instance=self.get_object().pages.all(), many=True, + context={'request': request} + ) + return Response(serializer.data) + """ + def get_queryset(self): + return get_object_or_404( + klass=Document, pk=self.kwargs['document_id'] + ).versions.all() + + +''' + + + class APIDeletedDocumentListView(generics.ListAPIView): """ Returns a list of all the trashed documents. """ filter_backends = (MayanObjectPermissionsFilter,) - lookup_url_kwarg = 'document_pk' + lookup_url_kwarg = 'document_id' mayan_object_permissions = {'GET': (permission_document_view,)} permission_classes = (MayanPermission,) queryset = Document.trash.all() @@ -59,9 +220,9 @@ class APIDeletedDocumentView(generics.RetrieveDestroyAPIView): delete: Delete the trashed document. get: Retreive the details of the trashed document. """ - lookup_url_kwarg = 'document_pk' + lookup_url_kwarg = 'document_id' mayan_object_permissions = { - 'DELETE': (permission_document_delete,), + 'DELETE': (permission_trashed_document_delete,), 'GET': (permission_document_view,) } permission_classes = (MayanPermission,) @@ -73,9 +234,9 @@ class APIDeletedDocumentRestoreView(generics.GenericAPIView): """ post: Restore a trashed document. """ - lookup_url_kwarg = 'document_pk' + lookup_url_kwarg = 'document_id' mayan_object_permissions = { - 'POST': (permission_document_restore,) + 'POST': (permission_trashed_document_restore,) } permission_classes = (MayanPermission,) queryset = Document.trash.all() @@ -95,7 +256,7 @@ class APIDocumentDownloadView(DownloadMixin, generics.RetrieveAPIView): """ get: Download the latest version of a document. """ - lookup_url_kwarg = 'document_pk' + lookup_url_kwarg = 'document_id' mayan_object_permissions = { 'GET': (permission_document_download,) } @@ -146,7 +307,7 @@ class APIDocumentListView(generics.ListCreateAPIView): def perform_create(self, serializer): AccessControlList.objects.check_access( - permissions=(permission_document_create,), user=self.request.user, + permission=permission_document_create,), user=self.request.user, obj=serializer.validated_data['document_type'] ) serializer.save(_user=self.request.user) @@ -156,7 +317,7 @@ class APIDocumentPageImageView(generics.RetrieveAPIView): """ get: Returns an image representation of the selected document. """ - lookup_url_kwarg = 'document_page_pk' + lookup_url_kwarg = 'document_page_id' def get_document(self): if self.request.method == 'GET': @@ -165,7 +326,7 @@ class APIDocumentPageImageView(generics.RetrieveAPIView): permission_required = permission_document_edit document = get_object_or_404( - klass=Document.passthrough, pk=self.kwargs['document_pk'] + klass=Document.passthrough, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( @@ -175,7 +336,7 @@ class APIDocumentPageImageView(generics.RetrieveAPIView): def get_document_version(self): return get_object_or_404( - klass=self.get_document().versions.all(), pk=self.kwargs['document_version_pk'] + klass=self.get_document().versions.all(), pk=self.kwargs['document_version_id'] ) def get_queryset(self): @@ -253,7 +414,7 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView): patch: Edit the selected document page. put: Edit the selected document page. """ - lookup_url_kwarg = 'document_page_pk' + lookup_url_kwarg = 'document_page_id' serializer_class = DocumentPageSerializer def get_document(self): @@ -263,7 +424,7 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView): permission_required = permission_document_edit document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( @@ -274,7 +435,7 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView): def get_document_version(self): return get_object_or_404( klass=self.get_document().versions.all(), - pk=self.kwargs['document_version_pk'] + pk=self.kwargs['document_version_id'] ) def get_queryset(self): @@ -287,7 +448,7 @@ class APIDocumentTypeListView(generics.ListCreateAPIView): post: Create a new document type. """ filter_backends = (MayanObjectPermissionsFilter,) - lookup_url_kwarg = 'document_type_pk' + lookup_url_kwarg = 'document_type_id' mayan_object_permissions = {'GET': (permission_document_type_view,)} mayan_view_permissions = {'POST': (permission_document_type_create,)} permission_classes = (MayanPermission,) @@ -314,7 +475,7 @@ class APIDocumentTypeView(generics.RetrieveUpdateDestroyAPIView): patch: Edit the properties of the selected document type. put: Edit the properties of the selected document type. """ - lookup_url_kwarg = 'document_type_pk' + lookup_url_kwarg = 'document_type_id' mayan_object_permissions = { 'GET': (permission_document_type_view,), 'PUT': (permission_document_type_edit,), @@ -342,13 +503,13 @@ class APIDocumentTypeDocumentListView(generics.ListAPIView): Returns a list of all the documents of a particular document type. """ filter_backends = (MayanObjectPermissionsFilter,) - lookup_url_kwarg = 'document_type_pk' + lookup_url_kwarg = 'document_type_id' mayan_object_permissions = {'GET': (permission_document_view,)} serializer_class = DocumentSerializer def get_queryset(self): document_type = get_object_or_404( - klass=DocumentType, pk=self.kwargs['document_type_pk'] + klass=DocumentType, pk=self.kwargs['document_type_id'] ) AccessControlList.objects.check_access( permissions=permission_document_type_view, user=self.request.user, @@ -362,15 +523,15 @@ class APIDocumentVersionDownloadView(DownloadMixin, generics.RetrieveAPIView): """ get: Download a document version. """ - lookup_url_kwarg = 'document_version_pk' + lookup_url_kwarg = 'document_version_id' def get_document(self): document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( - permissions=(permission_document_download,), user=self.request.user, + permission=permission_document_download,), user=self.request.user, obj=document ) return document @@ -418,7 +579,7 @@ class APIDocumentView(generics.RetrieveUpdateDestroyAPIView): patch: Edit the properties of the selected document. put: Edit the properties of the selected document. """ - lookup_url_kwarg = 'document_pk' + lookup_url_kwarg = 'document_id' mayan_object_permissions = { 'GET': (permission_document_view,), 'PUT': (permission_document_properties_edit,), @@ -463,7 +624,7 @@ class APIDocumentVersionPageListView(generics.ListAPIView): def get_document(self): document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( @@ -474,7 +635,7 @@ class APIDocumentVersionPageListView(generics.ListAPIView): def get_document_version(self): return get_object_or_404( klass=self.get_document().versions.all(), - pk=self.kwargs['document_version_pk'] + pk=self.kwargs['document_version_id'] ) def get_queryset(self): @@ -490,7 +651,7 @@ class APIDocumentVersionsListView(generics.ListCreateAPIView): mayan_object_permissions = { 'GET': (permission_document_version_view,), } - mayan_permission_attribute_check = 'document' + #mayan_permission_attribute_check = 'document' permission_classes = (MayanPermission,) def create(self, request, *args, **kwargs): @@ -514,16 +675,16 @@ class APIDocumentVersionsListView(generics.ListCreateAPIView): def get_queryset(self): return get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ).versions.all() def perform_create(self, serializer): document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( - permissions=(permission_document_new_version,), + permission=permission_document_new_version,), user=self.request.user, obj=document ) serializer.save(document=document, _user=self.request.user) @@ -536,7 +697,7 @@ class APIDocumentVersionView(generics.RetrieveUpdateDestroyAPIView): patch: Edit the selected document version. put: Edit the selected document version. """ - lookup_url_kwarg = 'document_version_pk' + lookup_url_kwarg = 'document_version_id' def get_document(self): if self.request.method == 'GET': @@ -547,7 +708,7 @@ class APIDocumentVersionView(generics.RetrieveUpdateDestroyAPIView): permission_required = permission_document_edit document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] + klass=Document, pk=self.kwargs['document_id'] ) AccessControlList.objects.check_access( @@ -569,3 +730,4 @@ class APIDocumentVersionView(generics.RetrieveUpdateDestroyAPIView): return DocumentVersionSerializer else: return WritableDocumentVersionSerializer +''' diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py index 46afbccd13..2aad41516e 100644 --- a/mayan/apps/documents/apps.py +++ b/mayan/apps/documents/apps.py @@ -53,17 +53,17 @@ from .handlers import ( ) from .links import ( link_clear_image_cache, link_document_clear_transformations, - link_document_clone_transformations, link_document_delete, - link_document_document_type_edit, link_document_download, + link_document_clone_transformations, link_trashed_document_delete, + link_document_change_type, link_document_download, link_document_duplicates_list, link_document_edit, link_document_favorites_add, link_document_favorites_remove, - link_document_list, link_document_list_deleted, + link_document_list, link_trashed_document_list, link_document_list_favorites, link_document_list_recent_access, link_document_list_recent_added, link_document_multiple_clear_transformations, - link_document_multiple_delete, link_document_multiple_document_type_edit, + link_trashed_document_multiple_delete, link_document_multiple_change_type, link_document_multiple_download, link_document_multiple_favorites_add, - link_document_multiple_favorites_remove, link_document_multiple_restore, + link_document_multiple_favorites_remove, link_trashed_document_multiple_restore, link_document_multiple_trash, link_document_multiple_update_page_count, link_document_page_navigation_first, link_document_page_navigation_last, link_document_page_navigation_next, link_document_page_navigation_previous, @@ -72,7 +72,7 @@ from .links import ( link_document_page_view_reset, link_document_page_zoom_in, link_document_page_zoom_out, link_document_pages, link_document_preview, link_document_print, link_document_properties, - link_document_quick_download, link_document_restore, link_document_trash, + link_document_quick_download, link_trashed_document_restore, link_document_trash, link_document_type_create, link_document_type_delete, link_document_type_edit, link_document_type_filename_create, link_document_type_filename_delete, link_document_type_filename_edit, @@ -90,14 +90,14 @@ from .literals import ( ) from .menus import menu_documents from .permissions import ( - permission_document_create, permission_document_delete, + permission_document_create, permission_document_download, permission_document_edit, permission_document_new_version, permission_document_print, - permission_document_properties_edit, permission_document_restore, - permission_document_trash, permission_document_type_delete, - permission_document_type_edit, permission_document_type_view, - permission_document_version_revert, permission_document_version_view, - permission_document_view + permission_document_properties_edit, permission_document_trash, + permission_document_type_delete, permission_document_type_edit, + permission_document_type_view, permission_document_version_revert, + permission_document_version_view, permission_document_view, + permission_trashed_document_delete, permission_trashed_document_restore ) from .queues import * # NOQA # Just import to initialize the search models @@ -119,14 +119,18 @@ class DocumentsApp(MayanAppConfig): super(DocumentsApp, self).ready() from actstream import registry - DeletedDocument = self.get_model('DeletedDocument') - Document = self.get_model('Document') - DocumentPage = self.get_model('DocumentPage') - DocumentPageSearchResult = self.get_model('DocumentPageSearchResult') - DocumentType = self.get_model('DocumentType') - DocumentTypeFilename = self.get_model('DocumentTypeFilename') - DocumentVersion = self.get_model('DocumentVersion') - DuplicatedDocumentProxy = self.get_model('DuplicatedDocumentProxy') + Document = self.get_model(model_name='Document') + DocumentPage = self.get_model(model_name='DocumentPage') + DocumentPageSearchResult = self.get_model( + model_name='DocumentPageSearchResult' + ) + DocumentType = self.get_model(model_name='DocumentType') + DocumentTypeFilename = self.get_model(model_name='DocumentTypeFilename') + DocumentVersion = self.get_model(model_name='DocumentVersion') + DuplicatedDocumentProxy = self.get_model( + model_name='DuplicatedDocumentProxy' + ) + TrashedDocument = self.get_model(model_name='TrashedDocument') DynamicSerializerField.add_serializer( klass=Document, @@ -185,15 +189,16 @@ class DocumentsApp(MayanAppConfig): ModelPermission.register( model=Document, permissions=( permission_acl_edit, permission_acl_view, - permission_document_delete, permission_document_download, - permission_document_edit, permission_document_new_version, - permission_document_print, permission_document_properties_edit, - permission_document_restore, permission_document_trash, + permission_document_download, permission_document_edit, + permission_document_new_version, permission_document_print, + permission_document_properties_edit, permission_document_trash, permission_document_version_revert, permission_document_version_view, permission_document_view, permission_events_view, permission_transformation_create, permission_transformation_delete, permission_transformation_edit, permission_transformation_view, + permission_trashed_document_delete, + permission_trashed_document_restore ) ) @@ -297,22 +302,22 @@ class DocumentsApp(MayanAppConfig): widget=TwoStateWidget ) - # DeletedDocument + # TrashedDocument SourceColumn( attribute='label', is_identifier=True, is_sortable=True, - source=DeletedDocument + source=TrashedDocument ) SourceColumn( func=lambda context: document_page_thumbnail_widget.render( instance=context['object'] - ), label=_('Thumbnail'), source=DeletedDocument + ), label=_('Thumbnail'), source=TrashedDocument ) SourceColumn( - attribute='document_type', is_sortable=True, source=DeletedDocument + attribute='document_type', is_sortable=True, source=TrashedDocument ) SourceColumn( - attribute='get_rendered_deleted_date_time', source=DeletedDocument + attribute='get_rendered_deleted_date_time', source=TrashedDocument ) # DocumentVersion @@ -445,7 +450,7 @@ class DocumentsApp(MayanAppConfig): links=( link_document_list_recent_access, link_document_list_recent_added, link_document_list_favorites, - link_document_list, link_document_list_deleted, + link_document_list, link_trashed_document_list, link_duplicated_document_list, ) ) @@ -493,7 +498,7 @@ class DocumentsApp(MayanAppConfig): menu_sidebar.bind_links( links=(link_trash_can_empty,), sources=( - 'documents:document_list_deleted', 'documents:trash_can_empty' + 'documents:trashed_document_list', 'documents:trash_can_empty' ) ) @@ -501,7 +506,7 @@ class DocumentsApp(MayanAppConfig): menu_object.bind_links( links=( link_document_favorites_add, link_document_favorites_remove, - link_document_edit, link_document_document_type_edit, + link_document_edit, link_document_change_type, link_document_print, link_document_trash, link_document_quick_download, link_document_download, link_document_clear_transformations, @@ -510,8 +515,8 @@ class DocumentsApp(MayanAppConfig): ), sources=(Document,) ) menu_object.bind_links( - links=(link_document_restore, link_document_delete), - sources=(DeletedDocument,) + links=(link_trashed_document_restore, link_trashed_document_delete), + sources=(TrashedDocument,) ) # Document facet links @@ -548,13 +553,14 @@ class DocumentsApp(MayanAppConfig): link_document_multiple_clear_transformations, link_document_multiple_trash, link_document_multiple_download, link_document_multiple_update_page_count, - link_document_multiple_document_type_edit, + link_document_multiple_change_type, ), sources=(Document,) ) menu_multi_item.bind_links( links=( - link_document_multiple_restore, link_document_multiple_delete - ), sources=(DeletedDocument,) + link_trashed_document_multiple_restore, + link_trashed_document_multiple_delete + ), sources=(TrashedDocument,) ) # Document pages @@ -614,7 +620,7 @@ class DocumentsApp(MayanAppConfig): receiver=handler_scan_duplicates_for, ) - registry.register(DeletedDocument) + registry.register(TrashedDocument) registry.register(Document) registry.register(DocumentType) registry.register(DocumentVersion) diff --git a/mayan/apps/documents/dashboard_widgets.py b/mayan/apps/documents/dashboard_widgets.py index e862c98b1c..887ccd8f36 100644 --- a/mayan/apps/documents/dashboard_widgets.py +++ b/mayan/apps/documents/dashboard_widgets.py @@ -32,17 +32,19 @@ class DashboardWidgetDocumentPagesTotal(DashboardWidgetNumeric): DocumentPage = apps.get_model( app_label='documents', model_name='DocumentPage' ) - self.count = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=request.user, - queryset=DocumentPage.objects.all() + self.count = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, + queryset=DocumentPage.objects.all(), user=request.user ).count() - return super(DashboardWidgetDocumentPagesTotal, self).render(request) + return super(DashboardWidgetDocumentPagesTotal, self).render( + request=request + ) class DashboardWidgetDocumentsTotal(DashboardWidgetNumeric): icon_class = icon_dashboard_total_document label = _('Total documents') - link = reverse_lazy('documents:document_list') + link = reverse_lazy(viewname='documents:document_list') def render(self, request): AccessControlList = apps.get_model( @@ -51,36 +53,40 @@ class DashboardWidgetDocumentsTotal(DashboardWidgetNumeric): Document = apps.get_model( app_label='documents', model_name='Document' ) - self.count = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=request.user, - queryset=Document.objects.all() + self.count = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, + queryset=Document.objects.all(), user=request.user ).count() - return super(DashboardWidgetDocumentsTotal, self).render(request) + return super(DashboardWidgetDocumentsTotal, self).render( + request=request + ) class DashboardWidgetDocumentsInTrash(DashboardWidgetNumeric): icon_class = icon_dashboard_documents_in_trash label = _('Documents in trash') - link = reverse_lazy('documents:document_list_deleted') + link = reverse_lazy(viewname='documents:trashed_document_list') def render(self, request): AccessControlList = apps.get_model( app_label='acls', model_name='AccessControlList' ) - DeletedDocument = apps.get_model( - app_label='documents', model_name='DeletedDocument' + TrashedDocument = apps.get_model( + app_label='documents', model_name='TrashedDocument' ) - self.count = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=request.user, - queryset=DeletedDocument.objects.all() + self.count = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, + queryset=TrashedDocument.objects.all(), user=request.user ).count() - return super(DashboardWidgetDocumentsInTrash, self).render(request) + return super(DashboardWidgetDocumentsInTrash, self).render( + request=request + ) class DashboardWidgetDocumentsTypesTotal(DashboardWidgetNumeric): icon_class = icon_dashboard_document_types label = _('Document types') - link = reverse_lazy('documents:document_type_list') + link = reverse_lazy(viewname='documents:document_type_list') def render(self, request): AccessControlList = apps.get_model( @@ -89,11 +95,13 @@ class DashboardWidgetDocumentsTypesTotal(DashboardWidgetNumeric): DocumentType = apps.get_model( app_label='documents', model_name='DocumentType' ) - self.count = AccessControlList.objects.filter_by_access( - permission=permission_document_type_view, user=request.user, - queryset=DocumentType.objects.all() + self.count = AccessControlList.objects.restrict_queryset( + permission=permission_document_type_view, + queryset=DocumentType.objects.all(), user=request.user ).count() - return super(DashboardWidgetDocumentsTypesTotal, self).render(request) + return super(DashboardWidgetDocumentsTypesTotal, self).render( + request=request + ) class DashboardWidgetDocumentsNewThisMonth(DashboardWidgetNumeric): @@ -106,7 +114,9 @@ class DashboardWidgetDocumentsNewThisMonth(DashboardWidgetNumeric): def render(self, request): self.count = new_documents_this_month(user=request.user) - return super(DashboardWidgetDocumentsNewThisMonth, self).render(request) + return super(DashboardWidgetDocumentsNewThisMonth, self).render( + request=request + ) class DashboardWidgetDocumentsPagesNewThisMonth(DashboardWidgetNumeric): @@ -119,4 +129,6 @@ class DashboardWidgetDocumentsPagesNewThisMonth(DashboardWidgetNumeric): def render(self, request): self.count = new_document_pages_this_month(user=request.user) - return super(DashboardWidgetDocumentsPagesNewThisMonth, self).render(request) + return super(DashboardWidgetDocumentsPagesNewThisMonth, self).render( + request=request + ) diff --git a/mayan/apps/documents/events.py b/mayan/apps/documents/events.py index 817970f27d..b841730d62 100644 --- a/mayan/apps/documents/events.py +++ b/mayan/apps/documents/events.py @@ -4,35 +4,35 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.events import EventTypeNamespace -namespace = EventTypeNamespace(name='documents', label=_('Documents')) +namespace = EventTypeNamespace(label=_('Documents'), name='documents') event_document_create = namespace.add_event_type( - name='document_create', label=_('Document created') + label=_('Document created'), name='document_create' ) event_document_download = namespace.add_event_type( - name='document_download', label=_('Document downloaded') + label=_('Document downloaded'), name='document_download' ) event_document_new_version = namespace.add_event_type( - name='document_new_version', label=_('New version uploaded') + label=_('New version uploaded'), name='document_new_version' ) event_document_properties_edit = namespace.add_event_type( - name='document_edit', label=_('Document properties edited') + label=_('Document properties edited'), name='document_edit' ) # The type of an existing document is changed to another type event_document_type_change = namespace.add_event_type( - name='document_type_change', label=_('Document type changed') + label=_('Document type changed'), name='document_type_change' ) # A document type is created event_document_type_created = namespace.add_event_type( - name='document_type_created', label=_('Document type created') + label=_('Document type created'), name='document_type_created' ) # An existing document type is modified event_document_type_edited = namespace.add_event_type( - name='document_type_edit', label=_('Document type edited') + label=_('Document type edited'), name='document_type_edit' ) event_document_version_revert = namespace.add_event_type( - name='document_version_revert', label=_('Document version reverted') + label=_('Document version reverted'), name='document_version_revert' ) event_document_view = namespace.add_event_type( - name='document_view', label=_('Document viewed') + label=_('Document viewed'), name='document_view' ) diff --git a/mayan/apps/documents/forms/document_type_forms.py b/mayan/apps/documents/forms/document_type_forms.py index f3795803c3..d6da439be0 100644 --- a/mayan/apps/documents/forms/document_type_forms.py +++ b/mayan/apps/documents/forms/document_type_forms.py @@ -45,7 +45,7 @@ class DocumentTypeFilteredSelectForm(forms.Form): queryset = DocumentType.objects.all() if permission: - queryset = AccessControlList.objects.filter_by_access( + queryset = AccessControlList.objects.restrict_queryset( permission=permission, queryset=queryset, user=user ) diff --git a/mayan/apps/documents/icons.py b/mayan/apps/documents/icons.py index 5e3a005029..a0e68084cc 100644 --- a/mayan/apps/documents/icons.py +++ b/mayan/apps/documents/icons.py @@ -18,9 +18,6 @@ icon_dashboard_new_documents_this_month = Icon( icon_dashboard_total_document = Icon( driver_name='fontawesome', symbol='file' ) -icon_document_delete = Icon( - driver_name='fontawesome', symbol='times' -) icon_document_download = Icon( driver_name='fontawesome', symbol='download' ) @@ -42,9 +39,6 @@ icon_document_image_loading = Icon( driver_name='fontawesomecss', css_classes='far fa-clock fa-2x' ) icon_document_list = Icon(driver_name='fontawesome', symbol='book') -icon_document_list_deleted = Icon( - driver_name='fontawesome', symbol='trash-alt' -) icon_document_list_favorites = Icon(driver_name='fontawesome', symbol='star') icon_document_list_recent_access = Icon( driver_name='fontawesome', symbol='clock' @@ -52,12 +46,6 @@ icon_document_list_recent_access = Icon( icon_document_list_recent_added = Icon( driver_name='fontawesome', symbol='asterisk' ) -icon_document_multiple_delete = Icon( - driver_name='fontawesome', symbol='trash-alt' -) -icon_document_multiepl_restore = Icon( - driver_name='fontawesome', symbol='recycle' -) icon_document_page_navigation_first = Icon( driver_name='fontawesome', symbol='step-backward' ) @@ -95,7 +83,6 @@ icon_document_print = Icon( driver_name='fontawesome', symbol='print' ) icon_document_properties = Icon(driver_name='fontawesome', symbol='info') -icon_document_restore = Icon(driver_name='fontawesome', symbol='recycle') icon_document_trash = Icon( driver_name='fontawesome', symbol='trash-alt' ) @@ -141,3 +128,18 @@ icon_menu_documents = Icon(driver_name='fontawesome', symbol='book') icon_trash_can_empty = Icon( driver_name='fontawesome', symbol='trash-alt' ) +icon_trashed_document_delete = Icon( + driver_name='fontawesome', symbol='times' +) +icon_trashed_document_list = Icon( + driver_name='fontawesome', symbol='trash-alt' +) +icon_trashed_document_multiple_delete = Icon( + driver_name='fontawesome', symbol='trash-alt' +) +icon_trashed_document_multiple_restore = Icon( + driver_name='fontawesome', symbol='recycle' +) +icon_trashed_document_restore = Icon( + driver_name='fontawesome', symbol='recycle' +) diff --git a/mayan/apps/documents/links.py b/mayan/apps/documents/links.py index 8478b5b6c5..218c35180c 100644 --- a/mayan/apps/documents/links.py +++ b/mayan/apps/documents/links.py @@ -9,19 +9,18 @@ from mayan.apps.navigation import Link from .icons import ( icon_clear_image_cache, icon_document_duplicates_list, icon_document_list, - icon_document_list_deleted, icon_document_list_favorites, + icon_document_list_favorites, icon_document_list_recent_access, icon_document_list_recent_added, - icon_document_delete, icon_document_download, icon_document_edit, + icon_trashed_document_delete, icon_document_download, icon_document_edit, icon_document_favorites_add, icon_document_favorites_remove, - icon_document_multiple_delete, - icon_document_multiple_restore, icon_document_page_navigation_first, + icon_document_page_navigation_first, icon_document_page_navigation_last, icon_document_page_navigation_next, icon_document_page_navigation_previous, icon_document_page_return, icon_document_page_rotate_left, icon_document_page_rotate_right, icon_document_page_view, icon_document_page_view_reset, icon_document_page_zoom_in, icon_document_page_zoom_out, icon_document_pages, icon_document_preview, - icon_document_print, icon_document_properties, icon_document_restore, + icon_document_print, icon_document_properties, icon_trashed_document_restore, icon_document_trash, icon_document_type_create, icon_document_type_delete, icon_document_type_edit, icon_document_type_filename, icon_document_type_filename_create, @@ -30,17 +29,20 @@ from .icons import ( icon_document_version_list, icon_document_version_return_document, icon_document_version_return_list, icon_document_version_view, icon_duplicated_document_list, icon_duplicated_document_scan, - icon_trash_can_empty + icon_trash_can_empty, + icon_trashed_document_list, + icon_trashed_document_multiple_delete, + icon_trashed_document_multiple_restore, ) from .permissions import ( - permission_document_delete, permission_document_download, - permission_document_print, permission_document_properties_edit, - permission_document_restore, permission_document_tools, + permission_document_download, permission_document_print, + permission_document_properties_edit, permission_document_tools, permission_document_trash, permission_document_type_create, permission_document_type_delete, permission_document_type_edit, permission_document_type_view, permission_document_version_revert, permission_document_version_view, permission_document_view, - permission_empty_trash + permission_empty_trash, permission_trashed_document_delete, + permission_trashed_document_restore ) from .settings import setting_zoom_max_level, setting_zoom_min_level @@ -74,22 +76,26 @@ def is_min_zoom(context): # Facet link_document_preview = Link( - args='resolved_object.id', icon_class=icon_document_preview, + icon_class=icon_document_preview, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_view, text=_('Preview'), view='documents:document_preview' ) link_document_properties = Link( - args='resolved_object.id', icon_class=icon_document_properties, + icon_class=icon_document_properties, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_view, text=_('Properties'), view='documents:document_properties' ) link_document_version_list = Link( - args='resolved_object.pk', icon_class=icon_document_version_list, + icon_class=icon_document_version_list, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_version_view, text=_('Versions'), view='documents:document_version_list' ) link_document_pages = Link( - args='resolved_object.pk', icon_class=icon_document_pages, + icon_class=icon_document_pages, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_view, text=_('Pages'), view='documents:document_pages' ) @@ -97,68 +103,68 @@ link_document_pages = Link( # Actions link_document_clear_transformations = Link( args='resolved_object.id', permission=permission_transformation_delete, + kwargs={'document_id': 'resolved_object.id'}, text=_('Clear transformations'), view='documents:document_clear_transformations' ) link_document_clone_transformations = Link( - args='resolved_object.id', permission=permission_transformation_edit, + permission=permission_transformation_edit, + kwargs={'document_id': 'resolved_object.id'}, text=_('Clone transformations'), view='documents:document_clone_transformations' ) -link_document_delete = Link( - args='resolved_object.id', icon_class=icon_document_delete, - permission=permission_document_delete, tags='dangerous', - text=_('Delete'), view='documents:document_delete' -) link_document_favorites_add = Link( - args='resolved_object.id', icon_class=icon_document_favorites_add, + icon_class=icon_document_favorites_add, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_view, text=_('Add to favorites'), view='documents:document_add_to_favorites' ) link_document_favorites_remove = Link( - args='resolved_object.id', icon_class=icon_document_favorites_remove, + icon_class=icon_document_favorites_remove, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_view, text=_('Remove from favorites'), view='documents:document_remove_from_favorites' ) link_document_trash = Link( - args='resolved_object.id', icon_class=icon_document_trash, + icon_class=icon_document_trash, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_trash, tags='dangerous', text=_('Move to trash'), view='documents:document_trash' ) link_document_edit = Link( - args='resolved_object.id', icon_class=icon_document_edit, + icon_class=icon_document_edit, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_properties_edit, text=_('Edit properties'), view='documents:document_edit' ) -link_document_document_type_edit = Link( - args='resolved_object.id', +link_document_change_type = Link( + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_properties_edit, text=_('Change type'), - view='documents:document_document_type_edit' + view='documents:document_change_type' ) link_document_download = Link( - args='resolved_object.id', icon_class=icon_document_download, + icon_class=icon_document_download, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_download, text=_('Advanced download'), view='documents:document_download_form' ) link_document_print = Link( - args='resolved_object.id', icon_class=icon_document_print, + icon_class=icon_document_print, + kwargs={'document_id': 'resolved_object.id'}, permission=permission_document_print, text=_('Print'), view='documents:document_print' ) link_document_quick_download = Link( - args='resolved_object.id', permission=permission_document_download, + permission=permission_document_download, + kwargs={'document_id': 'resolved_object.id'}, text=_('Quick download'), view='documents:document_download' ) link_document_update_page_count = Link( - args='resolved_object.pk', permission=permission_document_tools, + kwargs={'document_id': 'resolved_object.id'}, + permission=permission_document_tools, text=_('Recalculate page count'), view='documents:document_update_page_count' ) -link_document_restore = Link( - args='object.pk', icon_class=icon_document_restore, - permission=permission_document_restore, text=_('Restore'), - view='documents:document_restore' -) link_document_multiple_clear_transformations = Link( permission=permission_transformation_delete, text=_('Clear transformations'), @@ -168,10 +174,6 @@ link_document_multiple_trash = Link( tags='dangerous', text=_('Move to trash'), view='documents:document_multiple_trash' ) -link_document_multiple_delete = Link( - icon_class=icon_document_multiple_delete, tags='dangerous', - text=_('Delete'), view='documents:document_multiple_delete' -) link_document_multiple_favorites_add = Link( text=_('Add to favorites'), view='documents:document_multiple_add_to_favorites' @@ -180,9 +182,9 @@ link_document_multiple_favorites_remove = Link( text=_('Remove from favorites'), view='documents:document_multiple_remove_from_favorites' ) -link_document_multiple_document_type_edit = Link( +link_document_multiple_change_type = Link( text=_('Change type'), - view='documents:document_multiple_document_type_edit' + view='documents:document_multiple_change_type' ) link_document_multiple_download = Link( text=_('Advanced download'), @@ -192,31 +194,54 @@ link_document_multiple_update_page_count = Link( text=_('Recalculate page count'), view='documents:document_multiple_update_page_count' ) -link_document_multiple_restore = Link( - icon_class=icon_document_multiple_restore, text=_('Restore'), - view='documents:document_multiple_restore' + +link_trashed_document_delete = Link( + icon_class=icon_trashed_document_delete, + kwargs={'trashed_document_id': 'resolved_object.id'}, + permission=permission_trashed_document_delete, tags='dangerous', + text=_('Delete'), view='documents:trashed_document_delete' +) +link_trashed_document_list = Link( + icon_class=icon_trashed_document_list, text=_('Trash can'), + view='documents:trashed_document_list' +) +link_trashed_document_restore = Link( + icon_class=icon_trashed_document_restore, + kwargs={'trashed_document_id': 'resolved_object.id'}, + permission=permission_trashed_document_restore, text=_('Restore'), + view='documents:trashed_document_restore' +) +link_trashed_document_multiple_delete = Link( + icon_class=icon_trashed_document_multiple_delete, tags='dangerous', + text=_('Delete'), view='documents:trashed_document_multiple_delete' +) +link_trashed_document_multiple_restore = Link( + icon_class=icon_trashed_document_multiple_restore, text=_('Restore'), + view='documents:trashed_document_multiple_restore' ) # Versions link_document_version_download = Link( - args='resolved_object.pk', icon_class=icon_document_version_download, + icon_class=icon_document_version_download, + kwargs={'document_version_id': 'resolved_object.pk'}, permission=permission_document_download, text=_('Download version'), view='documents:document_version_download_form' ) link_document_version_return_document = Link( - args='resolved_object.document.pk', icon_class=icon_document_version_return_document, + kwargs={'document_id': 'resolved_object.document.pk'}, permission=permission_document_view, text=_('Document'), view='documents:document_preview' ) link_document_version_return_list = Link( - args='resolved_object.document.pk', icon_class=icon_document_version_return_list, + kwargs={'document_id': 'resolved_object.document.pk'}, permission=permission_document_version_view, text=_('Versions'), view='documents:document_version_list' ) link_document_version_view = Link( - args='resolved_object.pk', icon_class=icon_document_version_view, + icon_class=icon_document_version_view, + kwargs={'document_version_id': 'resolved_object.pk'}, permission=permission_document_version_view, text=_('Preview'), view='documents:document_version_view' ) @@ -238,10 +263,6 @@ link_document_list_recent_added = Link( icon_class=icon_document_list_recent_added, text=_('Recently added'), view='documents:document_list_recent_added' ) -link_document_list_deleted = Link( - icon_class=icon_document_list_deleted, text=_('Trash can'), - view='documents:document_list_deleted' -) # Tools link_clear_image_cache = Link( @@ -260,73 +281,80 @@ link_trash_can_empty = Link( # Document pages link_document_page_navigation_first = Link( - args='resolved_object.pk', conditional_disable=is_first_page, - icon_class=icon_document_page_navigation_first, - keep_query=True, permission=permission_document_view, - text=_('First page'), view='documents:document_page_navigation_first' + conditional_disable=is_first_page, + icon_class=icon_document_page_navigation_first, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, + permission=permission_document_view, text=_('First page'), + view='documents:document_page_navigation_first' ) link_document_page_navigation_last = Link( - args='resolved_object.pk', conditional_disable=is_last_page, - icon_class=icon_document_page_navigation_last, - keep_query=True, text=_('Last page'), - permission=permission_document_view, + conditional_disable=is_last_page, + icon_class=icon_document_page_navigation_last, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, + permission=permission_document_view, text=_('Last page'), view='documents:document_page_navigation_last' ) link_document_page_navigation_previous = Link( - args='resolved_object.pk', conditional_disable=is_first_page, - icon_class=icon_document_page_navigation_previous, - keep_query=True, permission=permission_document_view, - text=_('Previous page'), + conditional_disable=is_first_page, + icon_class=icon_document_page_navigation_previous, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, + permission=permission_document_view, text=_('Previous page'), view='documents:document_page_navigation_previous' ) link_document_page_navigation_next = Link( - args='resolved_object.pk', conditional_disable=is_last_page, - icon_class=icon_document_page_navigation_next, - keep_query=True, text=_('Next page'), + conditional_disable=is_last_page, + icon_class=icon_document_page_navigation_next, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, text=_('Next page'), permission=permission_document_view, view='documents:document_page_navigation_next' ) link_document_page_return = Link( - args='resolved_object.document.pk', icon_class=icon_document_page_return, + icon_class=icon_document_page_return, + kwargs={'document_id': 'resolved_object.document.pk'}, permission=permission_document_view, text=_('Document'), view='documents:document_preview' ) link_document_page_rotate_left = Link( - args='resolved_object.pk', icon_class=icon_document_page_rotate_left, - keep_query=True, permission=permission_document_view, - text=_('Rotate left'), view='documents:document_page_rotate_left' + icon_class=icon_document_page_rotate_left, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, + permission=permission_document_view, text=_('Rotate left'), + view='documents:document_page_rotate_left' ) link_document_page_rotate_right = Link( - args='resolved_object.pk', icon_class=icon_document_page_rotate_right, - keep_query=True, permission=permission_document_view, - text=_('Rotate right'), view='documents:document_page_rotate_right' + icon_class=icon_document_page_rotate_right, keep_query=True, + kwargs={'document_page_id': 'resolved_object.pk'}, + permission=permission_document_view, text=_('Rotate right'), + view='documents:document_page_rotate_right' ) link_document_page_view = Link( icon_class=icon_document_page_view, + kwargs={'document_page_id': 'resolved_object.pk'}, permission=permission_document_view, text=_('Page image'), - view='documents:document_page_view', args='resolved_object.pk' + view='documents:document_page_view' ) link_document_page_view_reset = Link( icon_class=icon_document_page_view_reset, + kwargs={'document_page_id': 'resolved_object.pk'}, permission=permission_document_view, text=_('Reset view'), - view='documents:document_page_view_reset', args='resolved_object.pk' + view='documents:document_page_view_reset' ) link_document_page_zoom_in = Link( - args='resolved_object.pk', conditional_disable=is_max_zoom, - icon_class=icon_document_page_zoom_in, keep_query=True, + conditional_disable=is_max_zoom, icon_class=icon_document_page_zoom_in, + keep_query=True, kwargs={'document_page_id': 'resolved_object.pk'}, permission=permission_document_view, text=_('Zoom in'), view='documents:document_page_zoom_in' ) link_document_page_zoom_out = Link( - args='resolved_object.pk', conditional_disable=is_min_zoom, - icon_class=icon_document_page_zoom_out, keep_query=True, + conditional_disable=is_min_zoom, icon_class=icon_document_page_zoom_out, + keep_query=True, kwargs={'document_page_id': 'resolved_object.pk'}, permission=permission_document_view, text=_('Zoom out'), view='documents:document_page_zoom_out' ) # Document versions link_document_version_revert = Link( - args='object.pk', condition=is_not_current_version, + condition=is_not_current_version, + kwargs={'document_version_id': 'object.pk'}, permission=permission_document_version_revert, tags='dangerous', text=_('Revert'), view='documents:document_version_revert' ) @@ -338,32 +366,38 @@ link_document_type_create = Link( text=_('Create document type'), view='documents:document_type_create' ) link_document_type_delete = Link( - args='resolved_object.id', icon_class=icon_document_type_delete, + icon_class=icon_document_type_delete, + kwargs={'document_type_id': 'resolved_object.pk'}, permission=permission_document_type_delete, tags='dangerous', text=_('Delete'), view='documents:document_type_delete' ) link_document_type_edit = Link( - args='resolved_object.id', icon_class=icon_document_type_edit, + icon_class=icon_document_type_edit, + kwargs={'document_type_id': 'resolved_object.pk'}, permission=permission_document_type_edit, text=_('Edit'), view='documents:document_type_edit' ) link_document_type_filename_create = Link( - args='document_type.id', icon_class=icon_document_type_filename_create, + icon_class=icon_document_type_filename_create, + kwargs={'document_type_id': 'resolved_object.pk'}, permission=permission_document_type_edit, text=_('Add quick label to document type'), view='documents:document_type_filename_create' ) link_document_type_filename_delete = Link( - args='resolved_object.id', permission=permission_document_type_edit, + kwargs={'filename_id': 'resolved_object.pk'}, + permission=permission_document_type_edit, tags='dangerous', text=_('Delete'), view='documents:document_type_filename_delete' ) link_document_type_filename_edit = Link( - args='resolved_object.id', permission=permission_document_type_edit, + kwargs={'filename_id': 'resolved_object.pk'}, + permission=permission_document_type_edit, text=_('Edit'), view='documents:document_type_filename_edit' ) link_document_type_filename_list = Link( - args='resolved_object.id', icon_class=icon_document_type_filename, + icon_class=icon_document_type_filename, + kwargs={'document_type_id': 'resolved_object.pk'}, permission=permission_document_type_view, text=_('Quick labels'), view='documents:document_type_filename_list' ) @@ -382,7 +416,8 @@ link_duplicated_document_list = Link( view='documents:duplicated_document_list' ) link_document_duplicates_list = Link( - args='resolved_object.id', icon_class=icon_document_duplicates_list, + icon_class=icon_document_duplicates_list, + kwargs={'document_id': 'resolved_object.pk'}, permission=permission_document_view, text=_('Duplicates'), view='documents:document_duplicates_list' ) diff --git a/mayan/apps/documents/managers.py b/mayan/apps/documents/managers.py index bdc1ec343d..126a68f596 100644 --- a/mayan/apps/documents/managers.py +++ b/mayan/apps/documents/managers.py @@ -40,7 +40,9 @@ class DocumentPageManager(models.Manager): except DocumentVersion.DoesNotExist: raise self.model.DoesNotExist - return self.get(document_version__pk=document_version.pk, page_number=page_number) + return self.get( + document_version__id=document_version.pk, page_number=page_number + ) class DocumentTypeManager(models.Manager): @@ -61,13 +63,13 @@ class DocumentTypeManager(models.Manager): 'Document type: %s, has a deletion period delta of: %s', document_type, delta ) - for document in document_type.deleted_documents.filter(deleted_date_time__lt=now() - delta): + for trashed_document in document_type.trashed_documents.filter(deleted_date_time__lt=now() - delta): logger.info( 'Document "%s" with id: %d, trashed on: %s, exceded ' - 'delete period', document, document.pk, - document.deleted_date_time + 'delete period', trashed_document, trashed_document.pk, + trashed_document.deleted_date_time ) - document.delete() + trashed_document.delete() else: logger.info( 'Document type: %s, has a no retention delta', document_type @@ -120,7 +122,7 @@ class DocumentVersionManager(models.Manager): except Document.DoesNotExist: raise self.model.DoesNotExist - return self.get(document__pk=document.pk, checksum=checksum) + return self.get(document__id=document.pk, checksum=checksum) class DuplicatedDocumentManager(models.Manager): @@ -204,7 +206,7 @@ class FavoriteDocumentManager(models.Manager): except User.DoesNotExist: raise self.model.DoesNotExist - return self.get(document__pk=document.pk, user__pk=user.pk) + return self.get(document__id=document.pk, user__id=user.pk) def get_for_user(self, user): Document = apps.get_model( @@ -254,7 +256,7 @@ class RecentDocumentManager(models.Manager): raise self.model.DoesNotExist return self.get( - document__pk=document.pk, user__pk=user.pk, + document__id=document.pk, user__id=user.pk, datetime_accessed=datetime_accessed ) diff --git a/mayan/apps/documents/models/document_models.py b/mayan/apps/documents/models/document_models.py index 83d7e5167d..7d1c35ad1c 100644 --- a/mayan/apps/documents/models/document_models.py +++ b/mayan/apps/documents/models/document_models.py @@ -31,8 +31,8 @@ from ..signals import post_document_type_change from .document_type_models import DocumentType __all__ = ( - 'Document', 'DeletedDocument', 'DuplicatedDocument', 'FavoriteDocument', - 'RecentDocument' + 'Document', 'DuplicatedDocument', 'FavoriteDocument', 'RecentDocument', + 'TrashedDocument' ) logger = logging.getLogger(__name__) @@ -137,7 +137,7 @@ class Document(models.Model): def get_absolute_url(self): return reverse( viewname='documents:document_preview', - kwargs={'document_pk': self.pk} + kwargs={'document_id': self.pk} ) def get_api_image_url(self, *args, **kwargs): @@ -279,13 +279,6 @@ class Document(models.Model): return DocumentPage.objects.none() -class DeletedDocument(Document): - objects = TrashCanManager() - - class Meta: - proxy = True - - @python_2_unicode_compatible class DuplicatedDocument(models.Model): document = models.ForeignKey( @@ -316,9 +309,9 @@ class DuplicatedDocumentProxy(Document): verbose_name_plural = _('Duplicated documents') def get_duplicate_count(self, user): - queryset = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=user, - queryset=self.get_duplicates() + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, queryset=self.get_duplicates(), + user=user ) return queryset.count() @@ -382,3 +375,10 @@ class RecentDocument(models.Model): def natural_key(self): return (self.datetime_accessed, self.document.natural_key(), self.user.natural_key()) natural_key.dependencies = ['documents.Document', settings.AUTH_USER_MODEL] + + +class TrashedDocument(Document): + objects = TrashCanManager() + + class Meta: + proxy = True diff --git a/mayan/apps/documents/models/document_page_models.py b/mayan/apps/documents/models/document_page_models.py index 6e59882360..7c3037e21a 100644 --- a/mayan/apps/documents/models/document_page_models.py +++ b/mayan/apps/documents/models/document_page_models.py @@ -103,7 +103,7 @@ class DocumentPage(models.Model): def get_absolute_url(self): return reverse( viewname='documents:document_page_view', - kwargs={'document_page_pk': self.pk} + kwargs={'document_page_id': self.pk} ) def get_api_image_url(self, *args, **kwargs): @@ -126,10 +126,10 @@ class DocumentPage(models.Model): final_url = furl() final_url.args = kwargs final_url.path = reverse( - viewname='rest_api:documentpage-image', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document_version.pk, - 'document_page_pk': self.pk + viewname='rest_api:document_page-image', kwargs={ + 'document_id': self.document.pk, + 'document_version_id': self.document_version.pk, + 'document_page_id': self.pk } ) final_url.args['_hash'] = transformations_hash diff --git a/mayan/apps/documents/models/document_type_models.py b/mayan/apps/documents/models/document_type_models.py index b57c123920..39a497d267 100644 --- a/mayan/apps/documents/models/document_type_models.py +++ b/mayan/apps/documents/models/document_type_models.py @@ -73,23 +73,16 @@ class DocumentType(models.Model): return super(DocumentType, self).delete(*args, **kwargs) - @property - def deleted_documents(self): - DeletedDocument = apps.get_model( - app_label='documents', model_name='DeletedDocument' - ) - return DeletedDocument.objects.filter(document_type=self) - def get_absolute_url(self): return reverse( viewname='documents:document_type_document_list', - kwargs={'document_type_pk': self.pk} + kwargs={'document_type_id': self.pk} ) def get_document_count(self, user): - queryset = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=user, - queryset=self.documents + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, queryset=self.documents, + user=user ) return queryset.count() get_document_count.short_description = _('Documents') @@ -134,6 +127,13 @@ class DocumentType(models.Model): return result + @property + def trashed_documents(self): + TrashedDocument = apps.get_model( + app_label='documents', model_name='TrashedDocument' + ) + return TrashedDocument.objects.filter(document_type=self) + @python_2_unicode_compatible class DocumentTypeFilename(models.Model): diff --git a/mayan/apps/documents/models/document_version_models.py b/mayan/apps/documents/models/document_version_models.py index 102ec6d7f8..873ec1ad07 100644 --- a/mayan/apps/documents/models/document_version_models.py +++ b/mayan/apps/documents/models/document_version_models.py @@ -151,7 +151,7 @@ class DocumentVersion(models.Model): def get_absolute_url(self): return reverse( viewname='documents:document_version_view', - kwargs={'document_version_pk': self.pk} + kwargs={'document_version_id': self.pk} ) def get_api_image_url(self, *args, **kwargs): @@ -195,6 +195,7 @@ class DocumentVersion(models.Model): filename, self.get_rendered_timestamp(), extension ) else: + #TODO: use get_rendered_timestamp here return Template( '{{ instance.document }} - {{ instance.timestamp }}' ).render(context=Context({'instance': self})) @@ -205,6 +206,10 @@ class DocumentVersion(models.Model): ) get_rendered_timestamp.short_description = _('Date and time') + @property + def label(self): + return self.get_rendered_string() + def natural_key(self): return (self.checksum, self.document.natural_key()) natural_key.dependencies = ['documents.Document'] @@ -250,9 +255,19 @@ class DocumentVersion(models.Model): self.document, self ) - event_document_version_revert.commit(actor=_user, target=self.document) - for version in self.document.versions.filter(timestamp__gt=self.timestamp): - version.delete() + try: + with transaction.atomic(): + event_document_version_revert.commit( + actor=_user, target=self.document + ) + for version in self.document.versions.filter(timestamp__gt=self.timestamp): + version.delete() + except Exception as exception: + logger.error( + 'Error reverting document version for document "%s"; %s', + self.document, exception + ) + raise def save(self, *args, **kwargs): """ diff --git a/mayan/apps/documents/permissions.py b/mayan/apps/documents/permissions.py index 4cd78f4c89..ed88131c58 100644 --- a/mayan/apps/documents/permissions.py +++ b/mayan/apps/documents/permissions.py @@ -7,62 +7,62 @@ from mayan.apps.permissions import PermissionNamespace namespace = PermissionNamespace(label=_('Documents'), name='documents') permission_document_create = namespace.add_permission( - name='document_create', label=_('Create documents') -) -permission_document_delete = namespace.add_permission( - name='document_delete', label=_('Delete documents') + label=_('Create documents'), name='document_create' ) permission_document_trash = namespace.add_permission( - name='document_trash', label=_('Trash documents') + label=_('Trash documents'), name='document_trash' ) permission_document_download = namespace.add_permission( - name='document_download', label=_('Download documents') + label=_('Download documents'), name='document_download' ) permission_document_edit = namespace.add_permission( - name='document_edit', label=_('Edit documents') + label=_('Edit documents'), name='document_edit' ) permission_document_new_version = namespace.add_permission( - name='document_new_version', label=_('Create new document versions') + label=_('Create new document versions'), name='document_new_version' ) permission_document_properties_edit = namespace.add_permission( - name='document_properties_edit', label=_('Edit document properties') + label=_('Edit document properties'), name='document_properties_edit' ) permission_document_print = namespace.add_permission( - name='document_print', label=_('Print documents') -) -permission_document_restore = namespace.add_permission( - name='document_restore', label=_('Restore trashed document') + label=_('Print documents'), name='document_print' ) permission_document_tools = namespace.add_permission( - name='document_tools', label=_('Execute document modifying tools') + label=_('Execute document modifying tools'), name='document_tools' ) permission_document_version_revert = namespace.add_permission( - name='document_version_revert', - label=_('Revert documents to a previous version') + label=_('Revert documents to a previous version'), + name='document_version_revert' ) permission_document_version_view = namespace.add_permission( - name='document_version_view', - label=_('View documents\' versions list') + label=_('View documents\' versions list'), + name='document_version_view' ) permission_document_view = namespace.add_permission( - name='document_view', label=_('View documents') + label=_('View documents'), name='document_view' ) permission_empty_trash = namespace.add_permission( - name='document_empty_trash', label=_('Empty trash') + label=_('Empty trash'), name='document_empty_trash' +) +permission_trashed_document_delete = namespace.add_permission( + label=_('Delete trashed documents'), name='document_delete' +) +permission_trashed_document_restore = namespace.add_permission( + label=_('Restore trashed document'), name='document_restore' ) setup_namespace = PermissionNamespace( label=_('Document types'), name='documents_types' ) permission_document_type_create = setup_namespace.add_permission( - name='document_type_create', label=_('Create document types') + label=_('Create document types'), name='document_type_create' ) permission_document_type_delete = setup_namespace.add_permission( - name='document_type_delete', label=_('Delete document types') + label=_('Delete document types'), name='document_type_delete' ) permission_document_type_edit = setup_namespace.add_permission( - name='document_type_edit', label=_('Edit document types') + label=_('Edit document types'), name='document_type_edit' ) permission_document_type_view = setup_namespace.add_permission( - name='document_type_view', label=_('View document types') + label=_('View document types'), name='document_type_view' ) diff --git a/mayan/apps/documents/queues.py b/mayan/apps/documents/queues.py index c97f3dc61c..b7215372f3 100644 --- a/mayan/apps/documents/queues.py +++ b/mayan/apps/documents/queues.py @@ -6,55 +6,55 @@ from mayan.apps.common.queues import queue_tools from mayan.apps.task_manager.classes import CeleryQueue queue_converter = CeleryQueue( - name='converter', label=_('Converter'), transient=True + label=_('Converter'), name='converter', transient=True ) queue_documents_periodic = CeleryQueue( - name='documents_periodic', label=_('Documents periodic'), transient=True + label=_('Documents periodic'), name='documents_periodic', transient=True ) queue_uploads = CeleryQueue( - name='uploads', label=_('Uploads') + label=_('Uploads'), name='uploads' ) queue_documents = CeleryQueue( - name='documents', label=_('Documents') + label=_('Documents'), name='documents' ) queue_converter.add_task_type( - name='mayan.apps.documents.tasks.task_generate_document_page_image', - label=_('Generate document page image') + label=_('Generate document page image'), + name='mayan.apps.documents.tasks.task_generate_document_page_image' ) queue_documents.add_task_type( - name='mayan.apps.documents.tasks.task_delete_document', - label=_('Delete a document') + label=_('Delete a document'), + name='mayan.apps.documents.tasks.task_delete_document' ) queue_documents.add_task_type( - name='mayan.apps.documents.tasks.task_clean_empty_duplicate_lists', - label=_('Clean empty duplicate lists') + label=_('Clean empty duplicate lists'), + name='mayan.apps.documents.tasks.task_clean_empty_duplicate_lists' ) queue_documents_periodic.add_task_type( - name='mayan.apps.documents.tasks.task_check_delete_periods', - label=_('Check document type delete periods') + label=_('Check document type delete periods'), + name='mayan.apps.documents.tasks.task_check_delete_periods' ) queue_documents_periodic.add_task_type( - name='mayan.apps.documents.tasks.task_check_trash_periods', - label=_('Check document type trash periods') + label=_('Check document type trash periods'), + name='mayan.apps.documents.tasks.task_check_trash_periods' ) queue_documents_periodic.add_task_type( - name='mayan.apps.documents.tasks.task_delete_stubs', - label=_('Delete document stubs') + label=_('Delete document stubs'), + name='mayan.apps.documents.tasks.task_delete_stubs' ) queue_tools.add_task_type( - name='mayan.apps.documents.tasks.task_clear_image_cache', - label=_('Clear image cache') + label=_('Clear image cache'), + name='mayan.apps.documents.tasks.task_clear_image_cache' ) queue_uploads.add_task_type( - name='mayan.apps.documents.tasks.task_update_page_count', - label=_('Update document page count') + label=_('Update document page count'), + name='mayan.apps.documents.tasks.task_update_page_count' ) queue_uploads.add_task_type( - name='mayan.apps.documents.tasks.task_upload_new_version', - label=_('Upload new document version') + label=_('Upload new document version'), + name='mayan.apps.documents.tasks.task_upload_new_version' ) diff --git a/mayan/apps/documents/serializers.py b/mayan/apps/documents/serializers.py index 3811779877..594ae16f13 100644 --- a/mayan/apps/documents/serializers.py +++ b/mayan/apps/documents/serializers.py @@ -6,6 +6,7 @@ from rest_framework import serializers from rest_framework.reverse import reverse from mayan.apps.common.models import SharedUploadedFile +from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField from .models import ( Document, DocumentPage, DocumentType, DocumentTypeFilename, @@ -16,40 +17,80 @@ from .tasks import task_upload_new_version class DocumentPageSerializer(serializers.HyperlinkedModelSerializer): - document_version_url = serializers.SerializerMethodField() - image_url = serializers.SerializerMethodField() - url = serializers.SerializerMethodField() + #document_versions_url = serializers.SerializerMethodField() + image_url = MultiKwargHyperlinkedIdentityField( + view_kwargs=( + { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_page_id', + }, + { + 'lookup_field': 'document.pk', 'lookup_url_kwarg': 'document_id', + }, + { + 'lookup_field': 'document_version_id', 'lookup_url_kwarg': 'document_version_id', + }, + ), + view_name='rest_api:document_page-image' + ) + #url = serializers.SerializerMethodField() + document_version_url = MultiKwargHyperlinkedIdentityField( + view_kwargs=( + { + 'lookup_field': 'document_version_id', 'lookup_url_kwarg': 'document_version_id', + }, + { + 'lookup_field': 'document.pk', 'lookup_url_kwarg': 'document_id', + } + ), + view_name='rest_api:document_version-detail' + ) + url = MultiKwargHyperlinkedIdentityField( + view_kwargs=( + { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_page_id', + }, + { + 'lookup_field': 'document.pk', 'lookup_url_kwarg': 'document_id', + }, + { + 'lookup_field': 'document_version_id', 'lookup_url_kwarg': 'document_version_id', + }, + ), + view_name='rest_api:document_page-detail' + ) class Meta: fields = ('document_version_url', 'image_url', 'page_number', 'url') + #fields = ('document_version_url', 'page_number', 'url') model = DocumentPage - def get_document_version_url(self, instance): + """ + def get_document_versions_url(self, instance): return reverse( viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.document_version.pk + 'document_id': instance.document.pk, + 'document_version_id': instance.document_version.pk }, request=self.context['request'], format=self.context['format'] ) def get_image_url(self, instance): return reverse( viewname='rest_api:documentpage-image', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.document_version.pk, - 'document_page_pk': instance.pk, + 'document_id': instance.document.pk, + 'document_version_id': instance.document_version.pk, + 'document_page_id': instance.pk, }, request=self.context['request'], format=self.context['format'] ) def get_url(self, instance): return reverse( viewname='rest_api:documentpage-detail', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.document_version.pk, - 'document_page_pk': instance.pk, + 'document_id': instance.document.pk, + 'document_version_id': instance.document_version.pk, + 'document_page_id': instance.pk, }, request=self.context['request'], format=self.context['format'] ) - + """ class DocumentTypeFilenameSerializer(serializers.ModelSerializer): class Meta: @@ -59,33 +100,35 @@ class DocumentTypeFilenameSerializer(serializers.ModelSerializer): class DocumentTypeSerializer(serializers.HyperlinkedModelSerializer): documents_url = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_type_pk', - view_name='rest_api:documenttype-document-list' + lookup_url_kwarg='document_type_id', + view_name='rest_api:document_type-document-list' ) - documents_count = serializers.SerializerMethodField() - filenames = DocumentTypeFilenameSerializer(many=True, read_only=True) + #documents_count = serializers.SerializerMethodField() + #filenames = DocumentTypeFilenameSerializer(many=True, read_only=True) class Meta: extra_kwargs = { 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_pk', - 'view_name': 'rest_api:documenttype-detail' + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_id', + 'view_name': 'rest_api:document_type-detail' } } fields = ( 'delete_time_period', 'delete_time_unit', 'documents_url', - 'documents_count', 'id', 'label', 'filenames', 'trash_time_period', + #'delete_time_period', 'delete_time_unit', + #'documents_count', 'id', 'label', 'filenames', 'trash_time_period', + 'id', 'label', 'trash_time_period', 'trash_time_unit', 'url' ) model = DocumentType - def get_documents_count(self, obj): - return obj.documents.count() - + #def get_documents_count(self, obj): + # return obj.documents.count() +""" class WritableDocumentTypeSerializer(serializers.ModelSerializer): documents_url = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_type_pk', + lookup_field='pk', lookup_url_kwarg='document_type_id', view_name='rest_api:documenttype-document-list' ) documents_count = serializers.SerializerMethodField() @@ -93,7 +136,7 @@ class WritableDocumentTypeSerializer(serializers.ModelSerializer): class Meta: extra_kwargs = { 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_pk', + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_id', 'view_name': 'rest_api:documenttype-detail' } } @@ -106,26 +149,69 @@ class WritableDocumentTypeSerializer(serializers.ModelSerializer): def get_documents_count(self, obj): return obj.documents.count() - +""" class DocumentVersionSerializer(serializers.HyperlinkedModelSerializer): - document_url = serializers.SerializerMethodField() - download_url = serializers.SerializerMethodField() - pages_url = serializers.SerializerMethodField() + #document_url = serializers.SerializerMethodField() + #download_url = serializers.SerializerMethodField() + document_url = serializers.HyperlinkedIdentityField( + lookup_field='document_id', lookup_url_kwarg='document_id', + view_name='rest_api:document-detail' + ) + #pages_url = serializers.SerializerMethodField() + + pages_url = MultiKwargHyperlinkedIdentityField( + view_kwargs=( + { + 'lookup_field': 'document_id', 'lookup_url_kwarg': 'document_id', + }, + { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_version_id', + } + ), + view_name='rest_api:document_page-list' + ) + size = serializers.SerializerMethodField() - url = serializers.SerializerMethodField() + #url = serializers.SerializerMethodField() + url = MultiKwargHyperlinkedIdentityField( + view_kwargs=( + { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_version_id', + }, + { + 'lookup_field': 'document_id', 'lookup_url_kwarg': 'document_id', + }, + ), + view_name='rest_api:document_version-detail' + ) class Meta: extra_kwargs = { - 'document': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_pk', - 'view_name': 'rest_api:document-detail' - }, + #'document': { + # 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_id', + # 'view_name': 'rest_api:document-detail' + #}, 'file': {'use_url': False}, + #'url': { + # 'view_kwargs': ( + # { + # 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_version_id', + # }, + # { + # 'lookup_field': 'document__pk', 'lookup_url_kwarg': 'document_id', + # }, + # ), + # #'lookup_field': 'pk', 'lookup_url_kwarg': 'document_version_id', + # 'view_name': 'rest_api:document_version-detail' + #}, } fields = ( - 'checksum', 'comment', 'document_url', 'download_url', 'encoding', + #'checksum', 'comment', 'document_url', 'download_url', 'encoding', + 'checksum', 'comment', 'document_url', 'encoding', + #'checksum', 'comment', 'encoding', 'file', 'mimetype', 'pages_url', 'size', 'timestamp', 'url' + #'file', 'mimetype', 'size', 'timestamp', 'url' ) model = DocumentVersion read_only_fields = ('document', 'file', 'size') @@ -133,38 +219,62 @@ class DocumentVersionSerializer(serializers.HyperlinkedModelSerializer): def get_size(self, instance): return instance.size + def build_url_field(self, field_name, model_class): + """ + Create a field representing the object's own URL. + """ + field_class = self.serializer_url_field + field_kwargs = {'kwargs': 1} + + return field_class, field_kwargs + + """ def get_document_url(self, instance): return reverse( viewname='rest_api:document-detail', kwargs={ - 'document_pk': instance.document.pk - }, request=self.context['request'], format=self.context['format'] + 'document_id': instance.document.pk + }, request=self.context['request']#, format=self.context['format'] ) def get_download_url(self, instance): return reverse( - viewname='rest_api:documentversion-download', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk - }, request=self.context['request'], format=self.context['format'] + viewname='rest_api:document_version-download', kwargs={ + 'document_id': instance.document.pk, + 'document_version_id': instance.pk + }, request=self.context['request']#, format=self.context['format'] ) def get_pages_url(self, instance): return reverse( - viewname='rest_api:documentversion-page-list', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk - }, request=self.context['request'], format=self.context['format'] + viewname='rest_api:document_version-page-list', kwargs={ + 'document_id': instance.document.pk, + 'document_version_id': instance.pk + }, request=self.context['request']#, format=self.context['format'] ) - def get_url(self, instance): + + def get_url(self, obj, view_name, request, format): + print ' obj, view_name, request, format', obj, view_name, request, format + # Unsaved objects will not yet have a valid URL. + if hasattr(obj, 'pk') and obj.pk in (None, ''): + return None + + lookup_value = getattr(obj, self.lookup_field) + kwargs = {self.lookup_url_kwarg: lookup_value} + return self.reverse(view_name, kwargs=kwargs, request=request, format=format) + """ + """ + + def get_url(self, instance, *args, **kwargs): + print ', *args, **kwargs', args, kwargs return reverse( - viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk - }, request=self.context['request'], format=self.context['format'] + viewname='rest_api:document_version-detail', kwargs={ + 'document_id': instance.document.pk, + 'document_version_id': instance.pk + }, request=self.context['request']#, format=self.context['format'] ) - - + """ +""" class WritableDocumentVersionSerializer(serializers.ModelSerializer): document_url = serializers.SerializerMethodField() download_url = serializers.SerializerMethodField() @@ -185,31 +295,31 @@ class WritableDocumentVersionSerializer(serializers.ModelSerializer): def get_document_url(self, instance): return reverse( viewname='rest_api:document-detail', kwargs={ - 'document_pk': instance.document.pk + 'document_id': instance.document.pk }, request=self.context['request'], format=self.context['format'] ) def get_download_url(self, instance): return reverse( viewname='rest_api:documentversion-download', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk + 'document_id': instance.document.pk, + 'document_version_id': instance.pk }, request=self.context['request'], format=self.context['format'] ) def get_pages_url(self, instance): return reverse( viewname='rest_api:documentversion-page-list', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk + 'document_id': instance.document.pk, + 'document_version_id': instance.pk }, request=self.context['request'], format=self.context['format'] ) def get_url(self, instance): return reverse( viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': instance.document.pk, - 'document_version_pk': instance.pk + 'document_id': instance.document.pk, + 'document_version_id': instance.pk }, request=self.context['request'], format=self.context['format'] ) @@ -228,23 +338,23 @@ class NewDocumentVersionSerializer(serializers.Serializer): document_id=document.pk, shared_uploaded_file_id=shared_uploaded_file.pk, user_id=_user.pk ) - +""" class DeletedDocumentSerializer(serializers.HyperlinkedModelSerializer): document_type_label = serializers.SerializerMethodField() restore = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_pk', + lookup_field='pk', lookup_url_kwarg='document_id', view_name='rest_api:trasheddocument-restore' ) class Meta: extra_kwargs = { 'document_type': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_pk', + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_id', 'view_name': 'rest_api:documenttype-detail' }, 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_pk', + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_id', 'view_name': 'rest_api:trasheddocument-detail' } } @@ -264,41 +374,49 @@ class DeletedDocumentSerializer(serializers.HyperlinkedModelSerializer): class DocumentSerializer(serializers.HyperlinkedModelSerializer): - document_type = DocumentTypeSerializer() + document_type = DocumentTypeSerializer(read_only=True) + #document_type_url = serializers.HyperlinkedIdentityField( + # lookup_field='document_type_id', lookup_url_kwarg='document_type_id', + # view_name='rest_api:document_type-detail' + #) latest_version = DocumentVersionSerializer(many=False, read_only=True) versions_url = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_pk', - view_name='rest_api:document-version-list' + lookup_field='pk', lookup_url_kwarg='document_id', + view_name='rest_api:document_version-list' ) + #view_name='rest_api:document_type-document-list' class Meta: extra_kwargs = { 'document_type': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_pk', - 'view_name': 'rest_api:documenttype-detail' + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_type_id', + 'view_name': 'rest_api:document_type-detail' }, 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_pk', + 'lookup_field': 'pk', 'lookup_url_kwarg': 'document_id', 'view_name': 'rest_api:document-detail' } } fields = ( 'date_added', 'description', 'document_type', 'id', 'label', + #'date_added', 'description', 'document_type_url', 'id', 'label', 'language', 'latest_version', 'url', 'uuid', 'versions_url', + #'language', 'url', 'uuid', 'versions_url' ) model = Document - read_only_fields = ('document_type',) + #read_only_fields = ('document_type', 'label') +""" class WritableDocumentSerializer(serializers.ModelSerializer): document_type = DocumentTypeSerializer(read_only=True) latest_version = DocumentVersionSerializer(many=False, read_only=True) versions = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_pk', + lookup_field='pk', lookup_url_kwarg='document_id', view_name='rest_api:document-version-list' ) url = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='document_pk', + lookup_field='pk', lookup_url_kwarg='document_id', view_name='rest_api:document-detail', ) @@ -344,7 +462,7 @@ class NewDocumentSerializer(serializers.ModelSerializer): 'description', 'document_type', 'id', 'file', 'label', 'language', ) model = Document - +""" class RecentDocumentSerializer(serializers.ModelSerializer): class Meta: diff --git a/mayan/apps/documents/settings.py b/mayan/apps/documents/settings.py index e120b2399d..356ee7a705 100644 --- a/mayan/apps/documents/settings.py +++ b/mayan/apps/documents/settings.py @@ -13,7 +13,7 @@ from .literals import ( ) from .utils import callback_update_cache_size -namespace = Namespace(name='documents', label=_('Documents')) +namespace = Namespace(label=_('Documents'), name='documents') setting_document_cache_maximum_size = namespace.add_setting( global_name='DOCUMENTS_CACHE_MAXIMUM_SIZE', diff --git a/mayan/apps/documents/signals.py b/mayan/apps/documents/signals.py index 0ec434bee1..425172366e 100644 --- a/mayan/apps/documents/signals.py +++ b/mayan/apps/documents/signals.py @@ -2,11 +2,11 @@ from __future__ import unicode_literals from django.dispatch import Signal -post_version_upload = Signal(providing_args=('instance',), use_caching=True) +post_document_created = Signal(providing_args=('instance',), use_caching=True) post_document_type_change = Signal( providing_args=('instance',), use_caching=True ) -post_document_created = Signal(providing_args=('instance',), use_caching=True) post_initial_document_type = Signal( providing_args=('instance',), use_caching=True ) +post_version_upload = Signal(providing_args=('instance',), use_caching=True) diff --git a/mayan/apps/documents/statistics.py b/mayan/apps/documents/statistics.py index d45a89b5a7..754466e27e 100644 --- a/mayan/apps/documents/statistics.py +++ b/mayan/apps/documents/statistics.py @@ -68,9 +68,8 @@ def new_documents_this_month(user=None): queryset = Document.objects.all() if user: - queryset = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=user, - queryset=queryset + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, queryset=queryset, user=user ) qss = qsstats.QuerySetStats(queryset, 'date_added') @@ -110,9 +109,9 @@ def new_document_pages_this_month(user=None): queryset = DocumentPage.objects.all() if user: - queryset = AccessControlList.objects.filter_by_access( - permission=permission_document_view, user=user, - queryset=queryset + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_document_view, queryset=queryset, + user=user ) qss = qsstats.QuerySetStats( @@ -230,44 +229,44 @@ def total_document_page_per_month(): namespace = StatisticNamespace(slug='documents', label=_('Documents')) namespace.add_statistic( - klass=StatisticLineChart, - slug='new-documents-per-month', - label=_('New documents per month'), func=new_documents_per_month, - minute='0' + klass=StatisticLineChart, + label=_('New documents per month'), + minute='0', + slug='new-documents-per-month' ) namespace.add_statistic( - klass=StatisticLineChart, - slug='new-document-versions-per-month', - label=_('New document versions per month'), func=new_document_versions_per_month, - minute='0' + klass=StatisticLineChart, + label=_('New document versions per month'), + minute='0', + slug='new-document-versions-per-month' ) namespace.add_statistic( - klass=StatisticLineChart, - slug='new-document-pages-per-month', - label=_('New document pages per month'), func=new_document_pages_per_month, - minute='0' + klass=StatisticLineChart, + label=_('New document pages per month'), + minute='0', + slug='new-document-pages-per-month' ) namespace.add_statistic( - klass=StatisticLineChart, - slug='total-documents-at-each-month', - label=_('Total documents at each month'), func=total_document_per_month, - minute='0' + klass=StatisticLineChart, + label=_('Total documents at each month'), + minute='0', + slug='total-documents-at-each-month' ) namespace.add_statistic( - klass=StatisticLineChart, - slug='total-document-versions-at-each-month', - label=_('Total document versions at each month'), func=total_document_version_per_month, - minute='0' + klass=StatisticLineChart, + label=_('Total document versions at each month'), + minute='0', + slug='total-document-versions-at-each-month' ) namespace.add_statistic( - klass=StatisticLineChart, - slug='total-document-pages-at-each-month', - label=_('Total document pages at each month'), func=total_document_page_per_month, - minute='0' + klass=StatisticLineChart, + label=_('Total document pages at each month'), + minute='0', + slug='total-document-pages-at-each-month' ) diff --git a/mayan/apps/documents/tasks.py b/mayan/apps/documents/tasks.py index 3ebebb63b6..ec5cc43120 100644 --- a/mayan/apps/documents/tasks.py +++ b/mayan/apps/documents/tasks.py @@ -58,14 +58,14 @@ def task_clear_image_cache(): @app.task(ignore_result=True) -def task_delete_document(deleted_document_id): - DeletedDocument = apps.get_model( - app_label='documents', model_name='DeletedDocument' +def task_delete_document(trashed_document_id): + TrashedDocument = apps.get_model( + app_label='documents', model_name='TrashedDocument' ) logger.debug('Executing') - deleted_document = DeletedDocument.objects.get(pk=deleted_document_id) - deleted_document.delete() + trashed_document = TrashedDocument.objects.get(pk=trashed_document_id) + trashed_document.delete() logger.debug('Finshed') diff --git a/mayan/apps/documents/tests/mixins.py b/mayan/apps/documents/tests/mixins.py index d43d1d4141..8a61720131 100644 --- a/mayan/apps/documents/tests/mixins.py +++ b/mayan/apps/documents/tests/mixins.py @@ -18,27 +18,25 @@ class DocumentTestMixin(object): auto_create_document_type = True auto_upload_document = True test_document_filename = TEST_SMALL_DOCUMENT_FILENAME + test_document_path = None def _create_document_type(self): self.document_type = DocumentType.objects.create( label=TEST_DOCUMENT_TYPE_LABEL ) - def upload_document(self, filename=None): - self._calculate_test_document_path() - - with open(self.test_document_path, mode='rb') as file_object: - document = self.document_type.new_document( - file_object=file_object, - label=filename or self.test_document_filename - ) - return document + def _create_document(self, *args, **kwargs): + """ + Alias for upload_document() + """ + self.test_document = self.upload_document(*args, **kwargs) def _calculate_test_document_path(self): - self.test_document_path = os.path.join( - settings.BASE_DIR, 'apps', 'documents', 'tests', 'contrib', - 'sample_documents', self.test_document_filename - ) + if not self.test_document_path: + self.test_document_path = os.path.join( + settings.BASE_DIR, 'apps', 'documents', 'tests', 'contrib', + 'sample_documents', self.test_document_filename + ) def setUp(self): super(DocumentTestMixin, self).setUp() @@ -54,6 +52,18 @@ class DocumentTestMixin(object): document_type.delete() super(DocumentTestMixin, self).tearDown() + def upload_document(self, document_type=None, filename=None): + self._calculate_test_document_path() + + document_type = document_type or self.document_type + + with open(self.test_document_path, mode='rb') as file_object: + document = document_type.new_document( + file_object=file_object, + label=filename or self.test_document_filename + ) + return document + class DocumentTypeQuickLabelTestMixin(object): def _create_quick_label(self): diff --git a/mayan/apps/documents/tests/test_api.py b/mayan/apps/documents/tests/test_api.py index 6fe4fa78e0..2299a4f03b 100644 --- a/mayan/apps/documents/tests/test_api.py +++ b/mayan/apps/documents/tests/test_api.py @@ -11,10 +11,10 @@ from mayan.apps.rest_api.tests import BaseAPITestCase from ..models import Document, DocumentType from ..permissions import ( - permission_document_create, permission_document_delete, - permission_document_download, permission_document_edit, - permission_document_new_version, permission_document_properties_edit, - permission_document_restore, permission_document_trash, + permission_document_create, permission_document_download, + permission_document_edit, permission_document_new_version, + permission_document_properties_edit, permission_document_trash, + permission_trashed_document_delete, permission_trashed_document_restore, permission_document_type_create, permission_document_type_delete, permission_document_type_edit, permission_document_version_revert, permission_document_version_view, permission_document_view @@ -61,7 +61,7 @@ class DocumentTypeAPITestCase(BaseAPITestCase): def _request_document_type_patch(self): return self.patch( viewname='rest_api:documenttype-detail', kwargs={ - 'document_type_pk': self.document_type.pk + 'document_type_id': self.document_type.pk }, data={'label': TEST_DOCUMENT_TYPE_LABEL_EDITED} ) @@ -90,7 +90,7 @@ class DocumentTypeAPITestCase(BaseAPITestCase): def _request_document_type_put(self): return self.put( viewname='rest_api:documenttype-detail', kwargs={ - 'document_type_pk': self.document_type.pk + 'document_type_id': self.document_type.pk }, data={'label': TEST_DOCUMENT_TYPE_LABEL_EDITED} ) @@ -119,7 +119,7 @@ class DocumentTypeAPITestCase(BaseAPITestCase): def _request_document_type_delete(self): return self.delete( viewname='rest_api:documenttype-detail', kwargs={ - 'document_type_pk': self.document_type.pk + 'document_type_id': self.document_type.pk } ) @@ -207,7 +207,7 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): with open(TEST_DOCUMENT_PATH, mode='rb') as file_descriptor: return self.post( viewname='rest_api:document-version-list', kwargs={ - 'document_pk': self.document.pk + 'document_id': self.document.pk }, data={ 'comment': '', 'file': file_descriptor, } @@ -247,8 +247,8 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_version_revert(self): return self.delete( viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document.latest_version.pk + 'document_id': self.document.pk, + 'document_version_id': self.document.latest_version.pk } ) @@ -275,7 +275,7 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_version_list(self): return self.get( viewname='rest_api:document-version-list', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_version_list_no_permission(self): @@ -301,7 +301,7 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_download(self): return self.get( viewname='rest_api:document-download', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_download_no_permission(self): @@ -329,8 +329,8 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_version_download(self): return self.get( viewname='rest_api:documentversion-download', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document.latest_version.pk, + 'document_id': self.document.pk, + 'document_version_id': self.document.latest_version.pk, } ) @@ -362,8 +362,8 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): response = self.get( viewname='rest_api:documentversion-download', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document.latest_version.pk, + 'document_id': self.document.pk, + 'document_version_id': self.document.latest_version.pk, }, data={'preserve_extension': True} ) @@ -380,8 +380,8 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_version_edit_via_patch(self): return self.patch( viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document.latest_version.pk, + 'document_id': self.document.pk, + 'document_version_id': self.document.latest_version.pk, }, data={'comment': TEST_DOCUMENT_VERSION_COMMENT_EDITED} ) @@ -407,8 +407,8 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_version_edit_via_put(self): return self.put( viewname='rest_api:documentversion-detail', kwargs={ - 'document_pk': self.document.pk, - 'document_version_pk': self.document.latest_version.pk, + 'document_id': self.document.pk, + 'document_version_id': self.document.latest_version.pk, }, data={'comment': TEST_DOCUMENT_VERSION_COMMENT_EDITED} ) @@ -434,7 +434,7 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_description_edit_via_patch(self): return self.patch( viewname='rest_api:document-detail', - kwargs={'document_pk': self.document.pk}, + kwargs={'document_id': self.document.pk}, data={'description': TEST_DOCUMENT_DESCRIPTION_EDITED} ) @@ -459,7 +459,7 @@ class DocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_description_edit_via_put(self): return self.put( viewname='rest_api:document-detail', - kwargs={'document_pk': self.document.pk}, + kwargs={'document_id': self.document.pk}, data={'description': TEST_DOCUMENT_DESCRIPTION_EDITED} ) @@ -491,8 +491,8 @@ class DocumentPageAPITestCase(DocumentTestMixin, BaseAPITestCase): page = self.document.pages.first() return self.get( viewname='rest_api:documentpage-image', kwargs={ - 'document_pk': page.document.pk, - 'document_version_pk': page.document_version.pk, 'document_page_pk': page.pk + 'document_id': page.document.pk, + 'document_version_id': page.document_version.pk, 'document_page_id': page.pk } ) @@ -518,7 +518,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_document_move_to_trash(self): return self.delete( viewname='rest_api:document-detail', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_move_to_trash_no_permission(self): @@ -540,7 +540,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_trashed_document_delete_view(self): return self.delete( viewname='rest_api:trasheddocument-detail', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_trashed_document_delete_from_trash_no_access(self): @@ -554,7 +554,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def test_trashed_document_delete_from_trash_with_access(self): self.document = self.upload_document() self.document.delete() - self.grant_access(permission=permission_document_delete, obj=self.document) + self.grant_access(permission=permission_trashed_document_delete, obj=self.document) response = self._request_trashed_document_delete_view() self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(Document.objects.count(), 0) @@ -563,7 +563,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_trashed_document_detail_view(self): return self.get( viewname='rest_api:trasheddocument-detail', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_trashed_document_detail_view_no_access(self): @@ -608,7 +608,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def _request_trashed_document_restore_view(self): return self.post( viewname='rest_api:trasheddocument-restore', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_trashed_document_restore_no_access(self): @@ -622,7 +622,7 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase): def test_trashed_document_restore_with_access(self): self.document = self.upload_document() self.document.delete() - self.grant_access(permission=permission_document_restore, obj=self.document) + self.grant_access(permission=permission_trashed_document_restore, obj=self.document) response = self._request_trashed_document_restore_view() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(Document.trash.count(), 0) diff --git a/mayan/apps/documents/tests/test_document_page_views.py b/mayan/apps/documents/tests/test_document_page_views.py index 7b85e54dec..d276c1e513 100644 --- a/mayan/apps/documents/tests/test_document_page_views.py +++ b/mayan/apps/documents/tests/test_document_page_views.py @@ -9,19 +9,15 @@ from .literals import TEST_MULTI_PAGE_TIFF class DocumentPageViewTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DocumentPageViewTestCase, self).setUp() - self.login_user() - def _document_page_list_view(self): return self.get( viewname='documents:document_pages', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_page_list_view_no_permission(self): response = self._document_page_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_page_list_view_with_access(self): self.grant_access( @@ -32,18 +28,39 @@ class DocumentPageViewTestCase(GenericDocumentViewTestCase): response=response, text=self.document.label, status_code=200 ) + def _request_document_page_view(self, document_page): + return self.get( + viewname='documents:document_page_view', kwargs={ + 'document_page_id': document_page.pk + } + ) + + def test_document_page_view_no_permissions(self): + response = self._request_document_page_view( + document_page=self.document.pages.first() + ) + self.assertEqual(response.status_code, 404) + + def test_document_page_view_with_access(self): + self.grant_access( + obj=self.document, permission=permission_document_view + ) + response = self._request_document_page_view( + document_page=self.document.pages.first() + ) + self.assertContains( + response=response, text=force_text(self.document.pages.first()), + status_code=200 + ) + class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): test_document_filename = TEST_MULTI_PAGE_TIFF - def setUp(self): - super(DocumentPageNavigationViewTestCase, self).setUp() - self.login_user() - def _request_document_page_navigation_next_view(self): return self.get( viewname='documents:document_page_navigation_next', - kwargs={'document_page_pk': self.document.pages.first().pk}, + kwargs={'document_page_id': self.document.pages.first().pk}, follow=True ) @@ -51,7 +68,6 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): self.grant_access( obj=self.document, permission=permission_document_view ) - response = self._request_document_page_navigation_next_view() self.assertContains( @@ -62,7 +78,7 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): def _request_document_page_navigation_last_view(self): return self.get( viewname='documents:document_page_navigation_last', - kwargs={'document_page_pk': self.document.pages.first().pk}, + kwargs={'document_page_id': self.document.pages.first().pk}, follow=True ) @@ -70,7 +86,6 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): self.grant_access( obj=self.document, permission=permission_document_view ) - response = self._request_document_page_navigation_last_view() self.assertContains( @@ -81,7 +96,7 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): def _request_document_page_navigation_previous_view(self): return self.get( viewname='documents:document_page_navigation_previous', - kwargs={'document_page_pk': self.document.pages.last().pk}, + kwargs={'document_page_id': self.document.pages.last().pk}, follow=True ) @@ -89,7 +104,6 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): self.grant_access( obj=self.document, permission=permission_document_view ) - response = self._request_document_page_navigation_previous_view() self.assertContains( @@ -100,7 +114,7 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): def _request_document_page_navigation_first_view(self): return self.get( viewname='documents:document_page_navigation_first', - kwargs={'document_page_pk': self.document.pages.last().pk}, + kwargs={'document_page_id': self.document.pages.last().pk}, follow=True ) @@ -108,7 +122,6 @@ class DocumentPageNavigationViewTestCase(GenericDocumentViewTestCase): self.grant_access( obj=self.document, permission=permission_document_view ) - response = self._request_document_page_navigation_first_view() self.assertContains( diff --git a/mayan/apps/documents/tests/test_document_type_views.py b/mayan/apps/documents/tests/test_document_type_views.py index c2b891975b..2bb6ab0f8c 100644 --- a/mayan/apps/documents/tests/test_document_type_views.py +++ b/mayan/apps/documents/tests/test_document_type_views.py @@ -16,10 +16,6 @@ from .mixins import DocumentTypeQuickLabelTestMixin class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DocumentTypeViewsTestCase, self).setUp() - self.login_user() - def _request_document_type_create(self): return self.post( viewname='documents:document_type_create', @@ -50,12 +46,12 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): def _request_document_type_delete(self): return self.post( viewname='documents:document_type_delete', - kwargs={'document_type_pk': self.document_type.pk} + kwargs={'document_type_id': self.document_type.pk} ) def test_document_type_delete_view_no_permission(self): response = self._request_document_type_delete() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(DocumentType.objects.count(), 1) def test_document_type_delete_view_with_access(self): @@ -69,7 +65,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): def _request_document_type_edit(self): return self.post( viewname='documents:document_type_edit', - kwargs={'document_type_pk': self.document_type.pk}, + kwargs={'document_type_id': self.document_type.pk}, data={ 'label': TEST_DOCUMENT_TYPE_LABEL_EDITED, 'delete_time_period': DEFAULT_DELETE_PERIOD, @@ -79,7 +75,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): def test_document_type_edit_view_no_permission(self): response = self._request_document_type_edit() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.document_type.refresh_from_db() self.assertEqual( self.document_type.label, TEST_DOCUMENT_TYPE_LABEL @@ -98,14 +94,10 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, GenericDocumentViewTestCase): - def setUp(self): - super(DocumentTypeQuickLabelViewsTestCase, self).setUp() - self.login_user() - def _request_quick_label_create(self): return self.post( viewname='documents:document_type_filename_create', - kwargs={'document_type_pk': self.document_type.pk}, + kwargs={'document_type_id': self.document_type.pk}, data={ 'filename': TEST_DOCUMENT_TYPE_QUICK_LABEL, } @@ -117,7 +109,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener ) response = self._request_quick_label_create() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(self.document_type.filenames.count(), 0) def test_document_type_quick_label_create_with_access(self): @@ -132,7 +124,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener def _request_quick_label_delete(self): return self.post( viewname='documents:document_type_filename_delete', - kwargs={'filename_pk': self.document_type_filename.pk}, + kwargs={'filename_id': self.document_type_filename.pk}, ) def test_document_type_quick_label_delete_no_access(self): @@ -158,7 +150,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener def _request_quick_label_edit(self): return self.post( viewname='documents:document_type_filename_edit', - kwargs={'filename_pk': self.document_type_filename.pk}, + kwargs={'filename_id': self.document_type_filename.pk}, data={ 'filename': TEST_DOCUMENT_TYPE_QUICK_LABEL_EDITED, } @@ -168,7 +160,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener self._create_quick_label() response = self._request_quick_label_edit() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.document_type_filename.refresh_from_db() self.assertEqual( self.document_type_filename.filename, @@ -193,13 +185,13 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener def _request_quick_label_list_view(self): return self.get( viewname='documents:document_type_filename_list', - kwargs={'document_type_pk': self.document_type.pk}, + kwargs={'document_type_id': self.document_type.pk}, ) def test_document_type_quick_label_list_no_access(self): self._create_quick_label() response = self._request_quick_label_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_type_quick_label_list_with_access(self): self._create_quick_label() diff --git a/mayan/apps/documents/tests/test_document_version_views.py b/mayan/apps/documents/tests/test_document_version_views.py index ae65ec32ec..d288d75ecd 100644 --- a/mayan/apps/documents/tests/test_document_version_views.py +++ b/mayan/apps/documents/tests/test_document_version_views.py @@ -1,7 +1,10 @@ from __future__ import unicode_literals +from django.utils.encoding import force_text + from ..permissions import ( - permission_document_version_revert, permission_document_version_view + permission_document_download, permission_document_version_revert, + permission_document_version_view ) from .base import GenericDocumentViewTestCase @@ -9,26 +12,101 @@ from .literals import TEST_SMALL_DOCUMENT_PATH, TEST_VERSION_COMMENT class DocumentVersionTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DocumentVersionTestCase, self).setUp() - self.login_user() - def _upload_new_version(self): with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: - self.document.new_version( + self.test_document_version = self.document.new_version( comment=TEST_VERSION_COMMENT, file_object=file_object ) + def _request_document_version_detail_view(self): + return self.get( + viewname='documents:document_version_view', + kwargs={'document_version_id': self.test_document_version.pk} + ) + + def test_document_version_detail_no_permission(self): + self._upload_new_version() + response = self._request_document_version_detail_view() + self.assertEqual(response.status_code, 404) + + def test_document_version_detail_with_access(self): + self._upload_new_version() + self.grant_access( + obj=self.document, permission=permission_document_version_view + ) + response = self._request_document_version_detail_view() + self.assertEqual(response.status_code, 200) + + def _request_document_version_download(self, data=None): + data = data or {} + return self.get( + viewname='documents:document_version_download', kwargs={ + 'document_version_id': self.document.latest_version.pk, + }, data=data + ) + + def test_document_version_download_view_no_permission(self): + response = self._request_document_version_download() + self.assertEqual(response.status_code, 404) + + def test_document_version_download_view_with_permission(self): + # Set the expected_content_type for + # common.tests.mixins.ContentTypeCheckMixin + self.expected_content_type = '{}; charset=utf-8'.format( + self.document.latest_version.mimetype + ) + + self.grant_access( + obj=self.document, permission=permission_document_download + ) + response = self._request_document_version_download() + self.assertEqual(response.status_code, 200) + + with self.document.open() as file_object: + self.assert_download_response( + response=response, content=file_object.read(), + basename=force_text(self.document.latest_version), + mime_type='{}; charset=utf-8'.format( + self.document.latest_version.mimetype + ) + ) + + def test_document_version_download_preserve_extension_view_with_permission(self): + # Set the expected_content_type for + # common.tests.mixins.ContentTypeCheckMixin + self.expected_content_type = '{}; charset=utf-8'.format( + self.document.latest_version.mimetype + ) + + self.grant_access( + obj=self.document, permission=permission_document_download + ) + response = self._request_document_version_download( + data={'preserve_extension': True} + ) + + self.assertEqual(response.status_code, 200) + + with self.document.open() as file_object: + self.assert_download_response( + response=response, content=file_object.read(), + basename=self.document.latest_version.get_rendered_string( + preserve_extension=True + ), mime_type='{}; charset=utf-8'.format( + self.document.latest_version.mimetype + ) + ) + def _request_document_version_list_view(self): return self.get( viewname='documents:document_version_list', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_version_list_no_permission(self): self._upload_new_version() response = self._request_document_version_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_version_list_with_access(self): self._upload_new_version() @@ -40,32 +118,28 @@ class DocumentVersionTestCase(GenericDocumentViewTestCase): response=response, text=TEST_VERSION_COMMENT, status_code=200 ) - def _request_document_version_revert_view(self, document_version): + def _request_document_version_revert_view(self): return self.post( viewname='documents:document_version_revert', - kwargs={'document_version_pk': document_version.pk} + kwargs={'document_version_id': self.test_document_version_first.pk} ) def test_document_version_revert_no_permission(self): - first_version = self.document.latest_version + self.test_document_version_first = self.document.latest_version self._upload_new_version() - response = self._request_document_version_revert_view( - document_version=first_version - ) - self.assertEqual(response.status_code, 403) + response = self._request_document_version_revert_view() + self.assertEqual(response.status_code, 302) self.assertEqual(self.document.versions.count(), 2) def test_document_version_revert_with_access(self): - first_version = self.document.latest_version + self.test_document_version_first = self.document.latest_version self._upload_new_version() self.grant_access( obj=self.document, permission=permission_document_version_revert ) - response = self._request_document_version_revert_view( - document_version=first_version - ) + response = self._request_document_version_revert_view() self.assertEqual(response.status_code, 302) self.assertEqual(self.document.versions.count(), 1) diff --git a/mayan/apps/documents/tests/test_document_views.py b/mayan/apps/documents/tests/test_document_views.py index c5e80a178c..0d3420558e 100644 --- a/mayan/apps/documents/tests/test_document_views.py +++ b/mayan/apps/documents/tests/test_document_views.py @@ -3,18 +3,16 @@ from __future__ import unicode_literals import os from django.contrib.contenttypes.models import ContentType -from django.utils.encoding import force_text from mayan.apps.converter.models import Transformation from mayan.apps.converter.permissions import permission_transformation_delete from ..literals import PAGE_RANGE_ALL -from ..models import DeletedDocument, Document, DocumentType +from ..models import Document, DocumentType, FavoriteDocument from ..permissions import ( permission_document_create, permission_document_download, permission_document_print, permission_document_properties_edit, - permission_document_tools, permission_document_view, - permission_empty_trash + permission_document_tools, permission_document_view ) from .base import GenericDocumentViewTestCase @@ -26,19 +24,15 @@ from .mixins import DocumentTypeQuickLabelTestMixin class DocumentsViewsTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DocumentsViewsTestCase, self).setUp() - self.login_user() - def _request_document_properties_view(self): return self.get( viewname='documents:document_properties', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_view_no_permissions(self): response = self._request_document_properties_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_view_with_permissions(self): self.grant_access( @@ -68,8 +62,8 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def _request_document_type_edit(self, document_type): return self.post( - viewname='documents:document_document_type_edit', - kwargs={'document_pk': self.document.pk}, + viewname='documents:document_change_type', + kwargs={'document_id': self.document.pk}, data={'document_type': document_type.pk} ) @@ -122,7 +116,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def _request_multiple_document_type_edit(self, document_type): return self.post( - viewname='documents:document_multiple_document_type_edit', + viewname='documents:document_multiple_change_type', data={ 'id_list': self.document.pk, 'document_type': document_type.pk @@ -174,40 +168,51 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): Document.objects.first().document_type, document_type_2 ) - def _request_document_download_form_view(self): + def _request_document_download_form_get_view(self): return self.get( viewname='documents:document_download_form', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) - def test_document_download_form_view_no_permission(self): - response = self._request_document_download_form_view() + def test_document_download_form_view_get_no_permission(self): + response = self._request_document_download_form_get_view() + self.assertEqual(response.status_code, 404) - self.assertNotContains( - response=response, text=self.document.label, status_code=200 - ) - - def test_document_download_form_view_with_access(self): + def test_document_download_form_get_view_with_access(self): self.grant_access( obj=self.document, permission=permission_document_download ) - response = self._request_document_download_form_view() + response = self._request_document_download_form_get_view() + self.assertEqual(response.status_code, 200) - self.assertContains( - response=response, text=self.document.label, status_code=200 + def _request_document_download_form_post_view(self): + return self.post( + viewname='documents:document_download_form', + kwargs={'document_id': self.document.pk} ) + def test_document_download_form_post_view_no_permission(self): + response = self._request_document_download_form_post_view() + self.assertEqual(response.status_code, 404) + + def test_document_download_form_post_view_with_access(self): + self.grant_access( + obj=self.document, permission=permission_document_download + ) + response = self._request_document_download_form_post_view() + self.assertEqual(response.status_code, 302) + def _request_document_download_view(self): return self.get( viewname='documents:document_download', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_download_view_no_permission(self): response = self._request_document_download_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) - def test_document_download_view_with_permission(self): + def test_document_download_view_with_access(self): # Set the expected_content_type for # common.tests.mixins.ContentTypeCheckMixin self.expected_content_type = '{}; charset=utf-8'.format( @@ -235,7 +240,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def test_document_multiple_download_view_no_permission(self): response = self._request_document_multiple_download_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_multiple_download_view_with_permission(self): # Set the expected_content_type for @@ -257,70 +262,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): mime_type=self.document.file_mimetype ) - def _request_document_version_download(self, data=None): - data = data or {} - return self.get( - viewname='documents:document_version_download', kwargs={ - 'document_version_pk': self.document.latest_version.pk, - }, data=data - ) - - def test_document_version_download_view_no_permission(self): - response = self._request_document_version_download() - self.assertEqual(response.status_code, 403) - - def test_document_version_download_view_with_permission(self): - # Set the expected_content_type for - # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.document.latest_version.mimetype - ) - - self.grant_access( - obj=self.document, permission=permission_document_download - ) - response = self._request_document_version_download() - self.assertEqual(response.status_code, 200) - - with self.document.open() as file_object: - self.assert_download_response( - response=response, content=file_object.read(), - basename=force_text(self.document.latest_version), - mime_type='{}; charset=utf-8'.format( - self.document.latest_version.mimetype - ) - ) - - def test_document_version_download_preserve_extension_view_with_permission(self): - # Set the expected_content_type for - # common.tests.mixins.ContentTypeCheckMixin - self.expected_content_type = '{}; charset=utf-8'.format( - self.document.latest_version.mimetype - ) - - self.grant_access( - obj=self.document, permission=permission_document_download - ) - response = self._request_document_version_download( - data={'preserve_extension': True} - ) - - self.assertEqual(response.status_code, 200) - - with self.document.open() as file_object: - self.assert_download_response( - response=response, content=file_object.read(), - basename=self.document.latest_version.get_rendered_string( - preserve_extension=True - ), mime_type='{}; charset=utf-8'.format( - self.document.latest_version.mimetype - ) - ) - def _request_document_update_page_count_view(self): return self.post( viewname='documents:document_update_page_count', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_update_page_count_view_no_permission(self): @@ -372,7 +317,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def _request_document_clear_transformations_view(self): return self.post( viewname='documents:document_clear_transformations', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_clear_transformations_view_no_permission(self): @@ -482,57 +427,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): Transformation.objects.get_for_model(document_page).count(), 0 ) - def _request_empty_trash_view(self): - return self.post(viewname='documents:trash_can_empty') - - def test_trash_can_empty_view_no_permission(self): - self.document.delete() - self.assertEqual(DeletedDocument.objects.count(), 1) - - response = self._request_empty_trash_view() - self.assertEqual(response.status_code, 403) - self.assertEqual(DeletedDocument.objects.count(), 1) - - def test_trash_can_empty_view_with_permission(self): - self.document.delete() - self.assertEqual(DeletedDocument.objects.count(), 1) - - self.grant_permission(permission=permission_empty_trash) - - response = self._request_empty_trash_view() - self.assertEqual(response.status_code, 302) - self.assertEqual(DeletedDocument.objects.count(), 0) - self.assertEqual(Document.objects.count(), 0) - - def _request_document_page_view(self, document_page): - return self.get( - viewname='documents:document_page_view', kwargs={ - 'document_page_pk': document_page.pk - } - ) - - def test_document_page_view_no_permissions(self): - response = self._request_document_page_view( - document_page=self.document.pages.first() - ) - self.assertEqual(response.status_code, 403) - - def test_document_page_view_with_access(self): - self.grant_access( - obj=self.document, permission=permission_document_view - ) - response = self._request_document_page_view( - document_page=self.document.pages.first() - ) - self.assertContains( - response=response, text=force_text(self.document.pages.first()), - status_code=200 - ) - def _request_document_print_view(self): return self.get( viewname='documents:document_print', kwargs={ - 'document_pk': self.document.pk + 'document_id': self.document.pk }, data={ 'page_group': PAGE_RANGE_ALL } @@ -540,7 +438,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def test_document_print_view_no_access(self): response = self._request_document_print_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_print_view_with_access(self): self.grant_access( @@ -551,10 +449,6 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): class DocumentsQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, GenericDocumentViewTestCase): - def setUp(self): - super(DocumentsQuickLabelViewsTestCase, self).setUp() - self.login_user() - def _request_document_quick_label_edit_view(self, extra_data=None): data = { 'document_type_available_filenames': self.document_type_filename.pk, @@ -566,14 +460,14 @@ class DocumentsQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, GenericD return self.post( viewname='documents:document_edit', - kwargs={'document_pk': self.document.pk}, + kwargs={'document_id': self.document.pk}, data=data ) def test_document_quick_label_no_permission(self): self._create_quick_label() response = self._request_document_quick_label_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.document.refresh_from_db() def test_document_quick_label_with_access(self): @@ -621,3 +515,72 @@ class DocumentsQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, GenericD self.assertEqual( self.document.label, self.document_type_filename.filename ) + + +class FavoriteDocumentsTestCase(GenericDocumentViewTestCase): + def _request_document_add_to_favorites_view(self): + return self.post( + viewname='documents:document_add_to_favorites', + kwargs={'document_id': self.document.pk} + ) + + def test_document_add_to_favorites_view_no_permission(self): + response = self._request_document_add_to_favorites_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(FavoriteDocument.objects.count(), 0) + + def test_document_add_to_favorites_view_with_access(self): + self.grant_access( + obj=self.document, permission=permission_document_view + ) + response = self._request_document_add_to_favorites_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(FavoriteDocument.objects.count(), 1) + + def _document_add_to_favorites(self): + FavoriteDocument.objects.add_for_user( + document=self.document, user=self._test_case_user + ) + + def _request_document_list_favorites(self): + return self.get( + viewname='documents:document_list_favorites', + ) + + def test_document_list_favorites_view_no_permission(self): + self._document_add_to_favorites() + response = self._request_document_list_favorites() + self.assertNotContains( + response=response, text=self.document.label, status_code=200 + ) + + def test_document_list_favorites_view_with_access(self): + self._document_add_to_favorites() + self.grant_access( + obj=self.document, permission=permission_document_view + ) + response = self._request_document_list_favorites() + self.assertContains( + response=response, text=self.document.label, status_code=200 + ) + + def _request_document_remove_from_favorites(self): + return self.post( + viewname='documents:document_remove_from_favorites', + kwargs={'document_id': self.document.pk} + ) + + def test_document_remove_from_favorites_view_no_permission(self): + self._document_add_to_favorites() + response = self._request_document_remove_from_favorites() + self.assertEqual(response.status_code, 302) + self.assertEqual(FavoriteDocument.objects.count(), 1) + + def test_document_remove_from_favorites_view_with_access(self): + self._document_add_to_favorites() + self.grant_access( + obj=self.document, permission=permission_document_view + ) + response = self._request_document_remove_from_favorites() + self.assertEqual(response.status_code, 302) + self.assertEqual(FavoriteDocument.objects.count(), 0) diff --git a/mayan/apps/documents/tests/test_duplicated_document_views.py b/mayan/apps/documents/tests/test_duplicated_document_views.py index 7dea4e1123..780b04f4bb 100644 --- a/mayan/apps/documents/tests/test_duplicated_document_views.py +++ b/mayan/apps/documents/tests/test_duplicated_document_views.py @@ -7,10 +7,6 @@ from .literals import TEST_SMALL_DOCUMENT_FILENAME, TEST_SMALL_DOCUMENT_PATH class DuplicatedDocumentsViewsTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DuplicatedDocumentsViewsTestCase, self).setUp() - self.login_user() - def _upload_duplicate_document(self): with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: self.document_duplicate = self.document_type.new_document( @@ -23,7 +19,7 @@ class DuplicatedDocumentsViewsTestCase(GenericDocumentViewTestCase): def _request_document_duplicates_list_view(self): return self.get( viewname='documents:document_duplicates_list', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_duplicated_document_list_no_permissions(self): @@ -53,7 +49,7 @@ class DuplicatedDocumentsViewsTestCase(GenericDocumentViewTestCase): self._upload_duplicate_document() response = self._request_document_duplicates_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_duplicates_list_with_access(self): self._upload_duplicate_document() diff --git a/mayan/apps/documents/tests/test_events.py b/mayan/apps/documents/tests/test_events.py index bd3b8859b4..c703a58bf8 100644 --- a/mayan/apps/documents/tests/test_events.py +++ b/mayan/apps/documents/tests/test_events.py @@ -3,10 +3,6 @@ from __future__ import unicode_literals from actstream.models import Action from django_downloadview import assert_download_response -from mayan.apps.user_management.tests.literals import ( - TEST_USER_PASSWORD, TEST_USER_USERNAME -) - from ..events import event_document_download, event_document_view from ..permissions import ( permission_document_download, permission_document_view @@ -21,38 +17,30 @@ TEST_TRANSFORMATION_ARGUMENT = 'degrees: 180' class DocumentEventsTestCase(GenericDocumentViewTestCase): - def test_document_download_event_no_permissions(self): - self.login( - username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD + def _request_document_download_view(self): + return self.get( + viewname='documents:document_download', + kwargs={'document_id': self.document.pk} ) + def test_document_download_event_no_permissions(self): Action.objects.all().delete() - response = self.get( - viewname='documents:document_download', - kwargs={'document_pk': self.document.pk} - ) + response = self._request_document_download_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(list(Action.objects.any(obj=self.document)), []) def test_document_download_event_with_permissions(self): - self.login( - username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD - ) - Action.objects.all().delete() - self.role.permissions.add( - permission_document_download.stored_permission + self.grant_access( + obj=self.document, permission=permission_document_download ) self.expected_content_type = 'image/png; charset=utf-8' - response = self.get( - viewname='documents:document_download', - kwargs={'document_pk': self.document.pk} - ) + response = self._request_document_download_view() # Download the file to close the file descriptor with self.document.open() as file_object: @@ -65,40 +53,31 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase): self.assertEqual(event.verb, event_document_download.id) self.assertEqual(event.target, self.document) - self.assertEqual(event.actor, self.user) + self.assertEqual(event.actor, self._test_case_user) + + def _request_document_preview_view(self): + return self.get( + viewname='documents:document_preview', + kwargs={'document_id': self.document.pk} + ) def test_document_view_event_no_permissions(self): - self.login( - username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD - ) - Action.objects.all().delete() - response = self.get( - viewname='documents:document_preview', - kwargs={'document_pk': self.document.pk} - ) + response = self._request_document_preview_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(list(Action.objects.any(obj=self.document)), []) - def test_document_view_event_with_permissions(self): - self.login( - username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD - ) - + def test_document_view_event_with_access(self): Action.objects.all().delete() - self.role.permissions.add( - permission_document_view.stored_permission - ) - self.get( - viewname='documents:document_preview', - kwargs={'document_pk': self.document.pk} - ) + self.grant_access(obj=self.document, permission=permission_document_view) + response = self._request_document_preview_view() + self.assertEqual(response.status_code, 200) event = Action.objects.any(obj=self.document).first() self.assertEqual(event.verb, event_document_view.id) self.assertEqual(event.target, self.document) - self.assertEqual(event.actor, self.user) + self.assertEqual(event.actor, self._test_case_user) diff --git a/mayan/apps/documents/tests/test_links.py b/mayan/apps/documents/tests/test_links.py index b820099da3..c9d8080c8f 100644 --- a/mayan/apps/documents/tests/test_links.py +++ b/mayan/apps/documents/tests/test_links.py @@ -4,14 +4,13 @@ import time from django.urls import reverse -from mayan.apps.acls.models import AccessControlList - from ..links import ( - link_document_restore, link_document_version_download, + link_trashed_document_restore, link_document_version_download, link_document_version_revert ) +from ..models import TrashedDocument from ..permissions import ( - permission_document_download, permission_document_restore, + permission_document_download, permission_trashed_document_restore, permission_document_version_revert ) @@ -26,15 +25,13 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase): self.assertTrue(self.document.versions.count(), 2) - self.login_user() - self.add_test_view(test_object=self.document.versions.first()) context = self.get_test_view() resolved_link = link_document_version_revert.resolve(context=context) self.assertEqual(resolved_link, None) - def test_document_version_revert_link_with_permission(self): + def test_document_version_revert_link_with_access(self): # Needed by MySQL as milliseconds value is not store in timestamp # field time.sleep(1.01) @@ -44,13 +41,8 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase): self.assertTrue(self.document.versions.count(), 2) - self.login_user() - - acl = AccessControlList.objects.create( - content_object=self.document, role=self.role - ) - acl.permissions.add( - permission_document_version_revert.stored_permission + self.grant_access( + obj=self.document, permission=permission_document_version_revert ) self.add_test_view(test_object=self.document.versions.first()) @@ -61,27 +53,22 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase): self.assertEqual( resolved_link.url, reverse( - 'documents:document_version_revert', - args=(self.document.versions.first().pk,) + viewname='documents:document_version_revert', + kwargs={'document_version_id': self.document.versions.first().pk} ) ) def test_document_version_download_link_no_permission(self): - self.login_user() - self.add_test_view(test_object=self.document.latest_version) context = self.get_test_view() resolved_link = link_document_version_download.resolve(context=context) self.assertEqual(resolved_link, None) - def test_document_version_download_link_with_permission(self): - self.login_user() - - acl = AccessControlList.objects.create( - content_object=self.document, role=self.role + def test_document_version_download_link_with_access(self): + self.grant_access( + obj=self.document, permission=permission_document_download ) - acl.permissions.add(permission_document_download.stored_permission) self.add_test_view(test_object=self.document.latest_version) context = self.get_test_view() @@ -91,45 +78,41 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase): self.assertEqual( resolved_link.url, reverse( - 'documents:document_version_download_form', - args=(self.document.latest_version.pk,) + viewname='documents:document_version_download_form', + kwargs={'document_version_id': self.document.latest_version.pk} ) ) class DeletedDocumentsLinksTestCase(GenericDocumentViewTestCase): + def _request_trashed_document_restore_link(self): + self.add_test_view( + test_object=TrashedDocument.objects.get(pk=self.document.pk) + ) + context = self.get_test_view() + return link_trashed_document_restore.resolve(context=context) + def test_deleted_document_restore_link_no_permission(self): self.document.delete() - self.login_user() - - self.add_test_view(test_object=self.document) - context = self.get_test_view() - resolved_link = link_document_restore.resolve(context=context) + resolved_link = self._request_trashed_document_restore_link() self.assertEqual(resolved_link, None) - def test_deleted_document_restore_link_with_permission(self): + def test_deleted_document_restore_link_with_access(self): self.document.delete() - self.login_user() - - acl = AccessControlList.objects.create( - content_object=self.document, role=self.role - ) - acl.permissions.add( - permission_document_restore.stored_permission + self.grant_access( + obj=self.document, permission=permission_trashed_document_restore ) - self.add_test_view(test_object=self.document) - context = self.get_test_view() - resolved_link = link_document_restore.resolve(context=context) + resolved_link = self._request_trashed_document_restore_link() self.assertNotEqual(resolved_link, None) self.assertEqual( resolved_link.url, reverse( - 'documents:document_restore', - args=(self.document.pk,) + viewname='documents:trashed_document_restore', + kwargs={'trashed_document_id': self.document.pk} ) ) diff --git a/mayan/apps/documents/tests/test_models.py b/mayan/apps/documents/tests/test_models.py index e3f6173eda..4b1839a5c2 100644 --- a/mayan/apps/documents/tests/test_models.py +++ b/mayan/apps/documents/tests/test_models.py @@ -7,7 +7,7 @@ from mayan.apps.common.tests import BaseTestCase from ..literals import STUB_EXPIRATION_INTERVAL from ..models import ( - DeletedDocument, Document, DocumentType, DuplicatedDocument + Document, DocumentType, DuplicatedDocument, TrashedDocument ) from .base import GenericDocumentTestCase @@ -58,12 +58,12 @@ class DocumentTestCase(DocumentTestMixin, BaseTestCase): # Trash the document self.document.delete() - self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(TrashedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) # Restore the document self.document.restore() - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 1) def test_trashing_documents(self): @@ -71,12 +71,12 @@ class DocumentTestCase(DocumentTestMixin, BaseTestCase): # Trash the document self.document.delete() - self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(TrashedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) # Delete the document self.document.delete() - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 0) def test_auto_trashing(self): @@ -94,12 +94,12 @@ class DocumentTestCase(DocumentTestMixin, BaseTestCase): time.sleep(1.01) self.assertEqual(Document.objects.count(), 1) - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 0) DocumentType.objects.check_trash_periods() self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(TrashedDocument.objects.count(), 1) def test_auto_delete(self): """ @@ -112,12 +112,12 @@ class DocumentTestCase(DocumentTestMixin, BaseTestCase): self.document_type.save() self.assertEqual(Document.objects.count(), 1) - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 0) self.document.delete() self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(TrashedDocument.objects.count(), 1) # Needed by MySQL as milliseconds value is not stored in timestamp # field @@ -126,7 +126,7 @@ class DocumentTestCase(DocumentTestMixin, BaseTestCase): DocumentType.objects.check_delete_periods() self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 0) class PDFCompatibilityTestCase(BaseTestCase): diff --git a/mayan/apps/documents/tests/test_search.py b/mayan/apps/documents/tests/test_search.py index b72b5eeed2..9f689e277c 100644 --- a/mayan/apps/documents/tests/test_search.py +++ b/mayan/apps/documents/tests/test_search.py @@ -9,12 +9,12 @@ from mayan.apps.documents.tests import DocumentTestMixin class DocumentSearchTestCase(DocumentTestMixin, BaseTestCase): def _perform_document_page_search(self): return document_page_search.search( - query_string={'q': self.document.label}, user=self.user + query_string={'q': self.document.label}, user=self._test_case_user ) def _perform_document_search(self): return document_search.search( - query_string={'q': self.document.label}, user=self.user + query_string={'q': self.document.label}, user=self._test_case_user ) def test_document_page_search_no_access(self): diff --git a/mayan/apps/documents/tests/test_trashed_document_views.py b/mayan/apps/documents/tests/test_trashed_document_views.py index f54ff227e2..543eb8c9df 100644 --- a/mayan/apps/documents/tests/test_trashed_document_views.py +++ b/mayan/apps/documents/tests/test_trashed_document_views.py @@ -1,56 +1,26 @@ from __future__ import unicode_literals -from ..models import DeletedDocument, Document +from ..models import Document, TrashedDocument from ..permissions import ( - permission_document_delete, permission_document_restore, - permission_document_trash, permission_document_view + permission_document_trash, permission_document_view, + permission_empty_trash, permission_trashed_document_delete, + permission_trashed_document_restore ) from .base import GenericDocumentViewTestCase -class DeletedDocumentTestCase(GenericDocumentViewTestCase): - def setUp(self): - super(DeletedDocumentTestCase, self).setUp() - self.login_user() - - def _request_document_restore_view(self): - return self.post( - viewname='documents:document_restore', - kwargs={'document_pk': self.document.pk} - ) - - def test_document_restore_view_no_permission(self): - self.document.delete() - self.assertEqual(Document.objects.count(), 0) - - response = self._request_document_restore_view() - self.assertEqual(response.status_code, 403) - self.assertEqual(DeletedDocument.objects.count(), 1) - self.assertEqual(Document.objects.count(), 0) - - def test_document_restore_view_with_access(self): - self.document.delete() - self.assertEqual(Document.objects.count(), 0) - - self.grant_access( - obj=self.document, permission=permission_document_restore - ) - response = self._request_document_restore_view() - self.assertEqual(response.status_code, 302) - self.assertEqual(DeletedDocument.objects.count(), 0) - self.assertEqual(Document.objects.count(), 1) - +class TrashedDocumentTestCase(GenericDocumentViewTestCase): def _request_document_trash_view(self): return self.post( viewname='documents:document_trash', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.document.pk} ) def test_document_trash_no_permissions(self): response = self._request_document_trash_view() - self.assertEqual(response.status_code, 403) - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(response.status_code, 302) + self.assertEqual(TrashedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 1) def test_document_trash_with_access(self): @@ -60,58 +30,107 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase): response = self._request_document_trash_view() self.assertEqual(response.status_code, 302) - self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(TrashedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) - def _request_document_delete_view(self): + def _request_trashed_document_restore_view(self): return self.post( - viewname='documents:document_delete', - kwargs={'document_pk': self.document.pk} + viewname='documents:trashed_document_restore', + kwargs={'trashed_document_id': self.document.pk} ) - def test_document_delete_no_permissions(self): + def test_trashed_document_restore_view_no_permission(self): self.document.delete() self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) - response = self._request_document_delete_view() - self.assertEqual(response.status_code, 403) + response = self._request_trashed_document_restore_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(TrashedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) - def test_document_delete_with_access(self): + def test_trashed_document_restore_view_with_access(self): self.document.delete() self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) self.grant_access( - obj=self.document, permission=permission_document_delete + obj=self.document, permission=permission_trashed_document_restore + ) + response = self._request_trashed_document_restore_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(TrashedDocument.objects.count(), 0) + self.assertEqual(Document.objects.count(), 1) + + def _request_trashed_document_delete_view(self): + return self.post( + viewname='documents:trashed_document_delete', + kwargs={'trashed_document_id': self.document.pk} ) - response = self._request_document_delete_view() + def test_trashed_document_delete_no_permissions(self): + self.document.delete() + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 1) + + response = self._request_trashed_document_delete_view() self.assertEqual(response.status_code, 302) - self.assertEqual(DeletedDocument.objects.count(), 0) + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 1) + + def test_trashed_document_delete_with_access(self): + self.document.delete() + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(TrashedDocument.objects.count(), 1) + + self.grant_access( + obj=self.document, permission=permission_trashed_document_delete + ) + + response = self._request_trashed_document_delete_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(TrashedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 0) - def _request_document_list_deleted_view(self): - return self.get(viewname='documents:document_list_deleted') + def _request_trashed_document_list_view(self): + return self.get(viewname='documents:trashed_document_list') - def test_deleted_document_list_view_no_permissions(self): + def test_trashed_document_list_view_no_permissions(self): self.document.delete() - response = self._request_document_list_deleted_view() + response = self._request_trashed_document_list_view() self.assertNotContains( response=response, text=self.document.label, status_code=200 ) - def test_deleted_document_list_view_with_access(self): + def test_trashed_document_list_view_with_access(self): self.document.delete() self.grant_access( obj=self.document, permission=permission_document_view ) - response = self._request_document_list_deleted_view() + response = self._request_trashed_document_list_view() self.assertContains( response=response, text=self.document.label, status_code=200 ) + + def _request_empty_trash_view(self): + return self.post(viewname='documents:trash_can_empty') + + def test_trash_can_empty_view_no_permission(self): + self.document.delete() + self.assertEqual(TrashedDocument.objects.count(), 1) + + response = self._request_empty_trash_view() + self.assertEqual(response.status_code, 403) + self.assertEqual(TrashedDocument.objects.count(), 1) + + def test_trash_can_empty_view_with_permission(self): + self.document.delete() + self.assertEqual(TrashedDocument.objects.count(), 1) + + self.grant_permission(permission=permission_empty_trash) + + response = self._request_empty_trash_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(TrashedDocument.objects.count(), 0) + self.assertEqual(Document.objects.count(), 0) diff --git a/mayan/apps/documents/urls.py b/mayan/apps/documents/urls.py index 92847752e4..bc1132fb23 100644 --- a/mayan/apps/documents/urls.py +++ b/mayan/apps/documents/urls.py @@ -2,29 +2,34 @@ from __future__ import unicode_literals from django.conf.urls import url +""" from .api_views import ( - APIDeletedDocumentListView, APIDeletedDocumentRestoreView, - APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentListView, + APITrashedDocumentListView, APITrashedDocumentRestoreView, + APITrashedDocumentView, APIDocumentDownloadView, APIDocumentListView, APIDocumentPageImageView, APIDocumentPageView, APIDocumentTypeDocumentListView, APIDocumentTypeListView, APIDocumentTypeView, APIDocumentVersionDownloadView, APIDocumentVersionPageListView, APIDocumentVersionsListView, APIDocumentVersionView, APIDocumentView, APIRecentDocumentListView ) +""" +from .api_views import ( + DocumentPageViewSet, DocumentTypeViewSet, DocumentVersionViewSet, + DocumentViewSet +) + from .views import ( - ClearImageCacheView, DeletedDocumentDeleteManyView, - DeletedDocumentDeleteView, DeletedDocumentListView, - DocumentDocumentTypeEditView, DocumentDownloadFormView, + ClearImageCacheView, DocumentChangeTypeView, DocumentDownloadFormView, DocumentDownloadView, DocumentDuplicatesListView, DocumentEditView, DocumentListView, DocumentPageListView, DocumentPageNavigationFirst, DocumentPageNavigationLast, DocumentPageNavigationNext, DocumentPageNavigationPrevious, DocumentPageRotateLeftView, DocumentPageRotateRightView, DocumentPageView, DocumentPageViewResetView, DocumentPageZoomInView, DocumentPageZoomOutView, DocumentPreviewView, - DocumentPrint, DocumentRestoreManyView, DocumentRestoreView, - DocumentTransformationsClearView, DocumentTransformationsCloneView, - DocumentTrashManyView, DocumentTrashView, DocumentTypeCreateView, - DocumentTypeDeleteView, DocumentTypeDocumentListView, DocumentTypeEditView, + DocumentPrintView, DocumentTransformationsClearView, + DocumentTransformationsCloneView, DocumentTrashView, + DocumentTypeCreateView, DocumentTypeDeleteView, + DocumentTypeDocumentListView, DocumentTypeEditView, DocumentTypeFilenameCreateView, DocumentTypeFilenameDeleteView, DocumentTypeFilenameEditView, DocumentTypeFilenameListView, DocumentTypeListView, DocumentUpdatePageCountView, @@ -33,12 +38,51 @@ from .views import ( DocumentView, DuplicatedDocumentListView, EmptyTrashCanView, FavoriteAddView, FavoriteDocumentListView, FavoriteRemoveView, RecentAccessDocumentListView, RecentAddedDocumentListView, - ScanDuplicatedDocuments + ScanDuplicatedDocuments, TrashedDocumentDeleteView, TrashedDocumentListView, + TrashedDocumentRestoreView +) + +urlpatterns_trashed_documents = ( + url( + regex=r'^documents/(?P\d+)/trash/$', + name='document_trash', view=DocumentTrashView.as_view() + ), + url( + regex=r'^documents/multiple/trash/$', name='document_multiple_trash', + view=DocumentTrashView.as_view() + ), + url( + regex=r'^trashed_documents/$', name='trashed_document_list', + view=TrashedDocumentListView.as_view() + ), + url( + regex=r'^trashed_documents/(?P\d+)/restore/$', + name='trashed_document_restore', + view=TrashedDocumentRestoreView.as_view() + ), + url( + regex=r'^trashed_documents/multiple/restore/$', + name='trashed_document_multiple_restore', + view=TrashedDocumentRestoreView.as_view() + ), + url( + regex=r'^trashed_documents/(?P\d+)/delete/$', + name='trashed_document_delete', view=TrashedDocumentDeleteView.as_view() + ), + url( + regex=r'^trashed_documents/multiple/delete/$', + name='trashed_document_multiple_delete', + view=TrashedDocumentDeleteView.as_view() + ), + url( + regex=r'^trash/empty/$', name='trash_can_empty', + view=EmptyTrashCanView.as_view() + ) ) urlpatterns = [ url( - regex=r'^documents/all/$', name='document_list', + regex=r'^documents/$', name='document_list', view=DocumentListView.as_view() ), url( @@ -50,10 +94,8 @@ urlpatterns = [ regex=r'^documents/recent_added/$', name='document_list_recent_added', view=RecentAddedDocumentListView.as_view() ), - url( - regex=r'^documents/deleted/$', name='document_list_deleted', - view=DeletedDocumentListView.as_view() - ), + + url( regex=r'^documents/duplicated/$', name='duplicated_document_list', view=DuplicatedDocumentListView.as_view() @@ -63,20 +105,20 @@ urlpatterns = [ view=FavoriteDocumentListView.as_view() ), url( - regex=r'^documents/(?P\d+)/preview/$', + regex=r'^documents/(?P\d+)/preview/$', name='document_preview', view=DocumentPreviewView.as_view() ), url( - regex=r'^documents/(?P\d+)/properties/$', + regex=r'^documents/(?P\d+)/properties/$', name='document_properties', view=DocumentView.as_view() ), url( - regex=r'^documents/(?P\d+)/duplicates/$', + regex=r'^documents/(?P\d+)/duplicates/$', name='document_duplicates_list', view=DocumentDuplicatesListView.as_view() ), url( - regex=r'^documents/(?P\d+)/add_to_favorites/$', + regex=r'^documents/(?P\d+)/add_to_favorites/$', name='document_add_to_favorites', view=FavoriteAddView.as_view() ), url( @@ -85,7 +127,7 @@ urlpatterns = [ view=FavoriteAddView.as_view() ), url( - regex=r'^documents/(?P\d+)/remove_from_favorites/$', + regex=r'^documents/(?P\d+)/remove_from_favorites/$', name='document_remove_from_favorites', view=FavoriteRemoveView.as_view() ), @@ -95,50 +137,25 @@ urlpatterns = [ view=FavoriteRemoveView.as_view() ), url( - regex=r'^documents/(?P\d+)/restore/$', - name='document_restore', view=DocumentRestoreView.as_view() - ), - url( - regex=r'^documents/multiple/restore/$', - name='document_multiple_restore', - view=DocumentRestoreManyView.as_view() - ), - url( - regex=r'^documents/(?P\d+)/delete/$', - name='document_delete', view=DeletedDocumentDeleteView.as_view() - ), - url( - regex=r'^documents/multiple/delete/$', name='document_multiple_delete', - view=DeletedDocumentDeleteManyView.as_view() - ), - url( - regex=r'^documents/(?P\d+)/type/$', - name='document_document_type_edit', - view=DocumentDocumentTypeEditView.as_view() + regex=r'^documents/(?P\d+)/type/$', + name='document_change_type', + view=DocumentChangeTypeView.as_view() ), url( regex=r'^documents/multiple/type/$', - name='document_multiple_document_type_edit', - view=DocumentDocumentTypeEditView.as_view() + name='document_multiple_change_type', + view=DocumentChangeTypeView.as_view() ), url( - regex=r'^documents/(?P\d+)/trash/$', - name='document_trash', view=DocumentTrashView.as_view() - ), - url( - regex=r'^documents/multiple/trash/$', name='document_multiple_trash', - view=DocumentTrashManyView.as_view() - ), - url( - regex=r'^documents/(?P\d+)/edit/$', name='document_edit', + regex=r'^documents/(?P\d+)/edit/$', name='document_edit', view=DocumentEditView.as_view() ), url( - regex=r'^documents/(?P\d+)/print/$', - name='document_print', view=DocumentPrint.as_view() + regex=r'^documents/(?P\d+)/print/$', + name='document_print', view=DocumentPrintView.as_view() ), url( - regex=r'^documents/(?P\d+)/reset_page_count/$', + regex=r'^documents/(?P\d+)/reset_page_count/$', name='document_update_page_count', view=DocumentUpdatePageCountView.as_view() ), @@ -148,11 +165,11 @@ urlpatterns = [ view=DocumentUpdatePageCountView.as_view() ), url( - regex=r'^documents/(?P\d+)/download/form/$', + regex=r'^documents/(?P\d+)/download/form/$', name='document_download_form', view=DocumentDownloadFormView.as_view() ), url( - regex=r'^documents/(?P\d+)/download/$', + regex=r'^documents/(?P\d+)/download/$', name='document_download', view=DocumentDownloadView.as_view() ), url( @@ -165,46 +182,46 @@ urlpatterns = [ name='document_multiple_download', view=DocumentDownloadView.as_view() ), url( - regex=r'^documents/(?P\d+)/clear_transformations/$', + regex=r'^documents/(?P\d+)/transformations/clear/$', name='document_clear_transformations', view=DocumentTransformationsClearView.as_view() ), url( - regex=r'^documents/(?P\d+)/clone_transformations/$', + regex=r'^documents/(?P\d+)/transformations/clone/$', name='document_clone_transformations', view=DocumentTransformationsCloneView.as_view() ), url( - regex=r'^documents/(?P\d+)/version/all/$', + regex=r'^documents/(?P\d+)/versions/$', name='document_version_list', view=DocumentVersionListView.as_view() ), url( - regex=r'^documents/versions/(?P\d+)/download/form/$', + regex=r'^documents/versions/(?P\d+)/download/form/$', name='document_version_download_form', view=DocumentVersionDownloadFormView.as_view() ), url( - regex=r'^documents/versions/(?P\d+)/$', + regex=r'^documents/versions/(?P\d+)/$', name='document_version_view', view=DocumentVersionView.as_view() ), url( - regex=r'^documents/versions/(?P\d+)/download/$', + regex=r'^documents/versions/(?P\d+)/download/$', name='document_version_download', view=DocumentVersionDownloadView.as_view() ), url( - regex=r'^documents/versions/(?P\d+)/revert/$', + regex=r'^documents/versions/(?P\d+)/revert/$', name='document_version_revert', view=DocumentVersionRevertView.as_view() ), url( - regex=r'^documents/(?P\d+)/pages/$', + regex=r'^documents/(?P\d+)/pages/$', name='document_pages', view=DocumentPageListView.as_view() ), url( - regex=r'^documents/multiple/clear_transformations/$', + regex=r'^documents/multiple/transformations/clear/$', name='document_multiple_clear_transformations', view=DocumentTransformationsClearView.as_view() ), @@ -213,96 +230,92 @@ urlpatterns = [ view=ClearImageCacheView.as_view() ), url( - regex=r'^trash_can/empty/$', name='trash_can_empty', - view=EmptyTrashCanView.as_view() - ), - url( - regex=r'^pages/(?P\d+)/$', + regex=r'^documents/pages/(?P\d+)/$', name='document_page_view', view=DocumentPageView.as_view() ), url( - regex=r'^pages/(?P\d+)/navigation/next/$', + regex=r'^documents/pages/(?P\d+)/navigation/next/$', name='document_page_navigation_next', view=DocumentPageNavigationNext.as_view() ), url( - regex=r'^pages/(?P\d+)/navigation/previous/$', + regex=r'^documents/pages/(?P\d+)/navigation/previous/$', name='document_page_navigation_previous', view=DocumentPageNavigationPrevious.as_view() ), url( - regex=r'^pages/(?P\d+)/navigation/first/$', + regex=r'^documents/pages/(?P\d+)/navigation/first/$', name='document_page_navigation_first', view=DocumentPageNavigationFirst.as_view() ), url( - regex=r'^pages/(?P\d+)/navigation/last/$', + regex=r'^documents/pages/(?P\d+)/navigation/last/$', name='document_page_navigation_last', view=DocumentPageNavigationLast.as_view() ), url( - regex=r'^pages/(?P\d+)/zoom/in/$', + regex=r'^documents/pages/(?P\d+)/zoom/in/$', name='document_page_zoom_in', view=DocumentPageZoomInView.as_view() ), url( - regex=r'^pages/(?P\d+)/zoom/out/$', + regex=r'^documents/pages/(?P\d+)/zoom/out/$', name='document_page_zoom_out', view=DocumentPageZoomOutView.as_view() ), url( - regex=r'^pages/(?P\d+)/rotate/left/$', + regex=r'^documents/pages/(?P\d+)/rotate/left/$', name='document_page_rotate_left', view=DocumentPageRotateLeftView.as_view() ), url( - regex=r'^pages/(?P\d+)/rotate/right/$', + regex=r'^documents/pages/(?P\d+)/rotate/right/$', name='document_page_rotate_right', view=DocumentPageRotateRightView.as_view() ), url( - regex=r'^pages/(?P\d+)/reset/$', + regex=r'^documents/pages/(?P\d+)/reset/$', name='document_page_view_reset', view=DocumentPageViewResetView.as_view() ), # Admin views url( - regex=r'^types/$', name='document_type_list', + regex=r'^documents_types/$', name='document_type_list', view=DocumentTypeListView.as_view() ), url( - regex=r'^types/create/$', name='document_type_create', + regex=r'^documents_types/create/$', name='document_type_create', view=DocumentTypeCreateView.as_view() ), url( - regex=r'^types/(?P\d+)/edit/$', name='document_type_edit', - view=DocumentTypeEditView.as_view() + regex=r'^documents_types/(?P\d+)/edit/$', + name='document_type_edit', view=DocumentTypeEditView.as_view() ), url( - regex=r'^types/(?P\d+)/delete/$', name='document_type_delete', - view=DocumentTypeDeleteView.as_view() + regex=r'^documents_types/(?P\d+)/delete/$', + name='document_type_delete', view=DocumentTypeDeleteView.as_view() ), url( - regex=r'^types/(?P\d+)/documents/$', + regex=r'^documents_types/(?P\d+)/documents/$', name='document_type_document_list', view=DocumentTypeDocumentListView.as_view() ), url( - regex=r'^types/(?P\d+)/filenames/create/$', - name='document_type_filename_create', - view=DocumentTypeFilenameCreateView.as_view() - ), - url( - regex=r'^types/(?P\d+)/filenames/$', + regex=r'^documents_types/(?P\d+)/filenames/$', name='document_type_filename_list', view=DocumentTypeFilenameListView.as_view() ), url( - regex=r'^types/filenames/(?P\d+)/edit/$', + regex=r'^documents_types/(?P\d+)/filenames/create/$', + name='document_type_filename_create', + view=DocumentTypeFilenameCreateView.as_view() + ), + url( + regex=r'^documents_types/filenames/(?P\d+)/edit/$', name='document_type_filename_edit', view=DocumentTypeFilenameEditView.as_view() ), url( - regex=r'^types/filenames/(?P\d+)/delete/$', + regex=r'^documents_types/filenames/(?P\d+)/delete/$', name='document_type_filename_delete', view=DocumentTypeFilenameDeleteView.as_view() ), @@ -316,17 +329,20 @@ urlpatterns = [ ), ] -api_urls = [ +urlpatterns.extend(urlpatterns_trashed_documents) + +api_urls = [] +""" url( regex=r'^document_types/$', name='documenttype-list', view=APIDocumentTypeListView.as_view() ), url( - regex=r'^document_types/(?P\d+)/$', + regex=r'^document_types/(?P\d+)/$', name='documenttype-detail', view=APIDocumentTypeView.as_view() ), url( - regex=r'^document_types/(?P\d+)/documents/$', + regex=r'^document_types/(?P\d+)/documents/$', name='documenttype-document-list', view=APIDocumentTypeDocumentListView.as_view() ), @@ -335,30 +351,30 @@ api_urls = [ view=APIDocumentListView.as_view() ), url( - regex=r'^documents/(?P\d+)/$', name='document-detail', + regex=r'^documents/(?P\d+)/$', name='document-detail', view=APIDocumentView.as_view() ), url( - regex=r'^documents/(?P\d+)/download/$', + regex=r'^documents/(?P\d+)/download/$', name='document-download', view=APIDocumentDownloadView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/$', + regex=r'^documents/(?P\d+)/versions/$', name='document-version-list', view=APIDocumentVersionsListView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/(?P\d+)/$', + regex=r'^documents/(?P\d+)/versions/(?P\d+)/$', name='documentversion-detail', view=APIDocumentVersionView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/$', + regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/$', name='documentversion-page-list', view=APIDocumentVersionPageListView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/(?P\d+)/download/$', + regex=r'^documents/(?P\d+)/versions/(?P\d+)/download/$', name='documentversion-download', view=APIDocumentVersionDownloadView.as_view() ), @@ -367,24 +383,45 @@ api_urls = [ view=APIRecentDocumentListView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/(?P\d+)$', + regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/(?P\d+)$', name='documentpage-detail', view=APIDocumentPageView.as_view() ), url( - regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/(?P\d+)/image/$', + regex=r'^documents/(?P\d+)/versions/(?P\d+)/pages/(?P\d+)/image/$', name='documentpage-image', view=APIDocumentPageImageView.as_view() ), url( regex=r'^trashed_documents/$', name='trasheddocument-list', - view=APIDeletedDocumentListView.as_view() + view=APITrashedDocumentListView.as_view() ), url( - regex=r'^trashed_documents/(?P\d+)/$', - name='trasheddocument-detail', view=APIDeletedDocumentView.as_view() + regex=r'^trashed_documents/(?P\d+)/$', + name='trasheddocument-detail', view=APITrashedDocumentView.as_view() ), url( - regex=r'^trashed_documents/(?P\d+)/restore/$', + regex=r'^trashed_documents/(?P\d+)/restore/$', name='trasheddocument-restore', - view=APIDeletedDocumentRestoreView.as_view() + view=APITrashedDocumentRestoreView.as_view() ), ] +""" + + +api_router_entries = ( + { + 'prefix': r'documents', 'viewset': DocumentViewSet, + 'basename': 'document' + }, + { + 'prefix': r'document_types', 'viewset': DocumentTypeViewSet, + 'basename': 'document_type' + }, + { + 'prefix': r'documents/(?P\d+)/versions', + 'viewset': DocumentVersionViewSet, 'basename': 'document_version' + }, + { + 'prefix': r'documents/(?P\d+)/versions/(?P\d+)/pages', + 'viewset': DocumentPageViewSet, 'basename': 'document_page' + }, +) diff --git a/mayan/apps/documents/views/__init__.py b/mayan/apps/documents/views/__init__.py index 602a5c9dd7..5f07051fc7 100644 --- a/mayan/apps/documents/views/__init__.py +++ b/mayan/apps/documents/views/__init__.py @@ -3,3 +3,4 @@ from .document_type_views import * # NOQA from .document_version_views import * # NOQA from .document_views import * # NOQA from .misc_views import * # NOQA +from .trashed_document_views import * # NOQA diff --git a/mayan/apps/documents/views/document_page_views.py b/mayan/apps/documents/views/document_page_views.py index b4405c54ed..928bb852df 100644 --- a/mayan/apps/documents/views/document_page_views.py +++ b/mayan/apps/documents/views/document_page_views.py @@ -6,15 +6,14 @@ from furl import furl from django.conf import settings from django.contrib import messages -from django.shortcuts import get_object_or_404, resolve_url +from django.shortcuts import resolve_url from django.urls import reverse from django.utils.encoding import force_text -from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _ from django.views.generic import RedirectView -from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import SimpleView, SingleObjectListView +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.common.utils import resolve from mayan.apps.converter.literals import DEFAULT_ROTATION, DEFAULT_ZOOM_LEVEL @@ -37,53 +36,40 @@ __all__ = ( logger = logging.getLogger(__name__) -class DocumentPageInteractiveTransformation(RedirectView): - def dispatch(self, request, *args, **kwargs): - obj = self.get_object() - - AccessControlList.objects.check_access( - obj=obj, permissions=permission_document_view, user=request.user, - ) - - return super(DocumentPageInteractiveTransformation, self).dispatch( - request, *args, **kwargs - ) +class DocumentPageInteractiveTransformation(ExternalObjectMixin, RedirectView): + external_object_class = DocumentPage + external_object_permission = permission_document_view + external_object_pk_url_kwarg = 'document_page_id' def get_object(self): - return get_object_or_404( - klass=DocumentPage, pk=self.kwargs['document_page_pk'] - ) + return self.get_external_object() def get_redirect_url(self, *args, **kwargs): - url = reverse( - viewname='documents:document_page_view', - kwargs={'document_page_pk': self.kwargs['document_page_pk']} - ) - query_dict = { - 'rotation': int( - self.request.GET.get('rotation', DEFAULT_ROTATION) - ), 'zoom': int(self.request.GET.get('zoom', DEFAULT_ZOOM_LEVEL)) + 'rotation': self.request.GET.get('rotation', DEFAULT_ROTATION), + 'zoom': self.request.GET.get('zoom', DEFAULT_ZOOM_LEVEL) } - self.transformation_function(query_dict) + url = furl( + args=query_dict, path=reverse( + viewname='documents:document_page_view', + kwargs={'document_page_id': self.kwargs['document_page_id']} + ) - return '{}?{}'.format(url, urlencode(query_dict)) - - -class DocumentPageListView(SingleObjectListView): - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_document(), permissions=permission_document_view, - user=self.request.user, ) - return super( - DocumentPageListView, self - ).dispatch(request, *args, **kwargs) + self.transformation_function(query_dict=query_dict) + + return url.tostr() + + +class DocumentPageListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = Document + external_object_permission = permission_document_view + external_object_pk_url_kwarg = 'document_id' def get_document(self): - return get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) + return self.get_external_object() def get_extra_context(self): return { @@ -95,27 +81,17 @@ class DocumentPageListView(SingleObjectListView): 'title': _('Pages for document: %s') % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().pages.all() -class DocumentPageNavigationBase(RedirectView): - def dispatch(self, request, *args, **kwargs): - document_page = self.get_object() - - AccessControlList.objects.check_access( - permissions=permission_document_view, user=request.user, - obj=document_page.document - ) - - return super(DocumentPageNavigationBase, self).dispatch( - request, *args, **kwargs - ) +class DocumentPageNavigationBase(ExternalObjectMixin, RedirectView): + external_object_class = DocumentPage + external_object_permission = permission_document_view + external_object_pk_url_kwarg = 'document_page_id' def get_object(self): - return get_object_or_404( - klass=DocumentPage, pk=self.kwargs['document_page_pk'] - ) + return self.get_external_object() def get_redirect_url(self, *args, **kwargs): """ @@ -158,14 +134,14 @@ class DocumentPageNavigationFirst(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() - return {'document_page_pk': document_page.siblings.first().pk} + return {'document_page_id': document_page.siblings.first().pk} class DocumentPageNavigationLast(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() - return {'document_page_pk': document_page.siblings.last().pk} + return {'document_page_id': document_page.siblings.last().pk} class DocumentPageNavigationNext(DocumentPageNavigationBase): @@ -178,12 +154,12 @@ class DocumentPageNavigationNext(DocumentPageNavigationBase): ) except DocumentPage.DoesNotExist: messages.warning( - request=self.request, message=_( + message=_( 'There are no more pages in this document' - ) + ), request=self.request ) finally: - return {'document_page_pk': document_page.pk} + return {'document_page_id': document_page.pk} class DocumentPageNavigationPrevious(DocumentPageNavigationBase): @@ -196,47 +172,40 @@ class DocumentPageNavigationPrevious(DocumentPageNavigationBase): ) except DocumentPage.DoesNotExist: messages.warning( - request=self.request, message=_( + message=_( 'You are already at the first page of this document' - ) + ), request=self.request ) finally: - return {'document_page_pk': document_page.pk} + return {'document_page_id': document_page.pk} class DocumentPageRotateLeftView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): query_dict['rotation'] = ( - query_dict['rotation'] - setting_rotation_step.value + int(query_dict['rotation']) - setting_rotation_step.value ) % 360 class DocumentPageRotateRightView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): query_dict['rotation'] = ( - query_dict['rotation'] + setting_rotation_step.value + int(query_dict['rotation']) + setting_rotation_step.value ) % 360 -class DocumentPageView(SimpleView): +class DocumentPageView(ExternalObjectMixin, SimpleView): + external_object_class = DocumentPage + external_object_permission = permission_document_view + external_object_pk_url_kwarg = 'document_page_id' template_name = 'appearance/generic_form.html' - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - permissions=permission_document_view, user=request.user, - obj=self.get_object().document - ) - - return super( - DocumentPageView, self - ).dispatch(request, *args, **kwargs) - def get_extra_context(self): zoom = int(self.request.GET.get('zoom', DEFAULT_ZOOM_LEVEL)) rotation = int(self.request.GET.get('rotation', DEFAULT_ROTATION)) document_page_form = DocumentPageForm( - instance=self.get_object(), zoom=zoom, rotation=rotation + instance=self.get_object(), rotation=rotation, zoom=zoom ) base_title = _('Image of: %s') % self.get_object() @@ -252,15 +221,13 @@ class DocumentPageView(SimpleView): 'navigation_object_list': ('page',), 'page': self.get_object(), 'rotation': rotation, - 'title': ' '.join((base_title, zoom_text,)), + 'title': ' '.join((base_title, zoom_text)), 'read_only': True, 'zoom': zoom, } def get_object(self): - return get_object_or_404( - klass=DocumentPage, pk=self.kwargs['document_page_pk'] - ) + return self.get_external_object() class DocumentPageViewResetView(RedirectView): @@ -269,7 +236,7 @@ class DocumentPageViewResetView(RedirectView): class DocumentPageZoomInView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): - zoom = query_dict['zoom'] + setting_zoom_percent_step.value + zoom = int(query_dict['zoom']) + setting_zoom_percent_step.value if zoom > setting_zoom_max_level.value: zoom = setting_zoom_max_level.value @@ -279,7 +246,7 @@ class DocumentPageZoomInView(DocumentPageInteractiveTransformation): class DocumentPageZoomOutView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): - zoom = query_dict['zoom'] - setting_zoom_percent_step.value + zoom = int(query_dict['zoom']) - setting_zoom_percent_step.value if zoom < setting_zoom_min_level.value: zoom = setting_zoom_min_level.value diff --git a/mayan/apps/documents/views/document_type_views.py b/mayan/apps/documents/views/document_type_views.py index 8835754ef5..becfbc4d3b 100644 --- a/mayan/apps/documents/views/document_type_views.py +++ b/mayan/apps/documents/views/document_type_views.py @@ -2,16 +2,15 @@ from __future__ import absolute_import, unicode_literals import logging -from django.shortcuts import get_object_or_404 from django.template import RequestContext from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ -from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( SingleObjectCreateView, SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from ..forms import DocumentTypeFilenameForm_create from ..icons import icon_document_type_filename, icon_document_type_setup @@ -60,7 +59,7 @@ class DocumentTypeDeleteView(SingleObjectDeleteView): model = DocumentType object_permission = permission_document_type_delete post_action_redirect = reverse_lazy(viewname='documents:document_type_list') - pk_url_kwarg = 'document_type_pk' + pk_url_kwarg = 'document_type_id' def get_extra_context(self): return { @@ -70,13 +69,17 @@ class DocumentTypeDeleteView(SingleObjectDeleteView): } -class DocumentTypeDocumentListView(DocumentListView): - def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['document_type_pk']) +class DocumentTypeDocumentListView(ExternalObjectMixin, DocumentListView): + external_object_class = DocumentType + external_object_permission = permission_document_type_view + external_object_pk_url_kwarg = 'document_type_id' def get_document_queryset(self): return self.get_document_type().documents.all() + def get_document_type(self): + return self.get_external_object() + def get_extra_context(self): context = super(DocumentTypeDocumentListView, self).get_extra_context() context.update( @@ -95,7 +98,7 @@ class DocumentTypeEditView(SingleObjectEditView): ) model = DocumentType object_permission = permission_document_type_edit - pk_url_kwarg = 'document_type_pk' + pk_url_kwarg = 'document_type_id' post_action_redirect = reverse_lazy( viewname='documents:document_type_list' ) @@ -136,21 +139,14 @@ class DocumentTypeListView(SingleObjectListView): } -class DocumentTypeFilenameCreateView(SingleObjectCreateView): +class DocumentTypeFilenameCreateView(ExternalObjectMixin, SingleObjectCreateView): + external_object_class = DocumentType + external_object_permission = permission_document_type_edit + external_object_pk_url_kwarg = 'document_type_id' form_class = DocumentTypeFilenameForm_create - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - permissions=permission_document_type_edit, user=request.user, - obj=self.get_document_type() - ) - - return super(DocumentTypeFilenameCreateView, self).dispatch( - request, *args, **kwargs - ) - def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['document_type_pk']) + return self.get_external_object() def get_extra_context(self): return { @@ -168,7 +164,7 @@ class DocumentTypeFilenameCreateView(SingleObjectCreateView): class DocumentTypeFilenameDeleteView(SingleObjectDeleteView): model = DocumentTypeFilename object_permission = permission_document_type_edit - pk_url_kwarg = 'filename_pk' + pk_url_kwarg = 'filename_id' def get_extra_context(self): return { @@ -187,7 +183,7 @@ class DocumentTypeFilenameDeleteView(SingleObjectDeleteView): def get_post_action_redirect(self): return reverse( viewname='documents:document_type_filename_list', - kwargs={'document_type_pk': self.get_object().document_type.pk} + kwargs={'document_type_id': self.get_object().document_type.pk} ) @@ -195,7 +191,7 @@ class DocumentTypeFilenameEditView(SingleObjectEditView): fields = ('enabled', 'filename',) model = DocumentTypeFilename object_permission = permission_document_type_edit - pk_url_kwarg = 'filename_pk' + pk_url_kwarg = 'filename_id' def get_extra_context(self): document_type_filename = self.get_object() @@ -216,16 +212,17 @@ class DocumentTypeFilenameEditView(SingleObjectEditView): def get_post_action_redirect(self): return reverse( viewname='documents:document_type_filename_list', - kwargs={'document_type_pk': self.get_object().document_type.pk} + kwargs={'document_type_id': self.get_object().document_type.pk} ) -class DocumentTypeFilenameListView(SingleObjectListView): - access_object_retrieve_method = 'get_document_type' - object_permission = permission_document_type_view +class DocumentTypeFilenameListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = DocumentType + external_object_permission = permission_document_type_view + external_object_pk_url_kwarg = 'document_type_id' def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['document_type_pk']) + return self.get_external_object() def get_extra_context(self): return { @@ -255,5 +252,5 @@ class DocumentTypeFilenameListView(SingleObjectListView): ) % self.get_document_type(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document_type().filenames.all() diff --git a/mayan/apps/documents/views/document_version_views.py b/mayan/apps/documents/views/document_version_views.py index 2e999435bd..b5608346a3 100644 --- a/mayan/apps/documents/views/document_version_views.py +++ b/mayan/apps/documents/views/document_version_views.py @@ -3,15 +3,14 @@ from __future__ import absolute_import, unicode_literals import logging from django.contrib import messages -from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ -from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( ConfirmView, SingleObjectDetailView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin -from ..events import event_document_view +from ..events import event_document_download, event_document_view from ..forms import DocumentVersionDownloadForm, DocumentVersionPreviewForm from ..models import Document, DocumentVersion from ..permissions import ( @@ -21,79 +20,22 @@ from ..permissions import ( from .document_views import DocumentDownloadFormView, DocumentDownloadView +__all__ = ( + 'DocumentVersionDownloadFormView', 'DocumentVersionDownloadView', + 'DocumentVersionListView', 'DocumentVersionRevertView', + 'DocumentVersionView' +) logger = logging.getLogger(__name__) -class DocumentVersionListView(SingleObjectListView): - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_document(), - permissions=permission_document_version_view, user=request.user - ) - - self.get_document().add_as_recent_document_for_user(user=request.user) - - return super( - DocumentVersionListView, self - ).dispatch(request, *args, **kwargs) - - def get_document(self): - return get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) - - def get_extra_context(self): - return { - 'hide_object': True, - 'list_as_items': True, - 'object': self.get_document(), - 'table_cell_container_classes': 'td-container-thumbnail', - 'title': _('Versions of document: %s') % self.get_document(), - } - - def get_object_list(self): - return self.get_document().versions.order_by('-timestamp') - - -class DocumentVersionRevertView(ConfirmView): - object_permission = permission_document_version_revert - - def get_extra_context(self): - return { - 'message': _( - 'All later version after this one will be deleted too.' - ), - 'object': self.get_object().document, - 'title': _('Revert to this version?'), - } - - def get_object(self): - return get_object_or_404( - klass=DocumentVersion, pk=self.kwargs['document_version_pk'] - ) - - def view_action(self): - try: - self.get_object().revert(_user=self.request.user) - messages.success( - request=self.request, message=_( - 'Document version reverted successfully' - ) - ) - except Exception as exception: - messages.error( - request=self.request, - message=_('Error reverting document version; %s') % exception - ) - - class DocumentVersionDownloadFormView(DocumentDownloadFormView): form_class = DocumentVersionDownloadForm model = DocumentVersion - multiple_download_view = None - pk_url_kwarg = 'document_version_pk' + pk_url_kwarg = 'document_version_id' querystring_form_fields = ( 'compressed', 'zip_filename', 'preserve_extension' ) - single_download_view = 'documents:document_version_download' + viewname = 'documents:document_version_download' def get_extra_context(self): result = super( @@ -106,30 +48,26 @@ class DocumentVersionDownloadFormView(DocumentDownloadFormView): return result - def get_document_queryset(self): - id_list = self.request.GET.get( - 'id_list', self.request.POST.get('id_list', '') - ) - - if not id_list: - id_list = self.kwargs['document_version_pk'] - - return self.model.objects.filter( - pk__in=id_list.split(',') - ) - class DocumentVersionDownloadView(DocumentDownloadView): model = DocumentVersion object_permission = permission_document_download - pk_url_kwarg = 'document_version_pk' + pk_url_kwarg = 'document_version_id' + + @staticmethod + def commit_event(item, request): + # TODO: Improve by adding a document version download event + event_document_download.commit( + actor=request.user, + target=item.document + ) @staticmethod def get_item_file(item): return item.file def get_encoding(self): - return self.get_object().encoding + return self.get_object_list().first().encoding def get_item_label(self, item): preserve_extension = self.request.GET.get( @@ -143,13 +81,69 @@ class DocumentVersionDownloadView(DocumentDownloadView): return item.get_rendered_string(preserve_extension=preserve_extension) def get_mimetype(self): - return self.get_object().mimetype + return self.get_object_list().first().mimetype + + +class DocumentVersionListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = Document + external_object_permission = permission_document_version_view + external_object_pk_url_kwarg = 'document_id' + + def get_document(self): + document = self.get_external_object() + document.add_as_recent_document_for_user(user=self.request.user) + return document + + def get_extra_context(self): + return { + 'hide_object': True, + 'list_as_items': True, + 'object': self.get_document(), + 'table_cell_container_classes': 'td-container-thumbnail', + 'title': _('Versions of document: %s') % self.get_document(), + } + + def get_source_queryset(self): + return self.get_document().versions.order_by('-timestamp') + + +class DocumentVersionRevertView(ExternalObjectMixin, ConfirmView): + external_object_class = DocumentVersion + external_object_permission = permission_document_version_revert + external_object_pk_url_kwarg = 'document_version_id' + + def get_extra_context(self): + return { + 'message': _( + 'All later version after this one will be deleted too.' + ), + 'object': self.get_object().document, + 'title': _('Revert to this version?'), + } + + def get_object(self): + return self.get_external_object() + + def view_action(self): + try: + self.get_object().revert(_user=self.request.user) + messages.success( + message=_( + 'Document version reverted successfully' + ), request=self.request + ) + except Exception as exception: + messages.error( + message=_('Error reverting document version; %s') % exception, + request=self.request + ) class DocumentVersionView(SingleObjectDetailView): form_class = DocumentVersionPreviewForm model = DocumentVersion object_permission = permission_document_version_view + pk_url_kwarg = 'document_version_id' def dispatch(self, request, *args, **kwargs): result = super( diff --git a/mayan/apps/documents/views/document_views.py b/mayan/apps/documents/views/document_views.py index 8f57427117..e94b10cae3 100644 --- a/mayan/apps/documents/views/document_views.py +++ b/mayan/apps/documents/views/document_views.py @@ -2,12 +2,13 @@ from __future__ import absolute_import, unicode_literals import logging +from furl import furl + from django.contrib import messages -from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 -from django.urls import reverse, reverse_lazy -from django.utils.http import urlencode +from django.urls import reverse +from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext @@ -15,11 +16,11 @@ from mayan.apps.acls.models import AccessControlList from mayan.apps.common.compressed_files import ZipArchive from mayan.apps.common.exceptions import ActionError from mayan.apps.common.generics import ( - ConfirmView, FormView, MultipleObjectConfirmActionView, - MultipleObjectFormActionView, SingleObjectDetailView, - SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView + FormView, MultipleObjectConfirmActionView, MultipleObjectDownloadView, + MultipleObjectFormActionView, SingleObjectDetailView, SingleObjectEditView, + SingleObjectListView ) -from mayan.apps.common.mixins import MultipleInstanceActionMixin +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.converter.models import Transformation from mayan.apps.converter.permissions import ( permission_transformation_delete, permission_transformation_edit @@ -32,32 +33,198 @@ from ..forms import ( DocumentTypeFilteredSelectForm ) from ..icons import ( - icon_document_list, icon_document_list_deleted, - icon_document_list_favorites, icon_document_list_recent_access, - icon_document_list_recent_added, icon_duplicated_document_list + icon_document_list, icon_document_list_favorites, + icon_document_list_recent_access, icon_document_list_recent_added, + icon_duplicated_document_list ) from ..literals import DEFAULT_ZIP_FILENAME, PAGE_RANGE_RANGE from ..models import ( - DeletedDocument, Document, DuplicatedDocument, FavoriteDocument, - RecentDocument + Document, DuplicatedDocument, FavoriteDocument, RecentDocument ) from ..permissions import ( - permission_document_delete, permission_document_download, - permission_document_print, permission_document_properties_edit, - permission_document_restore, permission_document_tools, - permission_document_trash, permission_document_view, - permission_empty_trash + permission_document_download, permission_document_print, + permission_document_properties_edit, permission_document_tools, + permission_document_view ) from ..settings import ( setting_favorite_count, setting_print_height, setting_print_width, setting_recent_added_count ) -from ..tasks import task_delete_document, task_update_page_count +from ..tasks import task_update_page_count from ..utils import parse_range +__all__ = ( + 'DocumentChangeTypeView', 'DocumentDownloadFormView', + 'DocumentDownloadView', 'DocumentDuplicatesListView', 'DocumentEditView', + 'DocumentListView', 'DocumentPreviewView', 'DocumentPrintView', + 'DocumentTransformationsClearView', 'DocumentTransformationsCloneView', + 'DocumentUpdatePageCountView', 'DocumentView', 'DuplicatedDocumentListView', + 'FavoriteAddView', 'FavoriteDocumentListView', 'FavoriteRemoveView', + 'RecentAccessDocumentListView', 'RecentAddedDocumentListView' +) logger = logging.getLogger(__name__) +class DocumentChangeTypeView(MultipleObjectFormActionView): + form_class = DocumentTypeFilteredSelectForm + model = Document + object_permission = permission_document_properties_edit + pk_url_kwarg = 'document_id' + success_message = _( + 'Document type change request performed on %(count)d document' + ) + success_message_plural = _( + 'Document type change request performed on %(count)d documents' + ) + + def get_extra_context(self): + queryset = self.get_object_list() + + result = { + 'submit_label': _('Change'), + 'title': ungettext( + singular='Change the type of the selected document', + plural='Change the type of the selected documents', + number=queryset.count() + ) + } + + if queryset.count() == 1: + result.update( + { + 'object': queryset.first(), + 'title': _( + 'Change the type of the document: %s' + ) % queryset.first() + } + ) + + return result + + def get_form_extra_kwargs(self): + result = { + 'user': self.request.user + } + + return result + + def object_action(self, form, instance): + instance.set_document_type( + document_type=form.cleaned_data['document_type'], + _user=self.request.user + ) + + messages.success( + message=_( + 'Document type for "%s" changed successfully.' + ) % instance, request=self.request + ) + + +class DocumentDownloadFormView(MultipleObjectFormActionView): + form_class = DocumentDownloadForm + model = Document + object_permission = permission_document_download + pk_url_kwarg = 'document_id' + querystring_form_fields = ('compressed', 'zip_filename') + viewname = 'documents:document_multiple_download' + + def form_valid(self, form): + # Turn a queryset into a comma separated list of primary keys + id_list = ','.join( + [ + force_text(pk) for pk in self.get_object_list().values_list('pk', flat=True) + ] + ) + + # Construct URL with querystring to pass on to the next view + url = furl( + args={ + 'id_list': id_list + }, path=reverse(viewname=self.viewname) + ) + + # Pass the form field data as URL querystring to the next view + for field in self.querystring_form_fields: + data = form.cleaned_data[field] + if data: + url.args['field'] = data + + return HttpResponseRedirect(redirect_to=url.tostr()) + + def get_extra_context(self): + context = { + 'submit_label': _('Download'), + 'title': _('Download documents'), + } + + if self.queryset.count() == 1: + context['object'] = self.queryset.first() + + return context + + def get_form_kwargs(self): + kwargs = super(DocumentDownloadFormView, self).get_form_kwargs() + self.queryset = self.get_object_list() + kwargs.update({'queryset': self.queryset}) + return kwargs + + +class DocumentDownloadView(MultipleObjectDownloadView): + model = Document + object_permission = permission_document_download + pk_url_kwarg = 'document_id' + + @staticmethod + def commit_event(item, request): + event_document_download.commit( + actor=request.user, + target=item + ) + + @staticmethod + def get_item_file(item): + return item.open() + + def get_file(self): + queryset = self.get_object_list() + zip_filename = self.request.GET.get( + 'zip_filename', DEFAULT_ZIP_FILENAME + ) + + if self.request.GET.get('compressed') == 'True' or queryset.count() > 1: + compressed_file = ZipArchive() + compressed_file.create() + for item in queryset: + with DocumentDownloadView.get_item_file(item=item) as file_object: + compressed_file.add_file( + file_object=file_object, + filename=self.get_item_label(item=item) + ) + DocumentDownloadView.commit_event( + item=item, request=self.request + ) + + compressed_file.close() + + return DocumentDownloadView.VirtualFile( + compressed_file.as_file(zip_filename), name=zip_filename + ) + else: + item = queryset.first() + DocumentDownloadView.commit_event( + item=item, request=self.request + ) + + return DocumentDownloadView.VirtualFile( + DocumentDownloadView.get_item_file(item=item), + name=self.get_item_label(item=item) + ) + + def get_item_label(self, item): + return item.label + + class DocumentListView(SingleObjectListView): object_permission = permission_document_view @@ -66,11 +233,11 @@ class DocumentListView(SingleObjectListView): return super(DocumentListView, self).get_context_data(**kwargs) except Exception as exception: messages.error( - request=self.request, message=_( + message=_( 'Error retrieving document list: %(exception)s.' ) % { 'exception': exception - } + }, request=self.request ) self.object_list = Document.objects.none() return super(DocumentListView, self).get_context_data(**kwargs) @@ -97,150 +264,19 @@ class DocumentListView(SingleObjectListView): 'title': _('All documents'), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document_queryset() -class DeletedDocumentDeleteView(ConfirmView): - extra_context = { - 'title': _('Delete the selected document?') - } - pk_url_kwarg = 'document_pk' - - def object_action(self, instance): - source_document = get_object_or_404( - klass=Document.passthrough, pk=instance.pk - ) - - AccessControlList.objects.check_access( - permissions=permission_document_delete, user=self.request.user, - obj=source_document - ) - - task_delete_document.apply_async( - kwargs={'deleted_document_id': instance.pk} - ) - - def view_action(self): - instance = get_object_or_404( - klass=DeletedDocument, pk=self.kwargs['document_pk'] - ) - self.object_action(instance=instance) - messages.success( - request=self.request, message=_( - 'Document: %(document)s deleted.' - ) % { - 'document': instance - } - ) - - -class DeletedDocumentDeleteManyView(MultipleInstanceActionMixin, DeletedDocumentDeleteView): - extra_context = { - 'title': _('Delete the selected documents?') - } - model = DeletedDocument - success_message = '%(count)d document deleted.' - success_message_plural = '%(count)d documents deleted.' - - -class DeletedDocumentListView(DocumentListView): - object_permission = None - - def get_document_queryset(self): - return AccessControlList.objects.filter_by_access( - permission=permission_document_view, - queryset=DeletedDocument.trash.all(), user=self.request.user - ) - - def get_extra_context(self): - context = super(DeletedDocumentListView, self).get_extra_context() - context.update( - { - 'hide_link': True, - 'no_results_icon': icon_document_list_deleted, - 'no_results_text': _( - 'To avoid loss of data, documents are not deleted ' - 'instantly. First, they are placed in the trash can. ' - 'From here they can be then finally deleted or restored.' - ), - 'no_results_title': _( - 'There are no documents in the trash can' - ), - 'title': _('Documents in trash'), - } - ) - return context - - -class DocumentDocumentTypeEditView(MultipleObjectFormActionView): - form_class = DocumentTypeFilteredSelectForm - model = Document - object_permission = permission_document_properties_edit - pk_url_kwarg = 'document_pk' - success_message = _( - 'Document type change request performed on %(count)d document' - ) - success_message_plural = _( - 'Document type change request performed on %(count)d documents' - ) - - def get_extra_context(self): - queryset = self.get_queryset() - - result = { - 'submit_label': _('Change'), - 'title': ungettext( - 'Change the type of the selected document', - 'Change the type of the selected documents', - queryset.count() - ) - } - - if queryset.count() == 1: - result.update( - { - 'object': queryset.first(), - 'title': _( - 'Change the type of the document: %s' - ) % queryset.first() - } - ) - - return result - - def get_form_extra_kwargs(self): - result = { - 'user': self.request.user - } - - return result - - def object_action(self, form, instance): - instance.set_document_type( - form.cleaned_data['document_type'], _user=self.request.user - ) - - messages.success( - request=self.request, message=_( - 'Document type for "%s" changed successfully.' - ) % instance - ) - - -class DocumentDuplicatesListView(DocumentListView): - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_document(), permissions=permission_document_view, - user=self.request.user - ) - - return super( - DocumentDuplicatesListView, self - ).dispatch(request, *args, **kwargs) +class DocumentDuplicatesListView(ExternalObjectMixin, DocumentListView): + external_object_class = Document + external_object_permission = permission_document_view + external_object_pk_url_kwarg = 'document_id' def get_document(self): - return get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) + document = self.get_external_object() + document.add_as_recent_document_for_user(user=self.request.user) + return document def get_extra_context(self): context = super(DocumentDuplicatesListView, self).get_extra_context() @@ -260,7 +296,7 @@ class DocumentDuplicatesListView(DocumentListView): ) return context - def get_object_list(self): + def get_source_queryset(self): return self.get_document().get_duplicates() @@ -268,7 +304,7 @@ class DocumentEditView(SingleObjectEditView): form_class = DocumentForm model = Document object_permission = permission_document_properties_edit - pk_url_kwarg = 'document_pk' + pk_url_kwarg = 'document_id' def dispatch(self, request, *args, **kwargs): result = super( @@ -291,7 +327,7 @@ class DocumentEditView(SingleObjectEditView): def get_post_action_redirect(self): return reverse( viewname='documents:document_properties', - kwargs={'document_pk': self.get_object().pk} + kwargs={'document_id': self.get_object().pk} ) @@ -299,7 +335,7 @@ class DocumentPreviewView(SingleObjectDetailView): form_class = DocumentPreviewForm model = Document object_permission = permission_document_view - pk_url_kwarg = 'document_pk' + pk_url_kwarg = 'document_id' def dispatch(self, request, *args, **kwargs): result = super( @@ -320,99 +356,11 @@ class DocumentPreviewView(SingleObjectDetailView): } -class DocumentRestoreView(ConfirmView): - extra_context = { - 'title': _('Restore the selected document?') - } - - def object_action(self, instance): - source_document = get_object_or_404( - klass=Document.passthrough, pk=instance.pk - ) - - AccessControlList.objects.check_access( - obj=source_document, permissions=permission_document_restore, - user=self.request.user - ) - - instance.restore() - - def view_action(self): - instance = get_object_or_404( - klass=DeletedDocument, pk=self.kwargs['document_pk'] - ) - - self.object_action(instance=instance) - - messages.success( - request=self.request, message=_( - 'Document: %(document)s restored.' - ) % { - 'document': instance - } - ) - - -class DocumentRestoreManyView(MultipleInstanceActionMixin, DocumentRestoreView): - extra_context = { - 'title': _('Restore the selected documents?') - } - model = DeletedDocument - success_message = '%(count)d document restored.' - success_message_plural = '%(count)d documents restored.' - - -class DocumentTrashView(ConfirmView): - def get_extra_context(self): - return { - 'object': self.get_object(), - 'title': _('Move "%s" to the trash?') % self.get_object() - } - - def get_object(self): - return get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) - - def get_post_action_redirect(self): - return reverse(viewname='documents:document_list_recent_access') - - def object_action(self, instance): - AccessControlList.objects.check_access( - obj=instance, permissions=permission_document_trash, - user=self.request.user - ) - - instance.delete() - - def view_action(self): - instance = self.get_object() - - self.object_action(instance=instance) - - messages.success( - request=self.request, message=_( - 'Document: %(document)s moved to trash successfully.' - ) % { - 'document': instance - } - ) - - -class DocumentTrashManyView(MultipleInstanceActionMixin, DocumentTrashView): - model = Document - success_message = '%(count)d document moved to the trash.' - success_message_plural = '%(count)d documents moved to the trash.' - - def get_extra_context(self): - return { - 'title': _('Move the selected documents to the trash?') - } - - class DocumentView(SingleObjectDetailView): form_class = DocumentPropertiesForm model = Document object_permission = permission_document_view - pk_url_kwarg = 'document_pk' + pk_url_kwarg = 'document_id' def dispatch(self, request, *args, **kwargs): result = super(DocumentView, self).dispatch(request, *args, **kwargs) @@ -427,195 +375,10 @@ class DocumentView(SingleObjectDetailView): } -class EmptyTrashCanView(ConfirmView): - extra_context = { - 'title': _('Empty trash?') - } - view_permission = permission_empty_trash - action_cancel_redirect = post_action_redirect = reverse_lazy( - viewname='documents:document_list_deleted' - ) - - def view_action(self): - for deleted_document in DeletedDocument.objects.all(): - task_delete_document.apply_async( - kwargs={'deleted_document_id': deleted_document.pk} - ) - - messages.success( - request=self.request, message=_('Trash emptied successfully') - ) - - -class DocumentDownloadFormView(FormView): - form_class = DocumentDownloadForm - model = Document - multiple_download_view = 'documents:document_multiple_download' - querystring_form_fields = ('compressed', 'zip_filename') - single_download_view = 'documents:document_download' - - def form_valid(self, form): - querystring_dictionary = {} - - for field in self.querystring_form_fields: - data = form.cleaned_data[field] - if data: - querystring_dictionary[field] = data - - querystring_dictionary.update( - { - 'id_list': ','.join( - map(str, self.queryset.values_list('pk', flat=True)) - ) - } - ) - - querystring = urlencode(querystring_dictionary, doseq=True) - - if self.queryset.count() > 1: - url = reverse(self.multiple_download_view) - else: - url = reverse( - viewname=self.single_download_view, - kwargs={'document_pk': self.queryset.first().pk} - ) - - return HttpResponseRedirect('{}?{}'.format(url, querystring)) - - def get_document_queryset(self): - id_list = self.request.GET.get( - 'id_list', self.request.POST.get('id_list', '') - ) - - if not id_list: - id_list = self.kwargs['document_pk'] - - return self.model.objects.filter( - pk__in=id_list.split(',') - ).filter(is_stub=False) - - def get_extra_context(self): - subtemplates_list = [ - { - 'name': 'appearance/generic_list_items_subtemplate.html', - 'context': { - 'hide_link': True, - 'hide_links': True, - 'hide_multi_item_actions': True, - 'object_list': self.queryset - } - } - ] - - context = { - 'submit_label': _('Download'), - 'subtemplates_list': subtemplates_list, - 'title': _('Download documents'), - } - - if self.queryset.count() == 1: - context['object'] = self.queryset.first() - - return context - - def get_form_kwargs(self): - kwargs = super(DocumentDownloadFormView, self).get_form_kwargs() - self.queryset = self.get_queryset() - kwargs.update({'queryset': self.queryset}) - return kwargs - - def get_queryset(self): - return AccessControlList.objects.filter_by_access( - permission=permission_document_download, - queryset=self.get_document_queryset(), user=self.request.user - ) - - -class DocumentDownloadView(SingleObjectDownloadView): - model = Document - # Set to None to disable the .get_object call - object_permission = None - pk_url_kwarg = 'document_pk' - - @staticmethod - def commit_event(item, request): - if isinstance(item, Document): - event_document_download.commit( - actor=request.user, - target=item - ) - else: - # TODO: Improve by adding a document version download event - event_document_download.commit( - actor=request.user, - target=item.document - ) - - @staticmethod - def get_item_file(item): - return item.open() - - def get_document_queryset(self): - id_list = self.request.GET.get( - 'id_list', self.request.POST.get('id_list', '') - ) - - if not id_list: - id_list = self.kwargs[self.pk_url_kwarg] - - queryset = self.model.objects.filter(pk__in=id_list.split(',')) - - return AccessControlList.objects.filter_by_access( - permission_document_download, self.request.user, queryset - ) - - def get_file(self): - queryset = self.get_document_queryset() - zip_filename = self.request.GET.get( - 'zip_filename', DEFAULT_ZIP_FILENAME - ) - - if self.request.GET.get('compressed') == 'True' or queryset.count() > 1: - compressed_file = ZipArchive() - compressed_file.create() - for item in queryset: - with DocumentDownloadView.get_item_file(item=item) as file_object: - compressed_file.add_file( - file_object=file_object, - filename=self.get_item_label(item=item) - ) - DocumentDownloadView.commit_event( - item=item, request=self.request - ) - - compressed_file.close() - - return DocumentDownloadView.VirtualFile( - compressed_file.as_file(zip_filename), name=zip_filename - ) - else: - item = queryset.first() - if item: - DocumentDownloadView.commit_event( - item=item, request=self.request - ) - else: - raise PermissionDenied - - return DocumentDownloadView.VirtualFile( - DocumentDownloadView.get_item_file(item=item), - name=self.get_item_label(item=item) - ) - - def get_item_label(self, item): - return item.label - - class DocumentUpdatePageCountView(MultipleObjectConfirmActionView): model = Document object_permission = permission_document_tools - pk_url_kwarg = 'document_pk' - + pk_url_kwarg = 'document_id' success_message = _( '%(count)d document queued for page count recalculation' ) @@ -654,20 +417,20 @@ class DocumentUpdatePageCountView(MultipleObjectConfirmActionView): ) else: messages.error( - request=self.request, message=_( + message=_( 'Document "%(document)s" is empty. Upload at least one ' 'document version before attempting to detect the ' 'page count.' ) % { 'document': instance, - } + }, request=self.request ) class DocumentTransformationsClearView(MultipleObjectConfirmActionView): model = Document object_permission = permission_transformation_delete - pk_url_kwarg = 'document_pk' + pk_url_kwarg = 'document_id' success_message = _( 'Transformation clear request processed for %(count)d document' ) @@ -705,16 +468,19 @@ class DocumentTransformationsClearView(MultipleObjectConfirmActionView): Transformation.objects.get_for_model(page).delete() except Exception as exception: messages.error( - request=self.request, message=_( + message=_( 'Error deleting the page transformations for ' 'document: %(document)s; %(error)s.' ) % { 'document': instance, 'error': exception - } + }, request=self.request ) -class DocumentTransformationsCloneView(FormView): +class DocumentTransformationsCloneView(ExternalObjectMixin, FormView): + external_object_class = Document + external_object_permission = permission_transformation_edit + external_object_pk_url_kwarg = 'document_id' form_class = DocumentPageNumberForm def form_valid(self, form): @@ -726,34 +492,29 @@ class DocumentTransformationsCloneView(FormView): ) for page in target_pages: - Transformation.objects.get_for_model(page).delete() + Transformation.objects.get_for_model(obj=page).delete() Transformation.objects.copy( source=form.cleaned_data['page'], targets=target_pages ) except Exception as exception: messages.error( - request=self.request, message=_( + message=_( 'Error deleting the page transformations for ' 'document: %(document)s; %(error)s.' ) % { 'document': instance, 'error': exception - } + }, request=self.request ) else: messages.success( - request=self.request, message=_( + message=_( 'Transformations cloned successfully.' - ) + ), request=self.request ) return super(DocumentTransformationsCloneView, self).form_valid(form=form) - def get_form_extra_kwargs(self): - return { - 'document': self.get_object() - } - def get_extra_context(self): instance = self.get_object() @@ -767,40 +528,28 @@ class DocumentTransformationsCloneView(FormView): return context + def get_form_extra_kwargs(self): + return { + 'document': self.get_object() + } + def get_object(self): - instance = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] - ) - - AccessControlList.objects.check_access( - obj=instance, permissions=permission_transformation_edit, - user=self.request.user - ) - - instance.add_as_recent_document_for_user(user=self.request.user) - - return instance + document = self.get_external_object() + document.add_as_recent_document_for_user(user=self.request.user) + return document -class DocumentPrint(FormView): +class DocumentPrintView(FormView): form_class = DocumentPrintForm def dispatch(self, request, *args, **kwargs): - instance = self.get_object() - AccessControlList.objects.check_access( - obj=instance, permissions=permission_document_print, - user=self.request.user - ) - - instance.add_as_recent_document_for_user(user=self.request.user) - self.page_group = self.request.GET.get('page_group') self.page_range = self.request.GET.get('page_range') - return super(DocumentPrint, self).dispatch(request, *args, **kwargs) + return super(DocumentPrintView, self).dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): if not self.page_group and not self.page_range: - return super(DocumentPrint, self).get(request, *args, **kwargs) + return super(DocumentPrintView, self).get(request, *args, **kwargs) else: instance = self.get_object() @@ -832,7 +581,7 @@ class DocumentPrint(FormView): context = { 'form_action': reverse( viewname='documents:document_print', - kwargs={'document_pk': instance.pk} + kwargs={'document_id': instance.pk} ), 'object': instance, 'submit_label': _('Submit'), @@ -844,7 +593,17 @@ class DocumentPrint(FormView): return context def get_object(self): - return get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) + obj = get_object_or_404( + klass=self.get_object_list(), pk=self.kwargs['document_id'] + ) + obj.add_as_recent_document_for_user(user=self.request.user) + return obj + + def get_object_list(self): + return AccessControlList.objects.restrict_queryset( + permission=permission_document_print, queryset=Document.objects.all(), + user=self.request.user + ) def get_template_names(self): if self.page_group or self.page_range: @@ -923,7 +682,7 @@ class FavoriteAddView(MultipleObjectConfirmActionView): def object_action(self, form, instance): FavoriteDocument.objects.add_for_user( - user=self.request.user, document=instance + document=instance, user=self.request.user ) @@ -939,7 +698,7 @@ class FavoriteRemoveView(MultipleObjectConfirmActionView): ) def get_extra_context(self): - queryset = self.get_queryset() + queryset = self.get_object_list() return { 'submit_label': _('Remove'), @@ -954,7 +713,7 @@ class FavoriteRemoveView(MultipleObjectConfirmActionView): def object_action(self, form, instance): try: FavoriteDocument.objects.remove_for_user( - user=self.request.user, document=instance + document=instance, user=self.request.user ) except FavoriteDocument.DoesNotExist: raise ActionError diff --git a/mayan/apps/documents/views/misc_views.py b/mayan/apps/documents/views/misc_views.py index d5d1d22967..3f0791b76f 100644 --- a/mayan/apps/documents/views/misc_views.py +++ b/mayan/apps/documents/views/misc_views.py @@ -24,7 +24,8 @@ class ClearImageCacheView(ConfirmView): def view_action(self): task_clear_image_cache.apply_async() messages.success( - self.request, _('Document cache clearing queued successfully.') + message=_('Document cache clearing queued successfully.'), + request=self.request ) @@ -37,5 +38,6 @@ class ScanDuplicatedDocuments(ConfirmView): def view_action(self): task_scan_duplicates_all.apply_async() messages.success( - self.request, _('Duplicated document scan queued successfully.') + message=_('Duplicated document scan queued successfully.'), + request=self.request ) diff --git a/mayan/apps/documents/views/trashed_document_views.py b/mayan/apps/documents/views/trashed_document_views.py new file mode 100644 index 0000000000..c00414a73f --- /dev/null +++ b/mayan/apps/documents/views/trashed_document_views.py @@ -0,0 +1,162 @@ +from __future__ import absolute_import, unicode_literals + +import logging + +from django.contrib import messages +from django.urls import reverse_lazy +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ungettext + +from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.generics import ( + ConfirmView, MultipleObjectConfirmActionView +) + +from ..icons import icon_trashed_document_list +from ..models import Document, TrashedDocument +from ..permissions import ( + permission_document_trash, permission_document_view, permission_empty_trash, + permission_trashed_document_delete, permission_trashed_document_restore +) +from ..tasks import task_delete_document + +from .document_views import DocumentListView + +__all__ = ( + 'DocumentTrashView', 'EmptyTrashCanView', 'TrashedDocumentDeleteView', + 'TrashedDocumentListView', 'TrashedDocumentRestoreView', +) +logger = logging.getLogger(__name__) + + +class DocumentTrashView(MultipleObjectConfirmActionView): + model = Document + object_permission = permission_document_trash + pk_url_kwarg = 'document_id' + success_message = _( + '%(count)d document moved to the trash.' + ) + success_message_plural = _( + '%(count)d documents moved to the trash.' + ) + + def get_extra_context(self): + queryset = self.get_object_list() + + result = { + 'title': ungettext( + single='Move the selected document to the trash?', + plural='Move the selected documents to the trash?', + number=queryset.count() + ) + } + + return result + + def object_action(self, form, instance): + instance.delete() + + +class EmptyTrashCanView(ConfirmView): + extra_context = { + 'title': _('Empty trash?') + } + view_permission = permission_empty_trash + action_cancel_redirect = post_action_redirect = reverse_lazy( + viewname='documents:trashed_document_list' + ) + + def view_action(self): + for trashed_document in TrashedDocument.objects.all(): + task_delete_document.apply_async( + kwargs={'trashed_document_id': trashed_document.pk} + ) + + messages.success( + request=self.request, message=_('Trash emptied successfully') + ) + + +class TrashedDocumentDeleteView(MultipleObjectConfirmActionView): + model = TrashedDocument + object_permission = permission_trashed_document_delete + pk_url_kwarg = 'trashed_document_id' + success_message = _( + '%(count)d trashed document deleted.' + ) + success_message_plural = _( + '%(count)d trashed documents deleted.' + ) + + def get_extra_context(self): + queryset = self.get_object_list() + + result = { + 'title': ungettext( + single='Delete the selected trashed document?', + plural='Delete the selected trashed documents?', + number=queryset.count() + ) + } + + return result + + def object_action(self, form, instance): + instance.delete() + + +class TrashedDocumentListView(DocumentListView): + object_permission = None + + def get_document_queryset(self): + return AccessControlList.objects.restrict_queryset( + permission=permission_document_view, + queryset=TrashedDocument.trash.all(), user=self.request.user + ) + + def get_extra_context(self): + context = super(TrashedDocumentListView, self).get_extra_context() + context.update( + { + 'hide_link': True, + 'no_results_icon': icon_trashed_document_list, + 'no_results_text': _( + 'To avoid loss of data, documents are not deleted ' + 'instantly. First, they are placed in the trash can. ' + 'From here they can be then finally deleted or restored.' + ), + 'no_results_title': _( + 'There are no documents in the trash can' + ), + 'title': _('Documents in trash'), + } + ) + return context + + +class TrashedDocumentRestoreView(MultipleObjectConfirmActionView): + model = TrashedDocument + object_permission = permission_trashed_document_restore + pk_url_kwarg = 'trashed_document_id' + success_message = _( + '%(count)d trashed document restored.' + ) + success_message_plural = _( + '%(count)d trashed documents restored.' + ) + + def get_extra_context(self): + queryset = self.get_object_list() + + result = { + 'title': ungettext( + single='Restore the selected trashed document?', + plural='Restore the selected trashed documents?', + number=queryset.count() + ) + } + + return result + + def object_action(self, form, instance): + instance.restore()