diff --git a/docs/releases/2.2.rst b/docs/releases/2.2.rst index f0ae030c6c..b160a603b4 100644 --- a/docs/releases/2.2.rst +++ b/docs/releases/2.2.rst @@ -48,6 +48,7 @@ calling the Tesseract executable. - API endpoint to view or change a role's permissions. - Make the lock_manager.backends.file_lock.FileLock the new default locking backend. +- Add view to clone a document page transformation to other pages. Removals -------- diff --git a/mayan/apps/common/generics.py b/mayan/apps/common/generics.py index d1bfaab802..ab67e9cc0c 100644 --- a/mayan/apps/common/generics.py +++ b/mayan/apps/common/generics.py @@ -30,27 +30,6 @@ __all__ = ( ) -class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormExtraKwargsMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): - """ - This view will present a form and upon receiving a POST request will - perform an action on an object or queryset - """ - - template_name = 'appearance/generic_form.html' - - def form_valid(self, form): - self.view_action(form=form) - return super(MultipleObjectFormActionView, self).form_valid(form=form) - - -class MultipleObjectConfirmActionView(ObjectActionMixin, MultipleObjectMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView): - template_name = 'appearance/generic_confirm.html' - - def post(self, request, *args, **kwargs): - self.view_action() - return HttpResponseRedirect(self.get_success_url()) - - class AssignRemoveView(ExtraContextMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, TemplateView): decode_content_type = False right_list_help_text = None @@ -198,7 +177,7 @@ class ConfirmView(ObjectListPermissionFilterMixin, ObjectPermissionCheckMixin, V return HttpResponseRedirect(self.get_success_url()) -class FormView(ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): +class FormView(FormExtraKwargsMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): template_name = 'appearance/generic_form.html' @@ -287,6 +266,27 @@ class MultiFormView(DjangoFormView): return self.forms_invalid(forms) +class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormExtraKwargsMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): + """ + This view will present a form and upon receiving a POST request will + perform an action on an object or queryset + """ + + template_name = 'appearance/generic_form.html' + + def form_valid(self, form): + self.view_action(form=form) + return super(MultipleObjectFormActionView, self).form_valid(form=form) + + +class MultipleObjectConfirmActionView(ObjectActionMixin, MultipleObjectMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView): + template_name = 'appearance/generic_confirm.html' + + def post(self, request, *args, **kwargs): + self.view_action() + return HttpResponseRedirect(self.get_success_url()) + + class SimpleView(ViewPermissionCheckMixin, ExtraContextMixin, TemplateView): pass diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py index b953ef3ba4..4d274da0ec 100644 --- a/mayan/apps/documents/apps.py +++ b/mayan/apps/documents/apps.py @@ -33,7 +33,8 @@ from statistics.classes import StatisticNamespace, CharJSLine from .handlers import create_default_document_type from .links import ( link_clear_image_cache, link_document_clear_transformations, - link_document_delete, link_document_document_type_edit, + link_document_clone_transformations, link_document_delete, + link_document_document_type_edit, link_document_multiple_document_type_edit, link_document_download, link_document_edit, link_document_list, link_document_list_deleted, link_document_list_recent, link_document_multiple_delete, @@ -374,6 +375,7 @@ class DocumentsApp(MayanAppConfig): link_document_edit, link_document_document_type_edit, link_document_print, link_document_trash, link_document_download, link_document_clear_transformations, + link_document_clone_transformations, link_document_update_page_count ), sources=(Document,) ) diff --git a/mayan/apps/documents/forms.py b/mayan/apps/documents/forms.py index ffc44facbe..37444188bf 100644 --- a/mayan/apps/documents/forms.py +++ b/mayan/apps/documents/forms.py @@ -223,3 +223,17 @@ class DocumentPrintForm(forms.Form): widget=forms.RadioSelect ) page_range = forms.CharField(label=_('Page range'), required=False) + + +class DocumentPageNumberForm(forms.Form): + page = forms.ModelChoiceField( + queryset=None, + help_text=_('Page number from which all the transformation will be ' + 'cloned. Existing transformations will be lost.' + ) + ) + + def __init__(self, *args, **kwargs): + self.document = kwargs.pop('document') + super(DocumentPageNumberForm, self).__init__(*args, **kwargs) + self.fields['page'].queryset = self.document.pages.all() diff --git a/mayan/apps/documents/links.py b/mayan/apps/documents/links.py index 308d7d99d3..436273eab8 100644 --- a/mayan/apps/documents/links.py +++ b/mayan/apps/documents/links.py @@ -2,7 +2,9 @@ from __future__ import absolute_import, unicode_literals from django.utils.translation import ugettext_lazy as _ -from converter.permissions import permission_transformation_delete +from converter.permissions import ( + permission_transformation_delete, permission_transformation_edit +) from navigation import Link from .permissions import ( @@ -64,6 +66,11 @@ link_document_clear_transformations = Link( text=_('Clear transformations'), view='documents:document_clear_transformations', args='resolved_object.id' ) +link_document_clone_transformations = Link( + permissions=(permission_transformation_edit,), + text=_('Clone transformations'), + view='documents:document_clone_transformations', args='resolved_object.id' +) link_document_delete = Link( permissions=(permission_document_delete,), tags='dangerous', text=_('Delete'), view='documents:document_delete', diff --git a/mayan/apps/documents/urls.py b/mayan/apps/documents/urls.py index 38fff707d6..5d13519187 100644 --- a/mayan/apps/documents/urls.py +++ b/mayan/apps/documents/urls.py @@ -21,15 +21,15 @@ from .views import ( DocumentPageRotateRightView, DocumentPageView, DocumentPageViewResetView, DocumentPageZoomInView, DocumentPageZoomOutView, DocumentPreviewView, DocumentPrint, DocumentRestoreView, DocumentRestoreManyView, - DocumentTransformationsClearView, DocumentTrashView, DocumentTrashManyView, - DocumentTypeCreateView, DocumentTypeDeleteView, - DocumentTypeDocumentListView, DocumentTypeFilenameCreateView, - DocumentTypeFilenameDeleteView, DocumentTypeFilenameEditView, - DocumentTypeFilenameListView, DocumentTypeListView, DocumentTypeEditView, - DocumentUpdatePageCountView, DocumentVersionDownloadFormView, - DocumentVersionDownloadView, DocumentVersionListView, - DocumentVersionRevertView, DocumentView, EmptyTrashCanView, - RecentDocumentListView, document_page_navigation_first, + DocumentTransformationsClearView, DocumentTransformationsCloneView, + DocumentTrashView, DocumentTrashManyView, DocumentTypeCreateView, + DocumentTypeDeleteView, DocumentTypeDocumentListView, + DocumentTypeFilenameCreateView, DocumentTypeFilenameDeleteView, + DocumentTypeFilenameEditView, DocumentTypeFilenameListView, + DocumentTypeListView, DocumentTypeEditView, DocumentUpdatePageCountView, + DocumentVersionDownloadFormView, DocumentVersionDownloadView, + DocumentVersionListView, DocumentVersionRevertView, DocumentView, + EmptyTrashCanView, RecentDocumentListView, document_page_navigation_first, document_page_navigation_last, document_page_navigation_next, document_page_navigation_previous ) @@ -125,7 +125,11 @@ urlpatterns = [ DocumentTransformationsClearView.as_view(), name='document_clear_transformations' ), - + url( + r'^(?P\d+)/clone_transformations/$', + DocumentTransformationsCloneView.as_view(), + name='document_clone_transformations' + ), url( r'^(?P\d+)/version/all/$', DocumentVersionListView.as_view(), name='document_version_list' diff --git a/mayan/apps/documents/views/document_views.py b/mayan/apps/documents/views/document_views.py index 9799383c20..8820992657 100644 --- a/mayan/apps/documents/views/document_views.py +++ b/mayan/apps/documents/views/document_views.py @@ -19,12 +19,15 @@ from common.generics import ( ) from common.mixins import MultipleInstanceActionMixin from converter.models import Transformation -from converter.permissions import permission_transformation_delete +from converter.permissions import ( + permission_transformation_delete, permission_transformation_edit +) from ..events import event_document_download, event_document_view from ..forms import ( - DocumentDownloadForm, DocumentForm, DocumentPreviewForm, - DocumentPrintForm, DocumentPropertiesForm, DocumentTypeSelectForm, + DocumentDownloadForm, DocumentForm, DocumentPageNumberForm, + DocumentPreviewForm, DocumentPrintForm, DocumentPropertiesForm, + DocumentTypeSelectForm, ) from ..literals import PAGE_RANGE_RANGE, DEFAULT_ZIP_FILENAME from ..models import DeletedDocument, Document, RecentDocument @@ -597,6 +600,70 @@ class DocumentTransformationsClearView(MultipleObjectConfirmActionView): ) +class DocumentTransformationsCloneView(FormView): + form_class = DocumentPageNumberForm + + def form_valid(self, form): + instance = self.get_object() + + try: + target_pages = instance.pages.exclude( + pk=form.cleaned_data['page'].pk + ) + + for page in target_pages: + Transformation.objects.get_for_model(page).delete() + + Transformation.objects.copy( + source=form.cleaned_data['page'], targets=target_pages + ) + except Exception as exception: + messages.error( + self.request, _( + 'Error deleting the page transformations for ' + 'document: %(document)s; %(error)s.' + ) % { + 'document': instance, 'error': exception + } + ) + else: + messages.success( + self.request, _('Transformations cloned successfully.') + ) + + 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() + + context = { + 'object': instance, + 'submit_label': _('Submit'), + 'title': _( + 'Clone page transformations for document: %s' + ) % instance, + } + + return context + + def get_object(self): + instance = get_object_or_404(Document, pk=self.kwargs['pk']) + + AccessControlList.objects.check_access( + permissions=permission_transformation_edit, + user=self.request.user, obj=instance + ) + + instance.add_as_recent_document_for_user(self.request.user) + + return instance + + class DocumentPrint(FormView): form_class = DocumentPrintForm @@ -631,7 +698,6 @@ class DocumentPrint(FormView): context = { 'object': instance, - 'submit_method': 'POST', 'submit_label': _('Submit'), 'title': _('Print: %s') % instance, }