Documents now have their own dedicated DocumentPage submodel. The old DocumentPage is now called DocumentVersionPage. This allows mappings between document pages and document version pages, allowing renumbering, appending pages. DocumentPages have a content_object to map them to any other object. For now they only map to DocumentVersionPages. New option added to the version upload form to append the pages of the new version. A new view was added to just append new pages with wraps the new document version upload form and hides the append pages checkbox set to True. Add a new action, reset_pages to reset the pages of the document to those of the latest version. Missing: appending tests, checks for proper content_object in OCR and document parsing. Author: Roberto Rosario <roberto.rosario@mayan-edms.com> Date: Thu Oct 11 12:00:25 2019 -0400
327 lines
10 KiB
Python
327 lines
10 KiB
Python
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, DocumentVersionPage
|
|
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()
|