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
+2
View File
@@ -131,6 +131,8 @@
- Add the document template sandbox feature. - Add the document template sandbox feature.
- Use the generic TemplateField for the expression field - Use the generic TemplateField for the expression field
of index tree templates. 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) 3.2.10 (2019-XX-XX)
=================== ===================
+1
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:`532` Workflow preview isn't updated right after transitions are modified
- :gitlab-issue:`540` hint-outdated/update documentation - :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:`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:`634` Failing docker entrypoint when using secret config
- :gitlab-issue:`635` Build a docker image for Python3 - :gitlab-issue:`635` Build a docker image for Python3
- :gitlab-issue:`640` UX: "Toast" Popup position prevents access to actions - :gitlab-issue:`640` UX: "Toast" Popup position prevents access to actions
+3
View File
@@ -18,6 +18,9 @@ event_document_new_version = namespace.add_event_type(
event_document_properties_edit = namespace.add_event_type( event_document_properties_edit = namespace.add_event_type(
label=_('Document properties edited'), name='document_edit' 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 # The type of an existing document is changed to another type
event_document_type_change = namespace.add_event_type( event_document_type_change = namespace.add_event_type(
label=_('Document type changed'), name='document_type_change' label=_('Document type changed'), name='document_type_change'
@@ -5,7 +5,7 @@ import uuid
from django.apps import apps from django.apps import apps
from django.core.files import File from django.core.files import File
from django.db import models from django.db import models, transaction
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now from django.utils.timezone import now
@@ -13,7 +13,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from ..events import ( from ..events import (
event_document_create, event_document_properties_edit, 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 ..managers import DocumentManager, PassthroughManager, TrashCanManager
from ..settings import setting_language from ..settings import setting_language
@@ -112,11 +112,14 @@ class Document(models.Model):
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
to_trash = kwargs.pop('to_trash', True) to_trash = kwargs.pop('to_trash', True)
_user = kwargs.pop('_user', True)
if not self.in_trash and to_trash: if not self.in_trash and to_trash:
self.in_trash = True self.in_trash = True
self.deleted_date_time = now() self.deleted_date_time = now()
self.save() with transaction.atomic():
self.save(_commit_events=False)
event_document_trashed.commit(actor=_user, target=self)
else: else:
for version in self.versions.all(): for version in self.versions.all():
version.delete() version.delete()
+47
View File
@@ -268,3 +268,50 @@ class DocumentViewTestMixin(object):
def _request_empty_trash_view(self): def _request_empty_trash_view(self):
return self.post(viewname='documents:trash_can_empty') 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')
+33 -14
View File
@@ -3,13 +3,16 @@ from __future__ import unicode_literals
from actstream.models import Action from actstream.models import Action
from django_downloadview import assert_download_response 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 ( from ..permissions import (
permission_document_download, permission_document_view permission_document_download, permission_document_trash,
permission_document_view
) )
from .base import GenericDocumentViewTestCase from .base import GenericDocumentViewTestCase
from .mixins import TrashedDocumentViewTestMixin
TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label' TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label'
TEST_DOCUMENT_TYPE_2_LABEL = 'test document type 2 label' TEST_DOCUMENT_TYPE_2_LABEL = 'test document type 2 label'
@@ -32,21 +35,22 @@ class DocumentEventsTestMixin(object):
class DocumentEventsTestCase( 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() Action.objects.all().delete()
def test_document_download_event_no_permission(self):
response = self._request_test_document_download_view() response = self._request_test_document_download_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
self.assertEqual(list(Action.objects.any(obj=self.test_document)), []) 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',) self.expected_content_types = ('image/png; charset=utf-8',)
Action.objects.all().delete()
self.grant_access( self.grant_access(
obj=self.test_document, permission=permission_document_download obj=self.test_document, permission=permission_document_download
) )
@@ -66,17 +70,13 @@ class DocumentEventsTestCase(
self.assertEqual(event.target, self.test_document) self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_download.id) self.assertEqual(event.verb, event_document_download.id)
def test_document_view_event_no_permissions(self): def test_document_view_event_no_permission(self):
Action.objects.all().delete()
response = self._request_test_document_preview_view() response = self._request_test_document_preview_view()
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
self.assertEqual(list(Action.objects.any(obj=self.test_document)), []) self.assertEqual(list(Action.objects.any(obj=self.test_document)), [])
def test_document_view_event_with_permissions(self): def test_document_view_event_with_access(self):
Action.objects.all().delete()
self.grant_access( self.grant_access(
obj=self.test_document, permission=permission_document_view 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.actor, self._test_case_user)
self.assertEqual(event.target, self.test_document) self.assertEqual(event.target, self.test_document)
self.assertEqual(event.verb, event_document_view.id) 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)
@@ -7,57 +7,11 @@ from ..permissions import (
) )
from .base import GenericDocumentViewTestCase from .base import GenericDocumentViewTestCase
from .mixins import TrashedDocumentViewTestMixin
class TrashedDocumentTestMixin(object): class TrashedDocumentViewTestCase(
def _request_document_trash_get_view(self): TrashedDocumentViewTestMixin, GenericDocumentViewTestCase
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
): ):
def test_document_trash_get_view_no_permissions(self): def test_document_trash_get_view_no_permissions(self):
document_count = Document.objects.count() document_count = Document.objects.count()
@@ -56,7 +56,7 @@ class DocumentTrashView(MultipleObjectConfirmActionView):
return result return result
def object_action(self, form, instance): def object_action(self, form, instance):
instance.delete() instance.delete(_user=self.request.user)
class EmptyTrashCanView(ConfirmView): class EmptyTrashCanView(ConfirmView):