Implement document trash can and soft delete support. First half or gh-issue 188.

This commit is contained in:
Roberto Rosario
2015-07-03 03:28:02 -04:00
parent e2e62d7640
commit 98b988038e
9 changed files with 147 additions and 40 deletions

View File

@@ -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 %}

View File

@@ -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'])

View File

@@ -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

View File

@@ -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()

View 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,
),
]

View File

@@ -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):
"""

View File

@@ -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'))

View File

@@ -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'),

View File

@@ -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)