diff --git a/mayan/apps/document_signatures/views.py b/mayan/apps/document_signatures/views.py index 4cf27a4507..6fc866c638 100644 --- a/mayan/apps/document_signatures/views.py +++ b/mayan/apps/document_signatures/views.py @@ -111,9 +111,7 @@ class DocumentVersionDetachedSignatureCreateView(FormView): def get_extra_context(self): return { - 'document': self.get_document_version().document, - 'document_version': self.get_document_version(), - 'navigation_object_list': ('document', 'document_version'), + 'object': self.get_document_version(), 'title': _( 'Sign document version "%s" with a detached signature' ) % self.get_document_version(), @@ -212,9 +210,7 @@ class DocumentVersionEmbeddedSignatureCreateView(FormView): def get_extra_context(self): return { - 'document': self.get_document_version().document, - 'document_version': self.get_document_version(), - 'navigation_object_list': ('document', 'document_version'), + 'object': self.get_document_version(), 'title': _( 'Sign document version "%s" with a embedded signature' ) % self.get_document_version(), @@ -237,11 +233,7 @@ class DocumentVersionSignatureDeleteView(SingleObjectDeleteView): def get_extra_context(self): return { - 'document': self.get_object().document_version.document, - 'document_version': self.get_object().document_version, - 'navigation_object_list': ( - 'document', 'document_version', 'signature' - ), + 'object': self.get_object().document_version, 'signature': self.get_object(), 'title': _('Delete detached signature: %s') % self.get_object() } @@ -260,13 +252,9 @@ class DocumentVersionSignatureDetailView(SingleObjectDetailView): def get_extra_context(self): return { - 'document': self.get_object().document_version.document, - 'document_version': self.get_object().document_version, - 'signature': self.get_object(), - 'navigation_object_list': ( - 'document', 'document_version', 'signature' - ), 'hide_object': True, + 'object': self.get_object().document_version, + 'signature': self.get_object(), 'title': _( 'Details for signature: %s' ) % self.get_object(), @@ -305,10 +293,8 @@ class DocumentVersionSignatureListView(SingleObjectListView): def get_extra_context(self): return { - 'document': self.get_document_version().document, - 'document_version': self.get_document_version(), - 'navigation_object_list': ('document', 'document_version'), 'hide_object': True, + 'object': self.get_document_version(), 'title': _( 'Signatures for document version: %s' ) % self.get_document_version(), @@ -337,9 +323,7 @@ class DocumentVersionSignatureUploadView(SingleObjectCreateView): def get_extra_context(self): return { - 'document': self.get_document_version().document, - 'document_version': self.get_document_version(), - 'navigation_object_list': ('document', 'document_version'), + 'object': self.get_document_version(), 'title': _( 'Upload detached signature for document version: %s' ) % self.get_document_version(), diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py index b694a46ad3..b4738a47af 100644 --- a/mayan/apps/documents/apps.py +++ b/mayan/apps/documents/apps.py @@ -62,8 +62,10 @@ from .links import ( link_document_type_filename_list, link_document_type_list, link_document_type_setup, link_document_update_page_count, link_document_version_download, link_document_version_list, - link_document_version_revert, link_duplicated_document_list, - link_duplicated_document_scan, link_trash_can_empty + link_document_version_return_document, link_document_version_return_list, + link_document_version_revert, link_document_version_view, + link_duplicated_document_list, link_duplicated_document_scan, + link_trash_can_empty ) from .literals import ( CHECK_DELETE_PERIOD_INTERVAL, CHECK_TRASH_PERIOD_INTERVAL, @@ -89,7 +91,10 @@ from .statistics import ( new_document_versions_per_month, total_document_per_month, total_document_page_per_month, total_document_version_per_month ) -from .widgets import DocumentThumbnailWidget, DocumentPageThumbnailWidget +from .widgets import ( + DocumentThumbnailWidget, DocumentPageThumbnailWidget, + DocumentVersionThumbnailWidget +) class DocumentsApp(MayanAppConfig): @@ -176,9 +181,11 @@ class DocumentsApp(MayanAppConfig): ) # Document and document page thumbnail widget - document_thumbnail_widget = DocumentThumbnailWidget() document_page_thumbnail_widget = DocumentPageThumbnailWidget() + document_thumbnail_widget = DocumentThumbnailWidget() + document_version_thumbnail_widget = DocumentVersionThumbnailWidget() + # Document SourceColumn( source=Document, label=_('Thumbnail'), func=lambda context: document_thumbnail_widget.render( @@ -189,6 +196,7 @@ class DocumentsApp(MayanAppConfig): source=Document, label=_('Type'), attribute='document_type' ) + # DocumentPage SourceColumn( source=DocumentPage, label=_('Thumbnail'), func=lambda context: document_page_thumbnail_widget.render( @@ -208,6 +216,7 @@ class DocumentsApp(MayanAppConfig): attribute='document_version.document.document_type' ) + # DocumentType SourceColumn( source=DocumentType, label=_('Documents'), func=lambda context: context['object'].get_document_count( @@ -220,6 +229,7 @@ class DocumentsApp(MayanAppConfig): func=lambda context: two_state_template(context['object'].enabled) ) + # DeletedDocument SourceColumn( source=DeletedDocument, label=_('Thumbnail'), func=lambda context: document_thumbnail_widget.render( @@ -235,6 +245,7 @@ class DocumentsApp(MayanAppConfig): attribute='deleted_date_time' ) + # DocumentVersion SourceColumn( source=DocumentVersion, label=_('Time and date'), attribute='timestamp' @@ -251,6 +262,14 @@ class DocumentsApp(MayanAppConfig): source=DocumentVersion, label=_('Comment'), attribute='comment' ) + SourceColumn( + source=DocumentVersion, label=_('Thumbnail'), + func=lambda context: document_version_thumbnail_widget.render( + instance=context['object'] + ) + ) + + # DuplicatedDocument SourceColumn( source=DuplicatedDocument, label=_('Thumbnail'), func=lambda context: document_thumbnail_widget.render( @@ -422,7 +441,8 @@ class DocumentsApp(MayanAppConfig): # Document actions menu_object.bind_links( links=( - link_document_version_revert, link_document_version_download + link_document_version_view, link_document_version_revert, + link_document_version_download ), sources=(DocumentVersion,) ) @@ -464,6 +484,14 @@ class DocumentsApp(MayanAppConfig): links=(link_transformation_list,), sources=(DocumentPage,) ) + # Document versions + menu_facet.bind_links( + links=( + link_document_version_return_document, + link_document_version_return_list + ), sources=(DocumentVersion,) + ) + namespace = StatisticNamespace(slug='documents', label=_('Documents')) namespace.add_statistic( slug='new-documents-per-month', @@ -519,3 +547,5 @@ class DocumentsApp(MayanAppConfig): registry.register(DeletedDocument) registry.register(Document) + registry.register(DocumentType) + registry.register(DocumentVersion) diff --git a/mayan/apps/documents/forms.py b/mayan/apps/documents/forms.py index ab3dad828d..702c65913a 100644 --- a/mayan/apps/documents/forms.py +++ b/mayan/apps/documents/forms.py @@ -56,6 +56,22 @@ class DocumentPreviewForm(forms.Form): preview = forms.CharField(widget=DocumentPagesCarouselWidget()) +class DocumentVersionPreviewForm(forms.Form): + def __init__(self, *args, **kwargs): + document_version = kwargs.pop('instance', None) + super(DocumentVersionPreviewForm, self).__init__(*args, **kwargs) + + self.fields['preview'].initial = document_version + try: + self.fields['preview'].label = _( + 'Document pages (%d)' + ) % document_version.pages.count() + except AttributeError: + self.fields['preview'].label = _('Document version pages (%d)') % 0 + + preview = forms.CharField(widget=DocumentPagesCarouselWidget()) + + class DocumentForm(forms.ModelForm): """ Form sub classes from DocumentForm used only when editing a document diff --git a/mayan/apps/documents/links.py b/mayan/apps/documents/links.py index 29ce7ffaf2..5e6b2a5ff4 100644 --- a/mayan/apps/documents/links.py +++ b/mayan/apps/documents/links.py @@ -142,10 +142,26 @@ link_document_multiple_update_page_count = Link( link_document_multiple_restore = Link( text=_('Restore'), view='documents:document_multiple_restore' ) + +# Versions link_document_version_download = Link( args='resolved_object.pk', permissions=(permission_document_download,), text=_('Download version'), view='documents:document_version_download_form' ) +link_document_version_return_document = Link( + icon='fa fa-file', permissions=(permission_document_view,), + text=_('Document'), view='documents:document_preview', + args='resolved_object.document.pk' +) +link_document_version_return_list = Link( + icon='fa fa-code-fork', permissions=(permission_document_version_view,), + text=_('Versions'), view='documents:document_version_list', + args='resolved_object.document.pk' +) +link_document_version_view = Link( + args='resolved_object.pk', permissions=(permission_document_version_view,), + text=_('Details'), view='documents:document_version_view' +) # Views link_document_list = Link( diff --git a/mayan/apps/documents/models.py b/mayan/apps/documents/models.py index 217542fa54..02621fb0f9 100644 --- a/mayan/apps/documents/models.py +++ b/mayan/apps/documents/models.py @@ -7,6 +7,7 @@ import uuid from django.conf import settings from django.core.files import File from django.db import models, transaction +from django.template import Template, Context from django.urls import reverse from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.timezone import now @@ -395,7 +396,9 @@ class DocumentVersion(models.Model): verbose_name_plural = _('Document version') def __str__(self): - return '{0} - {1}'.format(self.document, self.timestamp) + return Template( + '{{ instance.document }} - {{ instance.timestamp }}' + ).render(context=Context({'instance': self})) def delete(self, *args, **kwargs): for page in self.pages.all(): @@ -405,6 +408,9 @@ class DocumentVersion(models.Model): return super(DocumentVersion, self).delete(*args, **kwargs) + def get_absolute_url(self): + return reverse('documents:document_version_view', args=(self.pk,)) + def save(self, *args, **kwargs): """ Overloaded save method that updates the document version's checksum, @@ -453,7 +459,7 @@ class DocumentVersion(models.Model): else: if new_document_version: event_document_new_version.commit( - actor=user, target=self.document + actor=user, target=self, action_object=self.document ) post_version_upload.send(sender=DocumentVersion, instance=self) diff --git a/mayan/apps/documents/urls.py b/mayan/apps/documents/urls.py index e5c2250b3d..1fe04436f7 100644 --- a/mayan/apps/documents/urls.py +++ b/mayan/apps/documents/urls.py @@ -30,9 +30,9 @@ from .views import ( DocumentTypeFilenameEditView, DocumentTypeFilenameListView, DocumentTypeListView, DocumentTypeEditView, DocumentUpdatePageCountView, DocumentVersionDownloadFormView, DocumentVersionDownloadView, - DocumentVersionListView, DocumentVersionRevertView, DocumentView, - DuplicatedDocumentListView, EmptyTrashCanView, RecentDocumentListView, - ScanDuplicatedDocuments + DocumentVersionListView, DocumentVersionRevertView, DocumentVersionView, + DocumentView, DuplicatedDocumentListView, EmptyTrashCanView, + RecentDocumentListView, ScanDuplicatedDocuments ) @@ -148,6 +148,10 @@ urlpatterns = [ DocumentVersionDownloadFormView.as_view(), name='document_version_download_form' ), + url( + r'^document/version/(?P\d+)/$', DocumentVersionView.as_view(), + name='document_version_view' + ), url( r'^document/version/(?P\d+)/download/$', DocumentVersionDownloadView.as_view(), name='document_version_download' diff --git a/mayan/apps/documents/views/document_version_views.py b/mayan/apps/documents/views/document_version_views.py index 4140b262db..83e909d96b 100644 --- a/mayan/apps/documents/views/document_version_views.py +++ b/mayan/apps/documents/views/document_version_views.py @@ -7,8 +7,12 @@ from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from acls.models import AccessControlList -from common.generics import ConfirmView, SingleObjectListView +from common.generics import ( + ConfirmView, SingleObjectDetailView, SingleObjectListView +) +from ..events import event_document_view +from ..forms import DocumentVersionPreviewForm from ..models import Document, DocumentVersion from ..permissions import ( permission_document_download, permission_document_version_revert, @@ -38,7 +42,8 @@ class DocumentVersionListView(SingleObjectListView): def get_extra_context(self): return { - 'hide_object': True, 'object': self.get_document(), + 'list_as_items': True, + 'object': self.get_document(), 'title': _('Versions of document: %s') % self.get_document(), } @@ -96,3 +101,29 @@ class DocumentVersionDownloadFormView(DocumentDownloadFormView): class DocumentVersionDownloadView(DocumentDownloadView): model = DocumentVersion object_permission = permission_document_download + + +class DocumentVersionView(SingleObjectDetailView): + form_class = DocumentVersionPreviewForm + model = DocumentVersion + object_permission = permission_document_version_view + + def dispatch(self, request, *args, **kwargs): + result = super( + DocumentVersionView, self + ).dispatch(request, *args, **kwargs) + self.get_object().document.add_as_recent_document_for_user( + request.user + ) + event_document_view.commit( + actor=request.user, target=self.get_object().document + ) + + return result + + def get_extra_context(self): + return { + 'hide_labels': True, + 'object': self.get_object(), + 'title': _('Preview of document version: %s') % self.get_object(), + } diff --git a/mayan/apps/documents/widgets.py b/mayan/apps/documents/widgets.py index 55cf0dc673..9e6d961bc5 100644 --- a/mayan/apps/documents/widgets.py +++ b/mayan/apps/documents/widgets.py @@ -333,6 +333,48 @@ class DocumentPageThumbnailWidget(BaseDocumentThumbnailWidget): return force_text(instance) +class DocumentVersionThumbnailWidget(DocumentThumbnailWidget): + width = '100%' + + def get_click_view_kwargs(self, instance): + return { + 'pk': instance.document.pk, + 'version_pk': instance.pk, + 'page_pk': instance.pages.first().pk + } + + def get_click_view_url(self, instance): + first_page = instance.pages.first() + if first_page: + return super(DocumentVersionThumbnailWidget, self).get_click_view_url( + instance=instance + ) + else: + return '#' + + def get_preview_view_kwargs(self, instance): + return { + 'pk': instance.document.pk, + 'version_pk': instance.pk, + 'page_pk': instance.pages.first().pk + } + + def get_preview_view_url(self, instance): + first_page = instance.pages.first() + if first_page: + return super(DocumentVersionThumbnailWidget, self).get_preview_view_url( + instance=instance + ) + else: + return '' + + def get_title(self, instance): + return getattr(instance, 'label', None) + + def is_valid(self, instance): + return instance.pages + + class InteractiveDocumentPageWidget(BaseDocumentThumbnailWidget): click_view_name = None