from __future__ import absolute_import, unicode_literals import logging from furl import furl from django.contrib import messages from django.urls import reverse from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _, ungettext from django.views.generic import RedirectView from mayan.apps.common.generics import ( MultipleObjectConfirmActionView, SimpleView, SingleObjectListView ) from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.common.settings import setting_home_view from mayan.apps.common.utils import resolve from mayan.apps.converter.literals import DEFAULT_ROTATION, DEFAULT_ZOOM_LEVEL from ..forms import DocumentPageForm from ..icons import icon_document_pages from ..links import link_document_pages_reset from ..models import Document, DocumentPage from ..permissions import permission_document_edit, permission_document_view from ..settings import ( setting_rotation_step, setting_zoom_percent_step, setting_zoom_max_level, setting_zoom_min_level ) __all__ = ( 'DocumentPageDisable', 'DocumentPageEnable', 'DocumentPageListView', 'DocumentPageNavigationFirst', 'DocumentPageNavigationLast', 'DocumentPageNavigationNext', 'DocumentPageNavigationPrevious', 'DocumentPageView', 'DocumentPageViewResetView', 'DocumentPageInteractiveTransformation', 'DocumentPageZoomInView', 'DocumentPageZoomOutView', 'DocumentPageRotateLeftView', 'DocumentPageRotateRightView' ) logger = logging.getLogger(__name__) class DocumentPageListView(ExternalObjectMixin, SingleObjectListView): external_object_class = Document external_object_permission = permission_document_view external_object_pk_url_kwarg = 'pk' def get_extra_context(self): return { 'hide_object': True, 'list_as_items': True, 'no_results_icon': icon_document_pages, 'no_results_main_link': link_document_pages_reset.resolve( request=self.request, resolved_object=self.external_object ), 'no_results_text': _( 'This could mean that the document is of a format that is ' 'not supported, that it is corrupted, or that the upload ' 'process was interrupted. Use the document page reset ' 'action to attempt to introspect the page count again.' ), 'no_results_title': _('No document pages available'), 'object': self.external_object, 'title': _('Pages for document: %s') % self.external_object, } def get_source_queryset(self): return self.external_object.pages_all class DocumentPageNavigationBase(ExternalObjectMixin, RedirectView): external_object_class = DocumentPage external_object_permission = permission_document_view external_object_pk_url_kwarg = 'pk' def get_object(self): return self.external_object def get_redirect_url(self, *args, **kwargs): """ Attempt to jump to the same kind of view but resolved to a new object of the same kind. """ previous_url = self.request.META.get('HTTP_REFERER', None) if not previous_url: try: previous_url = self.get_object().get_absolute_url() except AttributeError: previous_url = reverse(viewname=setting_home_view.value) parsed_url = furl(url=previous_url) # Obtain the view name to be able to resolve it back with new keyword # arguments. resolver_match = resolve(path=force_text(parsed_url.path)) new_kwargs = self.get_new_kwargs() if set(new_kwargs) == set(resolver_match.kwargs): # It is the same type of object, reuse the URL to stay in the # same kind of view but pointing to a new object url = reverse( viewname=resolver_match.view_name, kwargs=new_kwargs ) else: url = parsed_url.path # Update just the path to retain the querystring in case there is # transformation data. parsed_url.path = url return parsed_url.tostr() class DocumentPageNavigationFirst(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() return {'pk': document_page.siblings.first().pk} class DocumentPageNavigationLast(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() return {'pk': document_page.siblings.last().pk} class DocumentPageNavigationNext(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() new_document_page = document_page.siblings.filter( page_number__gt=document_page.page_number ).first() if new_document_page: return {'pk': new_document_page.pk} else: messages.warning( message=_( 'There are no more pages in this document' ), request=self.request ) return {'pk': document_page.pk} class DocumentPageNavigationPrevious(DocumentPageNavigationBase): def get_new_kwargs(self): document_page = self.get_object() new_document_page = document_page.siblings.filter( page_number__lt=document_page.page_number ).last() if new_document_page: return {'pk': new_document_page.pk} else: messages.warning( message=_( 'You are already at the first page of this document' ), request=self.request ) return {'pk': document_page.pk} class DocumentPageView(ExternalObjectMixin, SimpleView): external_object_class = DocumentPage external_object_permission = permission_document_view external_object_pk_url_kwarg = 'pk' template_name = 'appearance/generic_form.html' 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(), rotation=rotation, zoom=zoom ) base_title = _('Image of: %s') % self.get_object() if zoom != DEFAULT_ZOOM_LEVEL: zoom_text = '({}%)'.format(zoom) else: zoom_text = '' return { 'form': document_page_form, 'hide_labels': True, 'navigation_object_list': ('page',), 'page': self.get_object(), 'rotation': rotation, 'title': ' '.join((base_title, zoom_text)), 'read_only': True, 'zoom': zoom, } def get_object(self): return self.external_object class DocumentPageViewResetView(RedirectView): pattern_name = 'documents:document_page_view' class DocumentPageInteractiveTransformation(ExternalObjectMixin, RedirectView): external_object_class = DocumentPage external_object_permission = permission_document_view external_object_pk_url_kwarg = 'pk' def get_object(self): return self.external_object def get_redirect_url(self, *args, **kwargs): query_dict = { 'rotation': self.request.GET.get('rotation', DEFAULT_ROTATION), 'zoom': self.request.GET.get('zoom', DEFAULT_ZOOM_LEVEL) } url = furl( args=query_dict, path=reverse( viewname='documents:document_page_view', kwargs={'pk': self.kwargs['pk']} ) ) self.transformation_function(query_dict=query_dict) # Refresh query_dict to args reference url.args = query_dict return url.tostr() class DocumentPageZoomInView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): zoom = int(query_dict['zoom']) + setting_zoom_percent_step.value if zoom > setting_zoom_max_level.value: zoom = setting_zoom_max_level.value query_dict['zoom'] = zoom class DocumentPageZoomOutView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): zoom = int(query_dict['zoom']) - setting_zoom_percent_step.value if zoom < setting_zoom_min_level.value: zoom = setting_zoom_min_level.value query_dict['zoom'] = zoom class DocumentPageRotateLeftView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): query_dict['rotation'] = ( int(query_dict['rotation']) - setting_rotation_step.value ) % 360 class DocumentPageRotateRightView(DocumentPageInteractiveTransformation): def transformation_function(self, query_dict): query_dict['rotation'] = ( int(query_dict['rotation']) + setting_rotation_step.value ) % 360 class DocumentPageDisable(MultipleObjectConfirmActionView): object_permission = permission_document_edit pk_url_kwarg = 'pk' success_message_singular = '%(count)d document page disabled.' success_message_plural = '%(count)d document pages disabled.' def get_extra_context(self): queryset = self.object_list result = { 'title': ungettext( singular='Disable the selected document page?', plural='Disable the selected document pages?', number=queryset.count() ) } if queryset.count() == 1: result['object'] = queryset.first() return result def get_source_queryset(self): return DocumentPage.passthrough.all() def object_action(self, form, instance): instance.enabled = False instance.save() class DocumentPageEnable(MultipleObjectConfirmActionView): object_permission = permission_document_edit pk_url_kwarg = 'pk' success_message_singular = '%(count)d document page enabled.' success_message_plural = '%(count)d document pages enabled.' def get_extra_context(self): queryset = self.object_list result = { 'title': ungettext( singular='Enable the selected document page?', plural='Enable the selected document pages?', number=queryset.count() ) } if queryset.count() == 1: result['object'] = queryset.first() return result def get_source_queryset(self): return DocumentPage.passthrough.all() def object_action(self, form, instance): instance.enabled = True instance.save()