diff --git a/HISTORY.rst b/HISTORY.rst index ef1c925c59..a993e20abb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -187,6 +187,9 @@ * Add test case mixin that produces ephimeral models. * Update ACL permissions view to use the new AddRemoveView class. * Add ACL created and edited events. +* Update index document types view to use the new AddRemoveView + class. +* Add index create and edit events. 3.1.11 (2019-04-XX) =================== diff --git a/docs/releases/3.2.rst b/docs/releases/3.2.rst index 57eef03f53..83e70b564f 100644 --- a/docs/releases/3.2.rst +++ b/docs/releases/3.2.rst @@ -219,6 +219,9 @@ Other changes * Add test case mixin that produces ephimeral models. * Update ACL permissions view to use the new AddRemoveView class. * Add ACL created and edited events. +* Update index document types view to use the new AddRemoveView + class. +* Add index create and edit events. Removals -------- diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py index f7f61f7326..7cfc912c3d 100644 --- a/mayan/apps/document_indexing/apps.py +++ b/mayan/apps/document_indexing/apps.py @@ -16,9 +16,14 @@ from mayan.apps.common.menus import ( menu_setup, menu_tools ) from mayan.apps.documents.signals import post_document_created, post_initial_document_type +from mayan.apps.events.classes import ModelEventType +from mayan.apps.events.links import ( + link_events_for_object, link_object_event_types_user_subcriptions_list +) from mayan.apps.navigation.classes import SourceColumn from mayan.celery import app +from .events import event_index_template_created, event_index_template_edited from .handlers import ( handler_create_default_document_index, handler_delete_empty, handler_index_document, handler_remove_document, @@ -55,6 +60,7 @@ class DocumentIndexingApp(MayanAppConfig): def ready(self): super(DocumentIndexingApp, self).ready() + from actstream import registry Document = apps.get_model( app_label='documents', model_name='Document' @@ -73,6 +79,12 @@ class DocumentIndexingApp(MayanAppConfig): IndexInstanceNode = self.get_model(model_name='IndexInstanceNode') IndexTemplateNode = self.get_model(model_name='IndexTemplateNode') + ModelEventType.register( + event_types=( + event_index_template_created, event_index_template_edited + ), model=Index + ) + ModelPermission.register( model=Index, permissions=( permission_acl_edit, permission_acl_view, @@ -182,8 +194,10 @@ class DocumentIndexingApp(MayanAppConfig): ) menu_list_facet.bind_links( links=( - link_acl_list, link_index_template_document_types, + link_acl_list, link_events_for_object, + link_index_template_document_types, link_index_template_node_tree_view, + link_object_event_types_user_subcriptions_list ), sources=(Index,) ) menu_object.bind_links( @@ -233,3 +247,5 @@ class DocumentIndexingApp(MayanAppConfig): receiver=handler_remove_document, sender=Document ) + + registry.register(Index) diff --git a/mayan/apps/document_indexing/events.py b/mayan/apps/document_indexing/events.py new file mode 100644 index 0000000000..f96fac5770 --- /dev/null +++ b/mayan/apps/document_indexing/events.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import, unicode_literals + +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.events.classes import EventTypeNamespace + +namespace = EventTypeNamespace( + label=_('Document indexing'), name='document_indexing' +) + +event_index_template_created = namespace.add_event_type( + label=_('Index created'), name='index_created' +) +event_index_template_edited = namespace.add_event_type( + label=_('Index edited'), name='index_edited' +) diff --git a/mayan/apps/document_indexing/models.py b/mayan/apps/document_indexing/models.py index 81d5bbc97a..addcf7ff7e 100644 --- a/mayan/apps/document_indexing/models.py +++ b/mayan/apps/document_indexing/models.py @@ -17,6 +17,7 @@ from mayan.apps.documents.permissions import permission_document_view from mayan.apps.lock_manager.exceptions import LockError from mayan.apps.lock_manager.runtime import locking_backend +from .events import event_index_template_created, event_index_template_edited from .managers import ( DocumentIndexInstanceNodeManager, IndexManager, IndexInstanceNodeManager ) @@ -60,6 +61,20 @@ class Index(models.Model): def __str__(self): return self.label + def document_types_add(self, queryset, _user=None): + with transaction.atomic(): + event_index_template_edited.commit( + actor=_user, target=self + ) + self.document_types.add(*queryset) + + def document_types_remove(self, queryset, _user=None): + with transaction.atomic(): + event_index_template_edited.commit( + actor=_user, target=self + ) + self.document_types.remove(*queryset) + def get_absolute_url(self): try: return reverse( @@ -131,11 +146,22 @@ class Index(models.Model): self.index_document(document=document) def save(self, *args, **kwargs): - """ - Automatically create the root index template node - """ - super(Index, self).save(*args, **kwargs) - IndexTemplateNode.objects.get_or_create(parent=None, index=self) + _user = kwargs.pop('_user', None) + + with transaction.atomic(): + is_new = not self.pk + super(Index, self).save(*args, **kwargs) + if is_new: + # Automatically create the root index template node + IndexTemplateNode.objects.get_or_create(parent=None, index=self) + + event_index_template_created.commit( + actor=_user, target=self + ) + else: + event_index_template_edited.commit( + actor=_user, target=self + ) @property def template_root(self): diff --git a/mayan/apps/document_indexing/tests/mixins.py b/mayan/apps/document_indexing/tests/mixins.py index a39f1ea2f6..a13b2fa2f4 100644 --- a/mayan/apps/document_indexing/tests/mixins.py +++ b/mayan/apps/document_indexing/tests/mixins.py @@ -2,10 +2,12 @@ from __future__ import unicode_literals from ..models import Index -from .literals import TEST_INDEX_LABEL +from .literals import ( + TEST_INDEX_LABEL, TEST_INDEX_LABEL_EDITED, TEST_INDEX_SLUG +) -class DocumentIndexingTestMixin(object): +class IndexTestMixin(object): def _create_test_index(self, rebuild=False): # Create empty index self.test_index = Index.objects.create(label=TEST_INDEX_LABEL) @@ -16,3 +18,35 @@ class DocumentIndexingTestMixin(object): # Rebuild indexes if rebuild: Index.objects.rebuild() + + +class IndexViewTestMixin(object): + def _request_test_index_create_view(self): + # Typecast to list to force queryset evaluation + values = list(Index.objects.values_list('pk', flat=True)) + + response = self.post( + viewname='indexing:index_setup_create', data={ + 'label': TEST_INDEX_LABEL, 'slug': TEST_INDEX_SLUG + } + ) + + self.test_index = Index.objects.exclude(pk__in=values).first() + + return response + + def _request_test_index_delete_view(self): + return self.post( + viewname='indexing:index_setup_delete', kwargs={ + 'pk': self.test_index.pk + } + ) + + def _request_test_index_edit_view(self): + return self.post( + viewname='indexing:index_setup_edit', kwargs={ + 'pk': self.test_index.pk + }, data={ + 'label': TEST_INDEX_LABEL_EDITED, 'slug': TEST_INDEX_SLUG + } + ) diff --git a/mayan/apps/document_indexing/tests/test_api.py b/mayan/apps/document_indexing/tests/test_api.py index c4d4965db2..26ea496125 100644 --- a/mayan/apps/document_indexing/tests/test_api.py +++ b/mayan/apps/document_indexing/tests/test_api.py @@ -12,10 +12,10 @@ from ..permissions import ( ) from .literals import TEST_INDEX_LABEL, TEST_INDEX_SLUG -from .mixins import DocumentIndexingTestMixin +from .mixins import IndexTestMixin -class DocumentIndexingAPITestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseAPITestCase): +class DocumentIndexingAPITestCase(IndexTestMixin, DocumentTestMixin, BaseAPITestCase): auto_upload_document = False def _request_index_create_api_view(self): diff --git a/mayan/apps/document_indexing/tests/test_events.py b/mayan/apps/document_indexing/tests/test_events.py new file mode 100644 index 0000000000..a57572c60d --- /dev/null +++ b/mayan/apps/document_indexing/tests/test_events.py @@ -0,0 +1,47 @@ +from __future__ import unicode_literals + +from actstream.models import Action + +from mayan.apps.common.tests import GenericViewTestCase +from mayan.apps.documents.tests import DocumentTestMixin + +from ..permissions import ( + permission_document_indexing_create, permission_document_indexing_edit, +) + +from ..events import event_index_template_created, event_index_template_edited + +from .mixins import IndexTestMixin, IndexViewTestMixin + + +class IndexTemplateEventsTestCase(DocumentTestMixin, IndexTestMixin, IndexViewTestMixin, GenericViewTestCase): + auto_upload_document = False + + def test_index_template_create_event(self): + Action.objects.all().delete() + + self.grant_permission( + permission=permission_document_indexing_create + ) + self._request_test_index_create_view() + + self.assertEqual(Action.objects.last().actor, self._test_case_user) + self.assertEqual(Action.objects.last().target, self.test_index) + self.assertEqual( + Action.objects.last().verb, event_index_template_created.id + ) + + def test_index_template_edit_event(self): + self._create_test_index() + + self.grant_access( + obj=self.test_index, permission=permission_document_indexing_edit + ) + Action.objects.all().delete() + + self._request_test_index_edit_view() + + self.assertEqual(Action.objects.last().target, self.test_index) + self.assertEqual( + Action.objects.last().verb, event_index_template_edited.id + ) diff --git a/mayan/apps/document_indexing/tests/test_models.py b/mayan/apps/document_indexing/tests/test_models.py index dbcd8fabaf..dba5017433 100644 --- a/mayan/apps/document_indexing/tests/test_models.py +++ b/mayan/apps/document_indexing/tests/test_models.py @@ -20,10 +20,10 @@ from .literals import ( TEST_INDEX_TEMPLATE_METADATA_EXPRESSION, TEST_METADATA_TYPE_LABEL, TEST_METADATA_TYPE_NAME ) -from .mixins import DocumentIndexingTestMixin +from .mixins import IndexTestMixin -class IndexTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase): +class IndexTestCase(IndexTestMixin, DocumentTestMixin, BaseTestCase): def test_document_description_index(self): self._create_test_index() diff --git a/mayan/apps/document_indexing/tests/test_views.py b/mayan/apps/document_indexing/tests/test_views.py index 19d310633a..31fa4af01d 100644 --- a/mayan/apps/document_indexing/tests/test_views.py +++ b/mayan/apps/document_indexing/tests/test_views.py @@ -10,22 +10,13 @@ from ..permissions import ( permission_document_indexing_rebuild ) -from .literals import ( - TEST_INDEX_LABEL, TEST_INDEX_LABEL_EDITED, TEST_INDEX_SLUG -) -from .mixins import DocumentIndexingTestMixin +from .literals import TEST_INDEX_LABEL, TEST_INDEX_LABEL_EDITED +from .mixins import IndexTestMixin, IndexViewTestMixin -class IndexViewTestCase(DocumentIndexingTestMixin, GenericDocumentViewTestCase): - def _request_index_create_view(self): - return self.post( - viewname='indexing:index_setup_create', data={ - 'label': TEST_INDEX_LABEL, 'slug': TEST_INDEX_SLUG - } - ) - +class IndexViewTestCase(IndexTestMixin, IndexViewTestMixin, GenericDocumentViewTestCase): def test_index_create_view_no_permission(self): - response = self._request_index_create_view() + response = self._request_test_index_create_view() self.assertEquals(response.status_code, 403) self.assertEqual(Index.objects.count(), 0) @@ -35,23 +26,16 @@ class IndexViewTestCase(DocumentIndexingTestMixin, GenericDocumentViewTestCase): permission=permission_document_indexing_create ) - response = self._request_index_create_view() + response = self._request_test_index_create_view() self.assertEqual(response.status_code, 302) self.assertEqual(Index.objects.count(), 1) self.assertEqual(Index.objects.first().label, TEST_INDEX_LABEL) - def _request_index_delete_view(self): - return self.post( - viewname='indexing:index_setup_delete', kwargs={ - 'pk': self.test_index.pk - } - ) - def test_index_delete_view_no_permission(self): self._create_test_index() - response = self._request_index_delete_view() + response = self._request_test_index_delete_view() self.assertEqual(response.status_code, 403) self.assertEqual(Index.objects.count(), 1) @@ -61,24 +45,15 @@ class IndexViewTestCase(DocumentIndexingTestMixin, GenericDocumentViewTestCase): self.grant_permission(permission=permission_document_indexing_delete) - response = self._request_index_delete_view() + response = self._request_test_index_delete_view() self.assertEqual(response.status_code, 302) self.assertEqual(Index.objects.count(), 0) - def _request_index_edit_view(self): - return self.post( - viewname='indexing:index_setup_edit', kwargs={ - 'pk': self.test_index.pk - }, data={ - 'label': TEST_INDEX_LABEL_EDITED, 'slug': TEST_INDEX_SLUG - } - ) - def test_index_edit_view_no_permission(self): self._create_test_index() - response = self._request_index_edit_view() + response = self._request_test_index_edit_view() self.assertEqual(response.status_code, 403) self.test_index.refresh_from_db() @@ -91,7 +66,7 @@ class IndexViewTestCase(DocumentIndexingTestMixin, GenericDocumentViewTestCase): obj=self.test_index, permission=permission_document_indexing_edit ) - response = self._request_index_edit_view() + response = self._request_test_index_edit_view() self.assertEqual(response.status_code, 302) self.test_index.refresh_from_db() diff --git a/mayan/apps/document_indexing/views.py b/mayan/apps/document_indexing/views.py index ab185879a7..847f0054c5 100644 --- a/mayan/apps/document_indexing/views.py +++ b/mayan/apps/document_indexing/views.py @@ -9,11 +9,13 @@ from django.utils.translation import ugettext_lazy as _, ungettext from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( - AssignRemoveView, FormView, SingleObjectCreateView, - SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView + AddRemoveView, FormView, SingleObjectCreateView, SingleObjectDeleteView, + SingleObjectEditView, SingleObjectListView ) from mayan.apps.documents.models import Document, DocumentType -from mayan.apps.documents.permissions import permission_document_view +from mayan.apps.documents.permissions import ( + permission_document_type_edit, permission_document_view +) from mayan.apps.documents.views import DocumentListView from .forms import IndexTemplateFilteredForm, IndexTemplateNodeForm @@ -43,6 +45,9 @@ class SetupIndexCreateView(SingleObjectCreateView): ) view_permission = permission_document_indexing_create + def get_save_extra_data(self): + return {'_user': self.request.user} + class SetupIndexDeleteView(SingleObjectDeleteView): model = Index @@ -72,6 +77,9 @@ class SetupIndexEditView(SingleObjectEditView): 'title': _('Edit index: %s') % self.get_object(), } + def get_save_extra_data(self): + return {'_user': self.request.user} + class SetupIndexListView(SingleObjectListView): model = Index @@ -95,53 +103,34 @@ class SetupIndexListView(SingleObjectListView): } -class SetupIndexDocumentTypesView(AssignRemoveView): - decode_content_type = True - left_list_title = _('Available document types') - object_permission = permission_document_indexing_edit - right_list_title = _('Document types linked') +class SetupIndexDocumentTypesView(AddRemoveView): + action_add_method = 'document_types_add' + action_remove_method = 'document_types_remove' + main_object_permission = permission_document_indexing_edit + main_object_model = Index + main_object_pk_url_kwarg = 'pk' + secondary_object_model = DocumentType + secondary_object_permission = permission_document_type_edit + list_available_title = _('Available document types') + # Translators: "User groups" here refer to the group list of a specific + # user. + list_added_title = _('Document types linked') + related_field = 'document_types' - def add(self, item): - self.get_object().document_types.add(item) - - def get_document_queryset(self): - return AccessControlList.objects.filter_by_access( - permission=permission_document_view, - queryset=DocumentType.objects.all(), - user=self.request.user - ) + def get_actions_extra_kwargs(self): + return {'_user': self.request.user} def get_extra_context(self): return { - 'object': self.get_object(), - 'title': _( - 'Document types linked to index: %s' - ) % self.get_object(), + 'object': self.main_object, 'subtitle': _( 'Only the documents of the types selected will be shown ' 'in the index when built. Only the events of the documents ' 'of the types select will trigger updates in the index.' ), + 'title': _('Document types linked to index: %s') % self.main_object, } - def get_object(self): - return get_object_or_404(klass=Index, pk=self.kwargs['pk']) - - def left_list(self): - return AssignRemoveView.generate_choices( - self.get_document_queryset().exclude( - id__in=self.get_object().document_types.all() - ) - ) - - def remove(self, item): - self.get_object().document_types.remove(item) - - def right_list(self): - return AssignRemoveView.generate_choices( - self.get_document_queryset() & self.get_object().document_types.all() - ) - class SetupIndexTreeTemplateListView(SingleObjectListView): object_permission = permission_document_indexing_edit diff --git a/mayan/apps/mirroring/tests/test_filesystems.py b/mayan/apps/mirroring/tests/test_filesystems.py index aa687f977b..003c9b1949 100644 --- a/mayan/apps/mirroring/tests/test_filesystems.py +++ b/mayan/apps/mirroring/tests/test_filesystems.py @@ -7,7 +7,7 @@ from fuse import FuseOSError from mayan.apps.common.tests import BaseTestCase from mayan.apps.documents.models import Document from mayan.apps.documents.tests import DocumentTestMixin -from mayan.apps.document_indexing.tests import DocumentIndexingTestMixin +from mayan.apps.document_indexing.tests import IndexTestMixin from ..filesystems import IndexFilesystem @@ -18,7 +18,7 @@ from .literals import ( ) -class IndexFilesystemTestCase(DocumentIndexingTestMixin, DocumentTestMixin, BaseTestCase): +class IndexFilesystemTestCase(IndexTestMixin, DocumentTestMixin, BaseTestCase): auto_upload_document = False def test_document_access(self):