Add document trashed event

Closes GitLab issue #608. Thanks to Vikas Kedia (@vikaskedia)
for the report.

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-11-12 13:46:00 -04:00
parent 77cae991f4
commit 8d8fc76962
8 changed files with 96 additions and 67 deletions

View File

@@ -131,6 +131,8 @@
- Add the document template sandbox feature.
- Use the generic TemplateField for the expression field
of index tree templates.
- Add document trashed event. Closes GitLab issue #608
Thanks to Vikas Kedia (@vikaskedia) for the report.
3.2.10 (2019-XX-XX)
===================

View File

@@ -216,6 +216,7 @@ Bugs fixed or issues closed
- :gitlab-issue:`532` Workflow preview isn't updated right after transitions are modified
- :gitlab-issue:`540` hint-outdated/update documentation
- :gitlab-issue:`594` 3.2b1: Unable to install/run under Python 3.5/3.6/3.7
- :gitlab-issue:`608` How to know who put a document in trash can? [Video]
- :gitlab-issue:`634` Failing docker entrypoint when using secret config
- :gitlab-issue:`635` Build a docker image for Python3
- :gitlab-issue:`640` UX: "Toast" Popup position prevents access to actions

View File

@@ -18,6 +18,9 @@ event_document_new_version = namespace.add_event_type(
event_document_properties_edit = namespace.add_event_type(
label=_('Document properties edited'), name='document_edit'
)
event_document_trashed = namespace.add_event_type(
label=_('Document trashed'), name='document_trashed'
)
# The type of an existing document is changed to another type
event_document_type_change = namespace.add_event_type(
label=_('Document type changed'), name='document_type_change'

View File

@@ -5,7 +5,7 @@ import uuid
from django.apps import apps
from django.core.files import File
from django.db import models
from django.db import models, transaction
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
@@ -13,7 +13,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from ..events import (
event_document_create, event_document_properties_edit,
event_document_type_change,
event_document_trashed, event_document_type_change,
)
from ..managers import DocumentManager, PassthroughManager, TrashCanManager
from ..settings import setting_language
@@ -112,11 +112,14 @@ class Document(models.Model):
def delete(self, *args, **kwargs):
to_trash = kwargs.pop('to_trash', True)
_user = kwargs.pop('_user', True)
if not self.in_trash and to_trash:
self.in_trash = True
self.deleted_date_time = now()
self.save()
with transaction.atomic():
self.save(_commit_events=False)
event_document_trashed.commit(actor=_user, target=self)
else:
for version in self.versions.all():
version.delete()

View File

@@ -268,3 +268,50 @@ class DocumentViewTestMixin(object):
def _request_empty_trash_view(self):
return self.post(viewname='documents:trash_can_empty')
class TrashedDocumentViewTestMixin(object):
def _request_document_trash_get_view(self):
return self.get(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_trash_post_view(self):
return self.post(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_restore_get_view(self):
return self.get(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_restore_post_view(self):
return self.post(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_delete_get_view(self):
return self.get(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_delete_post_view(self):
return self.post(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_list_view(self):
return self.get(viewname='documents:document_list_deleted')

View File

@@ -3,13 +3,16 @@ from __future__ import unicode_literals
from actstream.models import Action
from django_downloadview import assert_download_response
from ..events import event_document_download, event_document_view
from ..events import (
event_document_download, event_document_trashed, event_document_view
)
from ..permissions import (
permission_document_download, permission_document_view
permission_document_download, permission_document_trash,
permission_document_view
)
from .base import GenericDocumentViewTestCase
from .mixins import TrashedDocumentViewTestMixin
TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label'
TEST_DOCUMENT_TYPE_2_LABEL = 'test document type 2 label'
@@ -32,21 +35,22 @@ class DocumentEventsTestMixin(object):
class DocumentEventsTestCase(
DocumentEventsTestMixin, GenericDocumentViewTestCase
DocumentEventsTestMixin, TrashedDocumentViewTestMixin,
GenericDocumentViewTestCase
):
def test_document_download_event_no_permissions(self):
def setUp(self):
super(DocumentEventsTestCase, self).setUp()
Action.objects.all().delete()
def test_document_download_event_no_permission(self):
response = self._request_test_document_download_view()
self.assertEqual(response.status_code, 403)
self.assertEqual(list(Action.objects.any(obj=self.test_document)), [])
def test_document_download_event_with_permissions(self):
def test_document_download_event_with_access(self):
self.expected_content_types = ('image/png; charset=utf-8',)
Action.objects.all().delete()
self.grant_access(
obj=self.test_document, permission=permission_document_download
)
@@ -66,17 +70,13 @@ class DocumentEventsTestCase(
self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_download.id)
def test_document_view_event_no_permissions(self):
Action.objects.all().delete()
def test_document_view_event_no_permission(self):
response = self._request_test_document_preview_view()
self.assertEqual(response.status_code, 404)
self.assertEqual(list(Action.objects.any(obj=self.test_document)), [])
def test_document_view_event_with_permissions(self):
Action.objects.all().delete()
def test_document_view_event_with_access(self):
self.grant_access(
obj=self.test_document, permission=permission_document_view
)
@@ -88,3 +88,22 @@ class DocumentEventsTestCase(
self.assertEqual(event.actor, self._test_case_user)
self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_view.id)
def test_document_trashed_view_event_no_permission(self):
response = self._request_document_trash_post_view()
self.assertEqual(response.status_code, 404)
self.assertEqual(list(Action.objects.any(obj=self.test_document)), [])
def test_document_trashed_view_event_with_access(self):
self.grant_access(
obj=self.test_document, permission=permission_document_trash
)
response = self._request_document_trash_post_view()
self.assertEqual(response.status_code, 302)
event = Action.objects.any(obj=self.test_document).first()
self.assertEqual(event.actor, self._test_case_user)
self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_trashed.id)

View File

@@ -7,57 +7,11 @@ from ..permissions import (
)
from .base import GenericDocumentViewTestCase
from .mixins import TrashedDocumentViewTestMixin
class TrashedDocumentTestMixin(object):
def _request_document_trash_get_view(self):
return self.get(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def _request_document_trash_post_view(self):
return self.post(
viewname='documents:document_trash', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_restore_get_view(self):
return self.get(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_restore_post_view(self):
return self.post(
viewname='documents:document_restore', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_delete_get_view(self):
return self.get(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_delete_post_view(self):
return self.post(
viewname='documents:document_delete', kwargs={
'pk': self.test_document.pk
}
)
def _request_trashed_document_list_view(self):
return self.get(viewname='documents:document_list_deleted')
class TrashedDocumentTestCase(
TrashedDocumentTestMixin, GenericDocumentViewTestCase
class TrashedDocumentViewTestCase(
TrashedDocumentViewTestMixin, GenericDocumentViewTestCase
):
def test_document_trash_get_view_no_permissions(self):
document_count = Document.objects.count()

View File

@@ -56,7 +56,7 @@ class DocumentTrashView(MultipleObjectConfirmActionView):
return result
def object_action(self, form, instance):
instance.delete()
instance.delete(_user=self.request.user)
class EmptyTrashCanView(ConfirmView):