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()