Implement document trash can and soft delete support. First half or gh-issue 188.
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
<div class="row">
|
||||
{% get_menu_links 'front page menu' as resolved_links %}
|
||||
{% with 'navigation/large_button_link.html' as link_template %}
|
||||
{% with 'col-xs-12 col-sm-6 col-md-4 col-lg-4' as div_class %}
|
||||
{% with 'col-xs-12 col-sm-6 col-md-4 col-lg-3' as div_class %}
|
||||
{% for object_navigation_links in resolved_links %}
|
||||
{% include 'navigation/generic_navigation.html' %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -31,26 +31,27 @@ from .links import (
|
||||
link_document_delete, link_document_document_type_edit,
|
||||
link_document_events_view, link_document_multiple_document_type_edit,
|
||||
link_document_download, link_document_edit, link_document_list,
|
||||
link_document_list_recent, link_document_multiple_delete,
|
||||
link_document_multiple_clear_transformations,
|
||||
link_document_multiple_download, link_document_multiple_update_page_count,
|
||||
link_document_list_deleted, link_document_list_recent,
|
||||
link_document_multiple_delete, link_document_multiple_clear_transformations,
|
||||
link_document_multiple_download, link_document_multiple_restore,
|
||||
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, link_document_page_return,
|
||||
link_document_page_rotate_left, link_document_page_rotate_right,
|
||||
link_document_page_view, 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_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, 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_document_page_navigation_next, link_document_page_navigation_previous,
|
||||
link_document_page_return, link_document_page_rotate_left,
|
||||
link_document_page_rotate_right, link_document_page_view,
|
||||
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_restore,
|
||||
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,
|
||||
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
|
||||
)
|
||||
from .models import (
|
||||
Document, DocumentPage, DocumentType, DocumentTypeFilename,
|
||||
DeletedDocument, Document, DocumentPage, DocumentType, DocumentTypeFilename,
|
||||
DocumentVersion
|
||||
)
|
||||
from .permissions import (
|
||||
@@ -90,7 +91,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
menu_front_page.bind_links(links=[link_document_list_recent, link_document_list])
|
||||
menu_front_page.bind_links(links=[link_document_list_recent, link_document_list, link_document_list_deleted])
|
||||
menu_setup.bind_links(links=[link_document_type_setup])
|
||||
menu_tools.bind_links(links=[link_clear_image_cache])
|
||||
|
||||
@@ -102,6 +103,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
|
||||
# Document object links
|
||||
menu_object.bind_links(links=[link_document_edit, link_document_document_type_edit, link_document_print, link_document_delete, link_document_download, link_document_clear_transformations, link_document_update_page_count], sources=[Document])
|
||||
menu_object.bind_links(links=[link_document_restore], sources=[DeletedDocument])
|
||||
|
||||
# Document facet links
|
||||
menu_facet.bind_links(links=[link_acl_list], sources=[Document])
|
||||
@@ -113,6 +115,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
# Document actions
|
||||
menu_object.bind_links(links=[link_document_version_revert, link_document_version_download], sources=[DocumentVersion])
|
||||
menu_multi_item.bind_links(links=[link_document_multiple_clear_transformations, link_document_multiple_delete, link_document_multiple_download, link_document_multiple_update_page_count, link_document_multiple_document_type_edit], sources=[Document])
|
||||
menu_multi_item.bind_links(links=[link_document_multiple_restore], sources=[DeletedDocument])
|
||||
|
||||
# Document pages
|
||||
menu_facet.bind_links(links=[link_document_page_rotate_left, link_document_page_rotate_right, link_document_page_zoom_in, link_document_page_zoom_out, link_document_page_view_reset], sources=['documents:document_page_view'])
|
||||
|
||||
@@ -9,10 +9,10 @@ from navigation import Link
|
||||
from .permissions import (
|
||||
permission_document_delete, permission_document_download,
|
||||
permission_document_properties_edit, permission_document_print,
|
||||
permission_document_tools, permission_document_version_revert,
|
||||
permission_document_view, permission_document_type_create,
|
||||
permission_document_type_delete, permission_document_type_edit,
|
||||
permission_document_type_view
|
||||
permission_document_restore, permission_document_tools,
|
||||
permission_document_version_revert, permission_document_view,
|
||||
permission_document_type_create, permission_document_type_delete,
|
||||
permission_document_type_edit, permission_document_type_view
|
||||
)
|
||||
from .settings import setting_zoom_max_level, setting_zoom_min_level
|
||||
|
||||
@@ -52,17 +52,20 @@ link_document_document_type_edit = Link(permissions=[permission_document_propert
|
||||
link_document_download = Link(permissions=[permission_document_download], text=_('Download'), view='documents:document_download', args='object.id')
|
||||
link_document_print = Link(permissions=[permission_document_print], text=_('Print'), view='documents:document_print', args='object.id')
|
||||
link_document_update_page_count = Link(permissions=[permission_document_tools], text=_('Reset page count'), view='documents:document_update_page_count', args='object.pk')
|
||||
|
||||
# Views
|
||||
link_document_list = Link(icon='fa fa-file', text=_('All documents'), view='documents:document_list')
|
||||
link_document_list_recent = Link(icon='fa fa-clock-o', text=_('Recent documents'), view='documents:document_list_recent')
|
||||
link_document_restore = Link(permissions=[permission_document_restore], text=_('Restore'), view='documents:document_restore', args='object.pk')
|
||||
link_document_multiple_clear_transformations = Link(permissions=[permission_transformation_delete], text=_('Clear transformations'), view='documents:document_multiple_clear_transformations')
|
||||
link_document_multiple_delete = Link(permissions=[permission_document_delete], tags='dangerous', text=_('Delete'), view='documents:document_multiple_delete')
|
||||
link_document_multiple_document_type_edit = Link(permissions=[permission_document_properties_edit], text=_('Change type'), view='documents:document_multiple_document_type_edit')
|
||||
link_document_multiple_download = Link(permissions=[permission_document_download], text=_('Download'), view='documents:document_multiple_download')
|
||||
link_document_multiple_update_page_count = Link(permissions=[permission_document_tools], text=_('Reset page count'), view='documents:document_multiple_update_page_count')
|
||||
link_document_multiple_restore = Link(permissions=[permission_document_restore], text=_('Restore'), view='documents:document_multiple_restore')
|
||||
link_document_version_download = Link(args='object.pk', permissions=[permission_document_download], text=_('Download'), view='documents:document_version_download')
|
||||
|
||||
# Views
|
||||
link_document_list = Link(icon='fa fa-file', text=_('All documents'), view='documents:document_list')
|
||||
link_document_list_recent = Link(icon='fa fa-clock-o', text=_('Recent documents'), view='documents:document_list_recent')
|
||||
link_document_list_deleted = Link(icon='fa fa-trash', text=_('Deleted documents'), view='documents:document_list_deleted')
|
||||
|
||||
# Tools
|
||||
link_clear_image_cache = Link(
|
||||
icon='fa fa-file-image-o',
|
||||
@@ -97,5 +100,3 @@ link_document_type_filename_edit = Link(permissions=[permission_document_type_ed
|
||||
link_document_type_filename_list = Link(permissions=[permission_document_type_view], text=_('Filenames'), view='documents:document_type_filename_list', args='resolved_object.id')
|
||||
link_document_type_list = Link(permissions=[permission_document_type_view], text=_('Document types'), view='documents:document_type_list')
|
||||
link_document_type_setup = Link(icon='fa fa-file', permissions=[permission_document_type_view], text=_('Document types'), view='documents:document_type_list')
|
||||
|
||||
link_tools = Link
|
||||
|
||||
@@ -37,6 +37,9 @@ class DocumentTypeManager(models.Manager):
|
||||
|
||||
|
||||
class DocumentManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return TrashCanQuerySet(self.model, using=self._db).filter(in_trash=False)
|
||||
|
||||
def invalidate_cache(self):
|
||||
for document in self.model.objects.all():
|
||||
document.invalidate_cache()
|
||||
@@ -73,3 +76,20 @@ class DocumentManager(models.Manager):
|
||||
version = document.new_version(file_object=file_object, user=user)
|
||||
document.set_document_type(document_type, force=True)
|
||||
return version
|
||||
|
||||
|
||||
class TrashCanManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(TrashCanManager, self).get_queryset().filter(in_trash=True)
|
||||
|
||||
|
||||
class TrashCanQuerySet(models.QuerySet):
|
||||
def delete(self, to_trash=True):
|
||||
for instance in self:
|
||||
instance.delete(to_trash=to_trash)
|
||||
|
||||
#if to_trash:
|
||||
# for instance in self:
|
||||
# instance.delete(to_trash=to_trash)
|
||||
#else:
|
||||
# super(TrashCanQuerySet, self).delete()
|
||||
|
||||
20
mayan/apps/documents/migrations/0009_document_in_trash.py
Normal file
20
mayan/apps/documents/migrations/0009_document_in_trash.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('documents', '0008_auto_20150624_0520'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='document',
|
||||
name='in_trash',
|
||||
field=models.BooleanField(default=False, verbose_name='In trash?', editable=False),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
@@ -28,7 +28,7 @@ from .events import (
|
||||
event_document_version_revert
|
||||
)
|
||||
from .managers import (
|
||||
DocumentManager, DocumentTypeManager, RecentDocumentManager
|
||||
DocumentManager, DocumentTypeManager, RecentDocumentManager, TrashCanManager
|
||||
)
|
||||
from .runtime import storage_backend
|
||||
from .settings import (
|
||||
@@ -79,6 +79,7 @@ class Document(models.Model):
|
||||
description = models.TextField(blank=True, null=True, verbose_name=_('Description'))
|
||||
date_added = models.DateTimeField(verbose_name=_('Added'), auto_now_add=True)
|
||||
language = models.CharField(choices=setting_language_choices.value, default=setting_language.value, max_length=8, verbose_name=_('Language'))
|
||||
in_trash = models.BooleanField(default=False, editable=False, verbose_name=_('In trash?'))
|
||||
|
||||
objects = DocumentManager()
|
||||
|
||||
@@ -121,9 +122,18 @@ class Document(models.Model):
|
||||
RecentDocument.objects.add_document_for_user(user, self)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
for version in self.versions.all():
|
||||
version.delete()
|
||||
return super(Document, self).delete(*args, **kwargs)
|
||||
if not self.in_trash and kwargs.get('to_trash', True):
|
||||
self.in_trash = True
|
||||
self.save()
|
||||
else:
|
||||
for version in self.versions.all():
|
||||
version.delete()
|
||||
|
||||
return super(Document, self).delete(*args, **kwargs)
|
||||
|
||||
def restore(self):
|
||||
self.in_trash = False
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
@@ -209,6 +219,13 @@ class Document(models.Model):
|
||||
return self.save_to_file(temporary_path, buffer_size)
|
||||
|
||||
|
||||
class DeletedDocument(Document):
|
||||
objects = TrashCanManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class DocumentVersion(models.Model):
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ permission_document_edit = namespace.add_permission(name='document_edit', label=
|
||||
permission_document_new_version = namespace.add_permission(name='document_new_version', label=_('Create new document versions'))
|
||||
permission_document_properties_edit = namespace.add_permission(name='document_properties_edit', label=_('Edit document properties'))
|
||||
permission_document_print = namespace.add_permission(name='document_print', label=_('Can print documents'))
|
||||
permission_document_restore = namespace.add_permission(name='document_restore', label=_('Restore deleted document'))
|
||||
permission_document_tools = namespace.add_permission(name='document_tools', label=_('Execute document modifying tools'))
|
||||
permission_document_version_revert = namespace.add_permission(name='document_version_revert', label=_('Revert documents to a previous version'))
|
||||
permission_document_view = namespace.add_permission(name='document_view', label=_('View documents'))
|
||||
|
||||
@@ -11,16 +11,20 @@ from .api_views import (
|
||||
)
|
||||
from .settings import setting_print_size, setting_display_size
|
||||
from .views import (
|
||||
DocumentListView, DocumentPageListView, RecentDocumentListView
|
||||
DeletedDocumentListView, DocumentListView, DocumentManyRestoreView,
|
||||
DocumentPageListView, DocumentRestoreView, RecentDocumentListView
|
||||
)
|
||||
|
||||
urlpatterns = patterns(
|
||||
'documents.views',
|
||||
url(r'^list/$', DocumentListView.as_view(), name='document_list'),
|
||||
url(r'^list/recent/$', RecentDocumentListView.as_view(), name='document_list_recent'),
|
||||
url(r'^list/deleted/$', DeletedDocumentListView.as_view(), name='document_list_deleted'),
|
||||
|
||||
url(r'^(?P<document_id>\d+)/preview/$', 'document_preview', name='document_preview'),
|
||||
url(r'^(?P<document_id>\d+)/properties/$', 'document_properties', name='document_properties'),
|
||||
url(r'^(?P<pk>\d+)/restore/$', DocumentRestoreView.as_view(), name='document_restore'),
|
||||
url(r'^multiple/restore/$', DocumentManyRestoreView.as_view(), name='document_multiple_restore'),
|
||||
url(r'^(?P<document_id>\d+)/type/$', 'document_document_type_edit', name='document_document_type_edit'),
|
||||
url(r'^multiple/type/$', 'document_multiple_document_type_edit', name='document_multiple_document_type_edit'),
|
||||
url(r'^(?P<document_id>\d+)/delete/$', 'document_delete', name='document_delete'),
|
||||
|
||||
@@ -16,8 +16,9 @@ from django.utils.translation import ugettext_lazy as _, ungettext
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from common.compressed_files import CompressedFile
|
||||
from common.mixins import MultipleInstanceActionMixin
|
||||
from common.utils import encapsulate, pretty_size
|
||||
from common.views import ParentChildListView, SingleObjectListView
|
||||
from common.views import ConfirmView, ParentChildListView, SingleObjectListView
|
||||
from common.widgets import two_state_template
|
||||
from converter.literals import (
|
||||
DEFAULT_PAGE_NUMBER, DEFAULT_ROTATION, DEFAULT_ZOOM_LEVEL
|
||||
@@ -37,16 +38,16 @@ from .forms import (
|
||||
)
|
||||
from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT
|
||||
from .models import (
|
||||
Document, DocumentType, DocumentPage, DocumentTypeFilename,
|
||||
DeletedDocument, Document, DocumentType, DocumentPage, DocumentTypeFilename,
|
||||
DocumentVersion, RecentDocument
|
||||
)
|
||||
from .permissions import (
|
||||
permission_document_delete, permission_document_download,
|
||||
permission_document_print, permission_document_properties_edit,
|
||||
permission_document_tools, permission_document_type_create,
|
||||
permission_document_type_delete, permission_document_type_edit,
|
||||
permission_document_type_view, permission_document_version_revert,
|
||||
permission_document_view,
|
||||
permission_document_restore, permission_document_tools,
|
||||
permission_document_type_create, permission_document_type_delete,
|
||||
permission_document_type_edit, permission_document_type_view,
|
||||
permission_document_version_revert, permission_document_view,
|
||||
)
|
||||
from .settings import (
|
||||
setting_preview_size, setting_recent_count, setting_rotation_step,
|
||||
@@ -76,6 +77,16 @@ class DocumentListView(SingleObjectListView):
|
||||
return super(DocumentListView, self).get_queryset()
|
||||
|
||||
|
||||
class DeletedDocumentListView(DocumentListView):
|
||||
extra_context = {
|
||||
'hide_link': True,
|
||||
'title': _('Deleted documents'),
|
||||
}
|
||||
|
||||
def get_document_queryset(self):
|
||||
return DeletedDocument.objects.all()
|
||||
|
||||
|
||||
class DocumentPageListView(ParentChildListView):
|
||||
object_permission = permission_document_view
|
||||
parent_queryset = Document.objects.all()
|
||||
@@ -106,6 +117,36 @@ class RecentDocumentListView(DocumentListView):
|
||||
return RecentDocument.objects.get_for_user(self.request.user)
|
||||
|
||||
|
||||
class DocumentRestoreView(ConfirmView):
|
||||
extra_context = {
|
||||
'title': _('Restore the selected document?')
|
||||
}
|
||||
|
||||
def object_action(self, request, instance):
|
||||
try:
|
||||
Permission.check_permissions(request.user, [permission_document_restore])
|
||||
except PermissionDenied:
|
||||
AccessControlList.objects.check_access(permission_document_restore, request.user, instance)
|
||||
|
||||
instance.restore()
|
||||
messages.success(request, _('Document: %(document)s restored.') % {
|
||||
'document': instance}
|
||||
)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
document = get_object_or_404(DeletedDocument, pk=self.kwargs['pk'])
|
||||
self.object_action(request=request, instance=document)
|
||||
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
|
||||
class DocumentManyRestoreView(MultipleInstanceActionMixin, DocumentRestoreView):
|
||||
extra_context = {
|
||||
'title': _('Restore the selected documents?')
|
||||
}
|
||||
model = DeletedDocument
|
||||
|
||||
|
||||
def document_properties(request, document_id):
|
||||
document = get_object_or_404(Document, pk=document_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user