diff --git a/HISTORY.rst b/HISTORY.rst index f4801e18da..a9e7ff83a2 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -235,6 +235,11 @@ workflow proxy instance menu list. * Fix translation of the source upload forms using dropzone.js +* Rename get_object_list to get_source_queryset. +* Add uniqueness validation to SingleObjectCreateView. +* Remove MultipleInstanceActionMixin. +* Backport MultipleObjectMixin improvements. +* Remove ObjectListPermissionFilterMixin. 3.1.11 (2019-04-XX) =================== diff --git a/docs/releases/3.2.rst b/docs/releases/3.2.rst index 297e8d24e3..37740c5dc7 100644 --- a/docs/releases/3.2.rst +++ b/docs/releases/3.2.rst @@ -268,6 +268,11 @@ Other changes workflow proxy instance menu list. * Fix translation of the source upload forms using dropzone.js +* Rename get_object_list to get_source_queryset. +* Add uniqueness validation to SingleObjectCreateView. +* Remove MultipleInstanceActionMixin. +* Backport MultipleObjectMixin improvements. +* Remove ObjectListPermissionFilterMixin. Removals -------- diff --git a/mayan/apps/acls/managers.py b/mayan/apps/acls/managers.py index f70997d9b9..7cd148cbaf 100644 --- a/mayan/apps/acls/managers.py +++ b/mayan/apps/acls/managers.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.db import models -from django.db.models import CharField, Value as V, Q +from django.db.models import CharField, Value, Q from django.db.models.functions import Concat from django.utils.encoding import force_text from django.utils.translation import ugettext @@ -60,14 +60,15 @@ class AccessControlListManager(models.Manager): # id combinations content_type_object_id_queryset = queryset.annotate( ct_fk_combination=Concat( - related_field.ct_field, V('-'), related_field.fk_field, - output_field=CharField() + related_field.ct_field, Value('-'), + related_field.fk_field, output_field=CharField() ) ).values('ct_fk_combination') acl_filter = self.annotate( ct_fk_combination=Concat( - 'content_type', V('-'), 'object_id', output_field=CharField() + 'content_type', Value('-'), 'object_id', + output_field=CharField() ) ).filter( permissions=stored_permission, role__groups__user=user, diff --git a/mayan/apps/acls/views.py b/mayan/apps/acls/views.py index 45bd0c5cb5..29db693398 100644 --- a/mayan/apps/acls/views.py +++ b/mayan/apps/acls/views.py @@ -154,7 +154,7 @@ class ACLListView(SingleObjectListView): 'title': _('Access control lists for: %s' % self.content_object), } - def get_object_list(self): + def get_source_queryset(self): return AccessControlList.objects.filter( content_type=self.object_content_type, object_id=self.content_object.pk diff --git a/mayan/apps/cabinets/tests/test_views.py b/mayan/apps/cabinets/tests/test_views.py index 77834f3312..3e8ad35871 100644 --- a/mayan/apps/cabinets/tests/test_views.py +++ b/mayan/apps/cabinets/tests/test_views.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.common.tests import GenericViewTestCase +from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.tests import GenericDocumentViewTestCase from ..models import Cabinet @@ -47,7 +48,7 @@ class CabinetViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericViewTes self._create_test_cabinet() response = self._request_test_cabinet_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Cabinet.objects.count(), 1) @@ -66,7 +67,7 @@ class CabinetViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericViewTes self._create_test_cabinet() response = self._request_test_cabinet_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.label, TEST_CABINET_LABEL) @@ -84,6 +85,25 @@ class CabinetViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericViewTes self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.label, TEST_CABINET_LABEL_EDITED) + def test_cabinet_list_view_no_permission(self): + self._create_test_cabinet() + + response = self._request_test_cabinet_list_view() + self.assertNotContains( + response, text=self.test_cabinet.label, status_code=200 + ) + + def test_cabinet_list_view_with_access(self): + self._create_test_cabinet() + self.grant_access( + obj=self.test_cabinet, permission=permission_cabinet_view + ) + + response = self._request_test_cabinet_list_view() + self.assertContains( + response, text=self.test_cabinet.label, status_code=200 + ) + class CabinetChildViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericViewTestCase): def setUp(self): @@ -94,7 +114,7 @@ class CabinetChildViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericVi cabinet_count = Cabinet.objects.count() response = self._request_test_cabinet_child_create_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Cabinet.objects.count(), cabinet_count) @@ -115,7 +135,7 @@ class CabinetChildViewTestCase(CabinetTestMixin, CabinetViewTestMixin, GenericVi cabinet_count = Cabinet.objects.count() response = self._request_test_cabinet_child_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Cabinet.objects.count(), cabinet_count) @@ -149,9 +169,7 @@ class CabinetDocumentViewTestCase(CabinetTestMixin, CabinetViewTestMixin, Generi self.grant_permission(permission=permission_cabinet_view) response = self._add_document_to_cabinet() - self.assertContains( - response=response, text='Select a valid choice.', status_code=200 - ) + self.assertEqual(response.status_code, 404) self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.documents.count(), 0) @@ -184,9 +202,7 @@ class CabinetDocumentViewTestCase(CabinetTestMixin, CabinetViewTestMixin, Generi self.grant_permission(permission=permission_cabinet_view) response = self._request_test_document_multiple_cabinet_multiple_add_view_cabinet() - self.assertContains( - response=response, text='Select a valid choice', status_code=200 - ) + self.assertEqual(response.status_code, 404) self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.documents.count(), 0) @@ -216,9 +232,7 @@ class CabinetDocumentViewTestCase(CabinetTestMixin, CabinetViewTestMixin, Generi self.test_cabinet.documents.add(self.test_document) response = self._request_test_document_cabinet_multiple_remove_view() - self.assertContains( - response=response, text='Select a valid choice', status_code=200 - ) + self.assertEqual(response.status_code, 404) self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.documents.count(), 1) @@ -243,21 +257,72 @@ class CabinetDocumentViewTestCase(CabinetTestMixin, CabinetViewTestMixin, Generi self.test_cabinet.refresh_from_db() self.assertEqual(self.test_cabinet.documents.count(), 0) - def test_cabinet_list_view_no_permission(self): - self._create_test_cabinet() - - response = self._request_test_cabinet_list_view() - self.assertNotContains( - response, text=self.test_cabinet.label, status_code=200 + def _request_test_cabinet_document_list_view(self): + return self.get( + viewname='cabinets:cabinet_view', kwargs={ + 'pk': self.test_cabinet.pk + } ) - def test_cabinet_list_view_with_access(self): + def test_cabinet_document_list_view_no_permission(self): self._create_test_cabinet() + self.test_cabinet.documents.add(self.test_document) + + response = self._request_test_cabinet_document_list_view() + self.assertNotContains( + response, text=self.test_cabinet.label, status_code=404 + ) + self.assertNotContains( + response, text=self.test_document.label, status_code=404 + ) + + def test_cabinet_document_list_view_with_cabinet_access(self): + self._create_test_cabinet() + self.test_cabinet.documents.add(self.test_document) + self.grant_access( obj=self.test_cabinet, permission=permission_cabinet_view ) - response = self._request_test_cabinet_list_view() + response = self._request_test_cabinet_document_list_view() self.assertContains( response, text=self.test_cabinet.label, status_code=200 ) + self.assertNotContains( + response, text=self.test_document.label, status_code=200 + ) + + def test_cabinet_document_list_view_with_document_access(self): + self._create_test_cabinet() + self.test_cabinet.documents.add(self.test_document) + + self.grant_access( + obj=self.test_document, permission=permission_document_view + ) + + response = self._request_test_cabinet_document_list_view() + self.assertNotContains( + response, text=self.test_cabinet.label, status_code=404 + ) + self.assertNotContains( + response, text=self.test_document.label, status_code=404 + ) + + def test_cabinet_document_list_view_with_full_access(self): + self._create_test_cabinet() + self.test_cabinet.documents.add(self.test_document) + + self.grant_access( + obj=self.test_cabinet, permission=permission_cabinet_view + ) + self.grant_access( + obj=self.test_document, permission=permission_document_view + ) + + response = self._request_test_cabinet_document_list_view() + self.assertContains( + response, text=self.test_cabinet.label, status_code=200 + ) + self.assertContains( + response, text=self.test_document.label, status_code=200 + ) diff --git a/mayan/apps/cabinets/views.py b/mayan/apps/cabinets/views.py index b4764fa36e..d3225d643f 100644 --- a/mayan/apps/cabinets/views.py +++ b/mayan/apps/cabinets/views.py @@ -13,6 +13,7 @@ from mayan.apps.common.generics import ( MultipleObjectFormActionView, SingleObjectCreateView, SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.models import Document from mayan.apps.documents.views import DocumentListView @@ -45,38 +46,25 @@ class CabinetCreateView(SingleObjectCreateView): } -class CabinetChildAddView(SingleObjectCreateView): +class CabinetChildAddView(ExternalObjectMixin, SingleObjectCreateView): fields = ('label',) - model = Cabinet - - def form_valid(self, form): - """ - If the form is valid, save the associated model. - """ - self.object = form.save(commit=False) - self.object.parent = self.get_object() - self.object.save() - - return super(CabinetChildAddView, self).form_valid(form=form) - - def get_object(self, *args, **kwargs): - cabinet = super(CabinetChildAddView, self).get_object(*args, **kwargs) - - AccessControlList.objects.check_access( - obj=cabinet.get_root(), permissions=(permission_cabinet_edit,), - user=self.request.user - ) - - return cabinet + external_object_class = Cabinet + external_object_permission = permission_cabinet_edit def get_extra_context(self): return { 'title': _( 'Add new level to: %s' - ) % self.get_object().get_full_path(), - 'object': self.get_object() + ) % self.external_object.get_full_path(), + 'object': self.external_object } + def get_queryset(self): + return self.external_object.get_descendants() + + def get_save_extra_data(self): + return {'parent': self.external_object} + class CabinetDeleteView(SingleObjectDeleteView): model = Cabinet @@ -90,34 +78,34 @@ class CabinetDeleteView(SingleObjectDeleteView): } -class CabinetDetailView(DocumentListView): +class CabinetDetailView(ExternalObjectMixin, DocumentListView): + external_object_class = Cabinet + external_object_permission = permission_cabinet_view template_name = 'cabinets/cabinet_details.html' def get_document_queryset(self): - queryset = AccessControlList.objects.filter_by_access( - permission=permission_document_view, - queryset=self.get_object().documents.all(), user=self.request.user - ) - - return queryset + return self.external_object.documents.all() def get_context_data(self, **kwargs): context = super(CabinetDetailView, self).get_context_data(**kwargs) - cabinet = self.get_object() - context.update( { 'column_class': 'col-xs-12 col-sm-6 col-md-4 col-lg-3', 'hide_links': True, 'jstree_data': '\n'.join( - jstree_data(node=cabinet.get_root(), selected_node=cabinet) + jstree_data( + node=self.external_object.get_root(), + selected_node=self.external_object + ) ), 'list_as_items': True, 'no_results_icon': icon_cabinet, 'no_results_main_link': link_cabinet_child_add.resolve( context=RequestContext( - request=self.request, dict_={'object': cabinet} + request=self.request, dict_={ + 'object': self.external_object + } ) ), 'no_results_text': _( @@ -126,28 +114,15 @@ class CabinetDetailView(DocumentListView): 'select the cabinet view of a document view.' ), 'no_results_title': _('This cabinet level is empty'), - 'object': cabinet, - 'title': _('Details of cabinet: %s') % cabinet.get_full_path(), + 'object': self.external_object, + 'title': _( + 'Details of cabinet: %s' + ) % self.external_object.get_full_path(), } ) return context - def get_object(self): - cabinet = get_object_or_404(klass=Cabinet, pk=self.kwargs['pk']) - - if cabinet.is_root_node(): - permission_object = cabinet - else: - permission_object = cabinet.get_root() - - AccessControlList.objects.check_access( - obj=permission_object, permissions=(permission_cabinet_view,), - user=self.request.user - ) - - return cabinet - class CabinetEditView(SingleObjectEditView): fields = ('label',) @@ -182,7 +157,7 @@ class CabinetListView(SingleObjectListView): 'no_results_title': _('No cabinets available'), } - def get_object_list(self): + def get_source_queryset(self): # Add explicit ordering of root nodes since the queryset returned # is not affected by the model's order Meta option. return Cabinet.objects.root_nodes().order_by('label') @@ -220,7 +195,7 @@ class DocumentCabinetListView(CabinetListView): 'title': _('Cabinets containing document: %s') % self.document, } - def get_object_list(self): + def get_source_queryset(self): return self.document.document_cabinets() diff --git a/mayan/apps/checkouts/tests/test_views.py b/mayan/apps/checkouts/tests/test_views.py index 500d897eec..23d4ca8aa7 100644 --- a/mayan/apps/checkouts/tests/test_views.py +++ b/mayan/apps/checkouts/tests/test_views.py @@ -119,14 +119,11 @@ class DocumentCheckoutViewTestCase(DocumentCheckoutTestMixin, GenericDocumentVie def test_checkout_detail_view_no_permission(self): self._check_out_test_document() - self.grant_access( - obj=self.test_document, - permission=permission_document_check_out - ) response = self._request_check_out_detail_view() + self.assertNotContains( - response, text=STATE_LABELS[STATE_CHECKED_OUT], status_code=403 + response, text=STATE_LABELS[STATE_CHECKED_OUT], status_code=404 ) def test_checkout_detail_view_with_access(self): diff --git a/mayan/apps/checkouts/views.py b/mayan/apps/checkouts/views.py index 9d797dd0f9..0aa249663f 100644 --- a/mayan/apps/checkouts/views.py +++ b/mayan/apps/checkouts/views.py @@ -24,6 +24,64 @@ from .permissions import ( ) +class DocumentCheckinView(ConfirmView): + def get_extra_context(self): + document = self.get_object() + + context = { + 'object': document, + } + + if document.get_check_out_info().user != self.request.user: + context['title'] = _( + 'You didn\'t originally checked out this document. ' + 'Forcefully check in the document: %s?' + ) % document + else: + context['title'] = _('Check in the document: %s?') % document + + return context + + def get_object(self): + return get_object_or_404(klass=Document, pk=self.kwargs['pk']) + + def get_post_action_redirect(self): + return reverse( + viewname='checkouts:check_out_info', kwargs={ + 'pk': self.get_object().pk + } + ) + + def view_action(self): + document = self.get_object() + + if document.get_check_out_info().user == self.request.user: + AccessControlList.objects.check_access( + obj=document, permissions=(permission_document_check_in,), + user=self.request.user + ) + else: + AccessControlList.objects.check_access( + obj=document, + permissions=(permission_document_check_in_override,), + user=self.request.user + ) + + try: + document.check_in(user=self.request.user) + except DocumentNotCheckedOut: + messages.error( + message=_('Document has not been checked out.'), + request=self.request + ) + else: + messages.success( + message=_( + 'Document "%s" checked in successfully.' + ) % document, request=self.request + ) + + class CheckoutDocumentView(SingleObjectCreateView): form_class = DocumentCheckoutForm @@ -125,69 +183,8 @@ class CheckoutDetailView(SingleObjectDetailView): def get_extra_context(self): return { - 'object': self.get_object(), + 'object': self.object, 'title': _( 'Check out details for document: %s' - ) % self.get_object() + ) % self.object } - - def get_object(self): - return get_object_or_404(klass=Document, pk=self.kwargs['pk']) - - -class DocumentCheckinView(ConfirmView): - def get_extra_context(self): - document = self.get_object() - - context = { - 'object': document, - } - - if document.get_check_out_info().user != self.request.user: - context['title'] = _( - 'You didn\'t originally checked out this document. ' - 'Forcefully check in the document: %s?' - ) % document - else: - context['title'] = _('Check in the document: %s?') % document - - return context - - def get_object(self): - return get_object_or_404(klass=Document, pk=self.kwargs['pk']) - - def get_post_action_redirect(self): - return reverse( - viewname='checkouts:check_out_info', kwargs={ - 'pk': self.get_object().pk - } - ) - - def view_action(self): - document = self.get_object() - - if document.get_check_out_info().user == self.request.user: - AccessControlList.objects.check_access( - obj=document, permissions=(permission_document_check_in,), - user=self.request.user - ) - else: - AccessControlList.objects.check_access( - obj=document, - permissions=(permission_document_check_in_override,), - user=self.request.user - ) - - try: - document.check_in(user=self.request.user) - except DocumentNotCheckedOut: - messages.error( - message=_('Document has not been checked out.'), - request=self.request - ) - else: - messages.success( - message=_( - 'Document "%s" checked in successfully.' - ) % document, request=self.request - ) diff --git a/mayan/apps/common/generics.py b/mayan/apps/common/generics.py index 0988e6e339..b26219f04d 100644 --- a/mayan/apps/common/generics.py +++ b/mayan/apps/common/generics.py @@ -1,7 +1,7 @@ from __future__ import absolute_import, unicode_literals from django.contrib import messages -from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import ImproperlyConfigured, ValidationError from django.db import transaction from django.http import HttpResponseRedirect from django.urls import reverse @@ -36,7 +36,7 @@ from .literals import ( from .mixins import ( DeleteExtraDataMixin, DynamicFormViewMixin, ExternalObjectMixin, ExtraContextMixin, FormExtraKwargsMixin, MultipleObjectMixin, - ObjectActionMixin, ObjectListPermissionFilterMixin, ObjectNameMixin, + ObjectActionMixin, ObjectNameMixin, ObjectPermissionCheckMixin, RedirectionMixin, RestrictedQuerysetMixin, ViewPermissionCheckMixin ) @@ -138,7 +138,10 @@ class MultiFormView(DjangoFormView): return self.forms_invalid(forms=self.forms) -class AddRemoveView(ExternalObjectMixin, ExtraContextMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, MultiFormView): +class AddRemoveView( + ExternalObjectMixin, ExtraContextMixin, ViewPermissionCheckMixin, + RestrictedQuerysetMixin, MultiFormView +): form_classes = {'form_available': ChoiceForm, 'form_added': ChoiceForm} list_added_help_text = _( 'Select entries to be removed. Hold Control to select multiple ' @@ -384,15 +387,21 @@ class AddRemoveView(ExternalObjectMixin, ExtraContextMixin, ViewPermissionCheckM ) -class ConfirmView(ObjectListPermissionFilterMixin, ObjectPermissionCheckMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView): +class ConfirmView( + RestrictedQuerysetMixin, ViewPermissionCheckMixin, ExtraContextMixin, + RedirectionMixin, TemplateView +): template_name = 'appearance/generic_confirm.html' def post(self, request, *args, **kwargs): self.view_action() - return HttpResponseRedirect(self.get_success_url()) + return HttpResponseRedirect(redirect_to=self.get_success_url()) -class FormView(ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, FormExtraKwargsMixin, DjangoFormView): +class FormView( + ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, + FormExtraKwargsMixin, DjangoFormView +): template_name = 'appearance/generic_form.html' @@ -400,7 +409,11 @@ class DynamicFormView(DynamicFormViewMixin, FormView): pass -class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormExtraKwargsMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): +class MultipleObjectFormActionView( + ExtraContextMixin, ObjectActionMixin, ViewPermissionCheckMixin, + RestrictedQuerysetMixin, MultipleObjectMixin, FormExtraKwargsMixin, + RedirectionMixin, DjangoFormView +): """ This view will present a form and upon receiving a POST request will perform an action on an object or queryset @@ -413,7 +426,7 @@ class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormE if self.__class__.mro()[0].get_queryset != MultipleObjectFormActionView.get_queryset: raise ImproperlyConfigured( '%(cls)s is overloading the get_queryset method. Subclasses ' - 'should implement the get_object_list method instead. ' % { + 'should implement the get_source_queryset method instead. ' % { 'cls': self.__class__.__name__ } ) @@ -428,23 +441,50 @@ class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormE try: return super(MultipleObjectFormActionView, self).get_queryset() except ImproperlyConfigured: - self.queryset = self.get_object_list() + self.queryset = self.get_source_queryset() return super(MultipleObjectFormActionView, self).get_queryset() -class MultipleObjectConfirmActionView(ObjectActionMixin, MultipleObjectMixin, ObjectListPermissionFilterMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView): +class MultipleObjectConfirmActionView( + ExtraContextMixin, ObjectActionMixin, ViewPermissionCheckMixin, + RestrictedQuerysetMixin, MultipleObjectMixin, RedirectionMixin, TemplateView +): template_name = 'appearance/generic_confirm.html' + def __init__(self, *args, **kwargs): + result = super(MultipleObjectConfirmActionView, self).__init__(*args, **kwargs) + + if self.__class__.mro()[0].get_queryset != MultipleObjectConfirmActionView.get_queryset: + raise ImproperlyConfigured( + '%(cls)s is overloading the get_queryset method. Subclasses ' + 'should implement the get_source_queryset method instead. ' % { + 'cls': self.__class__.__name__ + } + ) + + return result + + def get_queryset(self): + try: + return super(MultipleObjectConfirmActionView, self).get_queryset() + except ImproperlyConfigured: + self.queryset = self.get_source_queryset() + return super(MultipleObjectConfirmActionView, self).get_queryset() + def post(self, request, *args, **kwargs): self.view_action() - return HttpResponseRedirect(self.get_success_url()) + return HttpResponseRedirect(redirect_to=self.get_success_url()) class SimpleView(ViewPermissionCheckMixin, ExtraContextMixin, TemplateView): pass -class SingleObjectCreateView(ObjectNameMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, FormExtraKwargsMixin, CreateView): +class SingleObjectCreateView( + ObjectNameMixin, ViewPermissionCheckMixin, ExtraContextMixin, + RedirectionMixin, FormExtraKwargsMixin, CreateView +): + error_message_duplicate = None template_name = 'appearance/generic_form.html' def form_valid(self, form): @@ -461,42 +501,78 @@ class SingleObjectCreateView(ObjectNameMixin, ViewPermissionCheckMixin, ExtraCon else: save_extra_data = {} + try: + self.object.validate_unique() + except ValidationError as exception: + context = self.get_context_data() + + error_message = self.get_error_message_duplicate() or _( + 'Duplicate data error: %(error)s' + ) % { + 'error': '\n'.join(exception.messages) + } + + messages.error( + message=error_message, request=self.request + ) + return super( + SingleObjectCreateView, self + ).form_invalid(form=form) + try: self.object.save(**save_extra_data) except Exception as exception: context = self.get_context_data() messages.error( - self.request, - _('%(object)s not created, error: %(error)s') % { + message=_('%(object)s not created, error: %(error)s') % { 'object': self.get_object_name(context=context), 'error': exception - } + }, request=self.request ) + return super( + SingleObjectCreateView, self + ).form_invalid(form=form) else: context = self.get_context_data() messages.success( - self.request, - _( + message=_( '%(object)s created successfully.' - ) % {'object': self.get_object_name(context=context)} + ) % {'object': self.get_object_name(context=context)}, + request=self.request ) - return HttpResponseRedirect(self.get_success_url()) + return HttpResponseRedirect(redirect_to=self.get_success_url()) + + def get_error_message_duplicate(self): + return self.error_message_duplicate -class SingleObjectDynamicFormCreateView(DynamicFormViewMixin, SingleObjectCreateView): +class SingleObjectDynamicFormCreateView( + DynamicFormViewMixin, SingleObjectCreateView +): pass -class SingleObjectDeleteView(ObjectNameMixin, DeleteExtraDataMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DeleteView): +class SingleObjectDeleteView( + ObjectNameMixin, DeleteExtraDataMixin, ViewPermissionCheckMixin, + RestrictedQuerysetMixin, ExtraContextMixin, RedirectionMixin, DeleteView +): template_name = 'appearance/generic_confirm.html' - def get_context_data(self, **kwargs): - context = super(SingleObjectDeleteView, self).get_context_data(**kwargs) - context.update({'delete_view': True}) - return context + def __init__(self, *args, **kwargs): + result = super(SingleObjectDeleteView, self).__init__(*args, **kwargs) + + if self.__class__.mro()[0].get_queryset != SingleObjectDeleteView.get_queryset: + raise ImproperlyConfigured( + '%(cls)s is overloading the get_queryset method. Subclasses ' + 'should implement the get_source_queryset method instead. ' % { + 'cls': self.__class__.__name__ + } + ) + + return result def delete(self, request, *args, **kwargs): self.object = self.get_object() @@ -507,40 +583,77 @@ class SingleObjectDeleteView(ObjectNameMixin, DeleteExtraDataMixin, ViewPermissi result = super(SingleObjectDeleteView, self).delete(request, *args, **kwargs) except Exception as exception: messages.error( - self.request, - _('%(object)s not deleted, error: %(error)s.') % { + message=_('%(object)s not deleted, error: %(error)s.') % { 'object': object_name, 'error': exception - } + }, request=self.request ) raise exception else: messages.success( - self.request, - _( + message=_( '%(object)s deleted successfully.' - ) % {'object': object_name} + ) % {'object': object_name}, + request=self.request ) return result + def get_context_data(self, **kwargs): + context = super(SingleObjectDeleteView, self).get_context_data(**kwargs) + context.update({'delete_view': True}) + return context -class SingleObjectDetailView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, FormExtraKwargsMixin, ExtraContextMixin, ModelFormMixin, DetailView): + def get_queryset(self): + try: + return super(SingleObjectDeleteView, self).get_queryset() + except ImproperlyConfigured: + self.queryset = self.get_source_queryset() + return super(SingleObjectDeleteView, self).get_queryset() + + +class SingleObjectDetailView( + ViewPermissionCheckMixin, RestrictedQuerysetMixin, FormExtraKwargsMixin, + ExtraContextMixin, ModelFormMixin, DetailView +): template_name = 'appearance/generic_form.html' + def __init__(self, *args, **kwargs): + result = super(SingleObjectDetailView, self).__init__(*args, **kwargs) + + if self.__class__.mro()[0].get_queryset != SingleObjectDetailView.get_queryset: + raise ImproperlyConfigured( + '%(cls)s is overloading the get_queryset method. Subclasses ' + 'should implement the get_source_queryset method instead. ' % { + 'cls': self.__class__.__name__ + } + ) + + return result + def get_context_data(self, **kwargs): context = super(SingleObjectDetailView, self).get_context_data(**kwargs) context.update({'read_only': True, 'form': self.get_form()}) return context + def get_queryset(self): + try: + return super(SingleObjectDetailView, self).get_queryset() + except ImproperlyConfigured: + self.queryset = self.get_source_queryset() + return super(SingleObjectDetailView, self).get_queryset() + class SingleObjectDownloadView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, VirtualDownloadView, SingleObjectMixin): TextIteratorIO = TextIteratorIO VirtualFile = VirtualFile -class SingleObjectEditView(ObjectNameMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, FormExtraKwargsMixin, RedirectionMixin, UpdateView): +class SingleObjectEditView( + ObjectNameMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, + ExtraContextMixin, FormExtraKwargsMixin, RedirectionMixin, UpdateView +): template_name = 'appearance/generic_form.html' def form_valid(self, form): @@ -564,23 +677,22 @@ class SingleObjectEditView(ObjectNameMixin, ViewPermissionCheckMixin, ObjectPerm self.object.save(**save_extra_data) except Exception as exception: messages.error( - self.request, - _('%(object)s not updated, error: %(error)s.') % { + message=_('%(object)s not updated, error: %(error)s.') % { 'object': object_name, 'error': exception - } + }, request=self.request ) - - raise exception + return super( + SingleObjectEditView, self + ).form_invalid(form=form) else: messages.success( - self.request, - _( + message=_( '%(object)s updated successfully.' - ) % {'object': object_name} + ) % {'object': object_name}, request=self.request ) - return HttpResponseRedirect(self.get_success_url()) + return HttpResponseRedirect(redirect_to=self.get_success_url()) def get_object(self, queryset=None): obj = super(SingleObjectEditView, self).get_object(queryset=queryset) @@ -592,11 +704,16 @@ class SingleObjectEditView(ObjectNameMixin, ViewPermissionCheckMixin, ObjectPerm return obj -class SingleObjectDynamicFormEditView(DynamicFormViewMixin, SingleObjectEditView): +class SingleObjectDynamicFormEditView( + DynamicFormViewMixin, SingleObjectEditView +): pass -class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectListPermissionFilterMixin, ExtraContextMixin, RedirectionMixin, ListView): +class SingleObjectListView( + PaginationMixin, ViewPermissionCheckMixin, RestrictedQuerysetMixin, + ExtraContextMixin, RedirectionMixin, ListView +): template_name = 'appearance/generic_list.html' def __init__(self, *args, **kwargs): @@ -605,7 +722,7 @@ class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectList if self.__class__.mro()[0].get_queryset != SingleObjectListView.get_queryset: raise ImproperlyConfigured( '%(cls)s is overloading the get_queryset method. Subclasses ' - 'should implement the get_object_list method instead. ' % { + 'should implement the get_source_queryset method instead. ' % { 'cls': self.__class__.__name__ } ) @@ -631,7 +748,7 @@ class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectList try: queryset = super(SingleObjectListView, self).get_queryset() except ImproperlyConfigured: - self.queryset = self.get_object_list() + self.queryset = self.get_source_queryset() queryset = super(SingleObjectListView, self).get_queryset() self.field_name = self.get_sort_field() diff --git a/mayan/apps/common/literals.py b/mayan/apps/common/literals.py index da9e461378..651eb871de 100644 --- a/mayan/apps/common/literals.py +++ b/mayan/apps/common/literals.py @@ -5,11 +5,14 @@ from django.utils.translation import ugettext_lazy as _ DEFAULT_COMMON_HOME_VIEW = 'common:home' DELETE_STALE_UPLOADS_INTERVAL = 60 * 10 # 10 minutes DJANGO_SQLITE_BACKEND = 'django.db.backends.sqlite3' + MESSAGE_SQLITE_WARNING = _( 'Your database backend is set to use SQLite. SQLite should only be used ' 'for development and testing, not for production.' ) +PK_LIST_SEPARATOR = ',' + TEXT_SORT_FIELD_PARAMETER = '_sort_field' TEXT_SORT_FIELD_VARIABLE_NAME = 'sort_field' TEXT_SORT_ORDER_CHOICE_ASCENDING = 'asc' diff --git a/mayan/apps/common/mixins.py b/mayan/apps/common/mixins.py index 7ba82f2adf..67a32e0e26 100644 --- a/mayan/apps/common/mixins.py +++ b/mayan/apps/common/mixins.py @@ -3,27 +3,23 @@ from __future__ import unicode_literals from django.conf import settings from django.contrib import messages from django.core.exceptions import ImproperlyConfigured, PermissionDenied -from django.db.models.query import QuerySet -from django.http import HttpResponseRedirect +from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, resolve_url from django.utils.translation import ungettext, ugettext_lazy as _ +from django.views.generic.detail import SingleObjectMixin from mayan.apps.acls.models import AccessControlList from mayan.apps.permissions import Permission from .exceptions import ActionError from .forms import DynamicForm - -__all__ = ( - 'DeleteExtraDataMixin', 'DynamicFormViewMixin', 'ExtraContextMixin', - 'FormExtraKwargsMixin', 'MultipleObjectMixin', 'ObjectActionMixin', - 'ObjectListPermissionFilterMixin', 'ObjectNameMixin', - 'ObjectPermissionCheckMixin', 'RedirectionMixin', - 'ViewPermissionCheckMixin' -) +from .literals import PK_LIST_SEPARATOR class DeleteExtraDataMixin(object): + """ + Mixin to populate the extra data needed for delete views + """ def delete(self, request, *args, **kwargs): self.object = self.get_object() success_url = self.get_success_url() @@ -44,7 +40,28 @@ class DynamicFormViewMixin(object): return data +class ExtraContextMixin(object): + """ + Mixin that allows views to pass extra context to the template much easier + than overloading .get_context_data(). + """ + extra_context = {} + + def get_extra_context(self): + return self.extra_context + + def get_context_data(self, **kwargs): + context = super(ExtraContextMixin, self).get_context_data(**kwargs) + context.update(self.get_extra_context()) + return context + + class ExternalObjectMixin(object): + """ + Mixin to allow views to load an object with minimal code but with all + the filtering and configurability possible. This object is often use as + the main or master object in multi object views. + """ external_object_class = None external_object_permission = None external_object_pk_url_kwarg = 'pk' @@ -100,21 +117,6 @@ class ExternalObjectMixin(object): return queryset -class ExtraContextMixin(object): - """ - Mixin that allows views to pass extra context to the template - """ - extra_context = {} - - def get_extra_context(self): - return self.extra_context - - def get_context_data(self, **kwargs): - context = super(ExtraContextMixin, self).get_context_data(**kwargs) - context.update(self.get_extra_context()) - return context - - class FormExtraKwargsMixin(object): """ Mixin that allows a view to pass extra keyword arguments to forms @@ -130,62 +132,103 @@ class FormExtraKwargsMixin(object): return result -class MultipleInstanceActionMixin(object): - # TODO: Deprecated, replace views using this with - # MultipleObjectFormActionView or MultipleObjectConfirmActionView - - model = None - success_message = _('Operation performed on %(count)d object') - success_message_plural = _('Operation performed on %(count)d objects') - - def get_pk_list(self): - return self.request.GET.get( - 'id_list', self.request.POST.get('id_list', '') - ).split(',') - - def get_queryset(self): - return self.model.objects.filter(pk__in=self.get_pk_list()) - - def get_success_message(self, count): - return ungettext( - self.success_message, - self.success_message_plural, - count - ) % { - 'count': count, - } - - def post(self, request, *args, **kwargs): - count = 0 - for instance in self.get_queryset(): - try: - self.object_action(instance=instance) - except PermissionDenied: - pass - else: - count += 1 - - messages.success( - self.request, - self.get_success_message(count=count) - ) - - return HttpResponseRedirect(self.get_success_url()) - - -class MultipleObjectMixin(object): +class MultipleObjectMixin(SingleObjectMixin): """ - Mixin that allows a view to work on a single or multiple objects + Mixin that allows a view to work on a single or multiple objects. It can + receive a pk, a slug or a list of IDs via an id_list query. + The pk, slug, and ID list parameter name can be changed using the + attributes: pk_url_kwargs, slug_url_kwarg, and pk_list_key. """ - model = None - object_permission = None pk_list_key = 'id_list' - pk_list_separator = ',' - pk_url_kwarg = 'pk' - queryset = None - slug_url_kwarg = 'slug' + pk_list_separator = PK_LIST_SEPARATOR + + def dispatch(self, request, *args, **kwargs): + self.object_list = self.get_object_list() + if self.view_mode_single: + self.object = self.object_list.first() + + return super(MultipleObjectMixin, self).dispatch(request=request, *args, **kwargs) + + def get(self, request, *args, **kwargs): + """ + Override BaseDetailView.get() + """ + return super(SingleObjectMixin, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + """ + Override SingleObjectMixin.get_context_data() + """ + return super(SingleObjectMixin, self).get_context_data(**kwargs) + + def get_object(self): + """ + Remove this method from the subclass + """ + raise AttributeError + + def get_object_list(self, queryset=None): + """ + Returns the list of objects the view is displaying. + + By default this requires `self.queryset` and a `pk`, `slug` ro + `pk_list' argument in the URLconf, but subclasses can override this + to return any object. + """ + self.view_mode_single = False + self.view_mode_multiple = False + + # Use a custom queryset if provided; this is required for subclasses + # like DateDetailView + if queryset is None: + queryset = self.get_queryset() + + # Next, try looking up by primary key. + pk = self.kwargs.get(self.pk_url_kwarg) + slug = self.kwargs.get(self.slug_url_kwarg) + pk_list = self.get_pk_list() + + if pk is not None: + queryset = queryset.filter(pk=pk) + self.view_mode_single = True + + # Next, try looking up by slug. + if slug is not None and (pk is None or self.query_pk_and_slug): + slug_field = self.get_slug_field() + queryset = queryset.filter(**{slug_field: slug}) + self.view_mode_single = True + + if pk_list is not None: + queryset = queryset.filter(pk__in=self.get_pk_list()) + self.view_mode_multiple = True + + # If none of those are defined, it's an error. + if pk is None and slug is None and pk_list is None: + raise AttributeError( + 'View %s must be called with ' + 'either an object pk, a slug or an pk list.' + % self.__class__.__name__ + ) + + try: + # Get the single item from the filtered queryset + queryset.get() + except queryset.model.MultipleObjectsReturned: + # Queryset has more than one item, this is good. + return queryset + except queryset.model.DoesNotExist: + raise Http404( + _('No %(verbose_name)s found matching the query') % + {'verbose_name': queryset.model._meta.verbose_name} + ) + else: + # Queryset has one item, this is good. + return queryset def get_pk_list(self): + # Accept pk_list even on POST request to allowing direct requests + # to the view bypassing the initial GET request to submit the form. + # Example: when the view is called from a test or a custom UI result = self.request.GET.get( self.pk_list_key, self.request.POST.get(self.pk_list_key) ) @@ -195,43 +238,6 @@ class MultipleObjectMixin(object): else: return None - def get_queryset(self): - if self.queryset is not None: - queryset = self.queryset - if isinstance(queryset, QuerySet): - queryset = queryset.all() - elif self.model is not None: - queryset = self.model._default_manager.all() - - pk = self.kwargs.get(self.pk_url_kwarg) - slug = self.kwargs.get(self.slug_url_kwarg) - pk_list = self.get_pk_list() - - if pk is not None: - queryset = queryset.filter(pk=pk) - - # Next, try looking up by slug. - if slug is not None and (pk is None or self.query_pk_and_slug): - slug_field = self.get_slug_field() - queryset = queryset.filter(**{slug_field: slug}) - - if pk_list is not None: - queryset = queryset.filter(pk__in=self.get_pk_list()) - - if pk is None and slug is None and pk_list is None: - raise AttributeError( - 'Generic detail view %s must be called with ' - 'either an object pk, a slug or an id list.' - % self.__class__.__name__ - ) - - if self.object_permission: - return AccessControlList.objects.filter_by_access( - self.object_permission, self.request.user, queryset=queryset - ) - else: - return queryset - class ObjectActionMixin(object): """ @@ -275,34 +281,6 @@ class ObjectActionMixin(object): ) -class ObjectListPermissionFilterMixin(object): - """ - access_object_retrieve_method is have the entire view check against - an object permission and not the individual secondary items. - """ - access_object_retrieve_method = None - object_permission = None - - def dispatch(self, request, *args, **kwargs): - if self.access_object_retrieve_method and self.object_permission: - AccessControlList.objects.check_access( - obj=getattr(self, self.access_object_retrieve_method)(), - permissions=(self.object_permission,), user=request.user - ) - return super(ObjectListPermissionFilterMixin, self).dispatch(request, *args, **kwargs) - - def get_queryset(self): - queryset = super(ObjectListPermissionFilterMixin, self).get_queryset() - - if not self.access_object_retrieve_method and self.object_permission: - return AccessControlList.objects.filter_by_access( - queryset=queryset, permission=self.object_permission, - user=self.request.user - ) - else: - return queryset - - class ObjectNameMixin(object): def get_object_name(self, context=None): if not context: @@ -319,6 +297,7 @@ class ObjectNameMixin(object): return object_name +# TODO: Remove this mixin and replace with restricted queryset class ObjectPermissionCheckMixin(object): object_permission = None @@ -425,6 +404,12 @@ class RestrictedQuerysetMixin(object): class ViewPermissionCheckMixin(object): + """ + Restrict access to the view based on the user's direct permissions from + roles. This mixing is used for views whose objects don't support ACLs or + for views that perform actions that are not related to a specify object or + object's permission like maintenance views. + """ view_permission = None def dispatch(self, request, *args, **kwargs): diff --git a/mayan/apps/common/views.py b/mayan/apps/common/views.py index af330a8f5f..9f0750a0e6 100644 --- a/mayan/apps/common/views.py +++ b/mayan/apps/common/views.py @@ -174,7 +174,7 @@ class ObjectErrorLogEntryListView(SingleObjectListView): klass=content_type.model_class(), pk=self.kwargs['object_id'] ) - def get_object_list(self): + def get_source_queryset(self): return self.get_object().error_logs.all() diff --git a/mayan/apps/converter/views.py b/mayan/apps/converter/views.py index 516f958e90..fe4c042069 100644 --- a/mayan/apps/converter/views.py +++ b/mayan/apps/converter/views.py @@ -232,5 +232,5 @@ class TransformationListView(SingleObjectListView): 'title': _('Transformations for: %s') % self.content_object, } - def get_object_list(self): + def get_source_queryset(self): return Transformation.objects.get_for_object(obj=self.content_object) diff --git a/mayan/apps/dependencies/views.py b/mayan/apps/dependencies/views.py index 09f554c826..e5d9951e7e 100644 --- a/mayan/apps/dependencies/views.py +++ b/mayan/apps/dependencies/views.py @@ -49,7 +49,7 @@ class DependencyGroupEntryListView(SingleObjectListView): 'title': _('Entries for dependency group: %s') % self.get_object(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_object().get_entries() def get_object(self): @@ -75,7 +75,7 @@ class DependencyGroupListView(SingleObjectListView): 'title': _('Dependency groups'), } - def get_object_list(self): + def get_source_queryset(self): return DependencyGroup.get_all() @@ -121,7 +121,7 @@ class DependencyGroupEntryDetailView(SingleObjectListView): ] ) - def get_object_list(self): + def get_source_queryset(self): return self.get_dependency_group_entry().get_dependencies() diff --git a/mayan/apps/django_gpg/views.py b/mayan/apps/django_gpg/views.py index 8cac032ac1..22d1436a3a 100644 --- a/mayan/apps/django_gpg/views.py +++ b/mayan/apps/django_gpg/views.py @@ -112,7 +112,7 @@ class KeyQueryResultView(SingleObjectListView): 'title': _('Key query results'), } - def get_object_list(self): + def get_source_queryset(self): term = self.request.GET.get('term') if term: return Key.objects.search(query=term) @@ -156,7 +156,7 @@ class KeyUploadView(SingleObjectCreateView): class PrivateKeyListView(SingleObjectListView): object_permission = permission_key_view - queryset = Key.objects.private_keys() + source_queryset = Key.objects.private_keys() def get_extra_context(self): return { @@ -179,7 +179,7 @@ class PrivateKeyListView(SingleObjectListView): class PublicKeyListView(SingleObjectListView): object_permission = permission_key_view - queryset = Key.objects.public_keys() + source_queryset = Key.objects.public_keys() def get_extra_context(self): return { diff --git a/mayan/apps/document_comments/views.py b/mayan/apps/document_comments/views.py index 19b8e369a0..b30ad5785a 100644 --- a/mayan/apps/document_comments/views.py +++ b/mayan/apps/document_comments/views.py @@ -118,7 +118,7 @@ class DocumentCommentListView(SingleObjectListView): 'title': _('Comments for document: %s') % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): AccessControlList.objects.check_access( obj=self.get_document(), permissions=(permission_comment_view,), user=self.request.user diff --git a/mayan/apps/document_indexing/apps.py b/mayan/apps/document_indexing/apps.py index a1043a2577..e1ae55c8ef 100644 --- a/mayan/apps/document_indexing/apps.py +++ b/mayan/apps/document_indexing/apps.py @@ -96,6 +96,13 @@ class DocumentIndexingApp(MayanAppConfig): permission_document_indexing_view, ) ) + ModelPermission.register_inheritance( + model=IndexTemplateNode, related='index' + ) + + ModelPermission.register_inheritance( + model=IndexInstanceNode, related='index_template_node__index' + ) SourceColumn( attribute='label', is_identifier=True, is_sortable=True, diff --git a/mayan/apps/document_indexing/tests/test_views.py b/mayan/apps/document_indexing/tests/test_views.py index 31fa4af01d..30e83a574a 100644 --- a/mayan/apps/document_indexing/tests/test_views.py +++ b/mayan/apps/document_indexing/tests/test_views.py @@ -36,7 +36,7 @@ class IndexViewTestCase(IndexTestMixin, IndexViewTestMixin, GenericDocumentViewT self._create_test_index() response = self._request_test_index_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Index.objects.count(), 1) @@ -54,7 +54,7 @@ class IndexViewTestCase(IndexTestMixin, IndexViewTestMixin, GenericDocumentViewT self._create_test_index() response = self._request_test_index_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_index.refresh_from_db() self.assertEqual(self.test_index.label, TEST_INDEX_LABEL) diff --git a/mayan/apps/document_indexing/views.py b/mayan/apps/document_indexing/views.py index 67940d8d41..5972da6870 100644 --- a/mayan/apps/document_indexing/views.py +++ b/mayan/apps/document_indexing/views.py @@ -191,7 +191,7 @@ class SetupIndexTreeTemplateListView(SingleObjectListView): def get_index(self): return get_object_or_404(klass=Index, pk=self.kwargs['pk']) - def get_object_list(self): + def get_source_queryset(self): return self.get_index().template_root.get_descendants( include_self=True ) @@ -231,7 +231,6 @@ class TemplateNodeCreateView(SingleObjectCreateView): class TemplateNodeDeleteView(SingleObjectDeleteView): model = IndexTemplateNode object_permission = permission_document_indexing_edit - object_permission_related = 'index' def get_extra_context(self): return { @@ -255,7 +254,6 @@ class TemplateNodeEditView(SingleObjectEditView): form_class = IndexTemplateNodeForm model = IndexTemplateNode object_permission = permission_document_indexing_edit - object_permission_related = 'index' def get_extra_context(self): return { @@ -294,7 +292,7 @@ class IndexListView(SingleObjectListView): 'title': _('Indexes'), } - def get_object_list(self): + def get_source_queryset(self): queryset = IndexInstance.objects.filter(enabled=True) return queryset.filter( node_templates__index_instance_nodes__isnull=False @@ -355,10 +353,10 @@ class IndexInstanceNodeView(DocumentListView): return context - def get_object_list(self): + def get_source_queryset(self): if self.index_instance_node: if self.index_instance_node.index_template_node.link_documents: - return super(IndexInstanceNodeView, self).get_object_list() + return super(IndexInstanceNodeView, self).get_source_queryset() else: self.object_permission = None return self.index_instance_node.get_children().order_by( @@ -374,7 +372,6 @@ class DocumentIndexNodeListView(SingleObjectListView): Show a list of indexes where the current document can be found """ object_permission = permission_document_indexing_instance_view - object_permission_related = 'index' def dispatch(self, request, *args, **kwargs): AccessControlList.objects.check_access( @@ -407,7 +404,7 @@ class DocumentIndexNodeListView(SingleObjectListView): ) % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return DocumentIndexInstanceNode.objects.get_for( document=self.get_document() ) diff --git a/mayan/apps/document_parsing/tests/test_views.py b/mayan/apps/document_parsing/tests/test_views.py index 8f3d0939dd..b964e4aab9 100644 --- a/mayan/apps/document_parsing/tests/test_views.py +++ b/mayan/apps/document_parsing/tests/test_views.py @@ -30,7 +30,7 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase): def test_document_content_view_no_permissions(self): response = self._request_document_content_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_content_view_with_access(self): self.grant_access( @@ -51,7 +51,7 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase): def test_document_page_content_view_no_permissions(self): response = self._request_document_page_content_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_page_content_view_with_access(self): self.grant_access( @@ -96,7 +96,7 @@ class DocumentContentViewsTestCase(GenericDocumentViewTestCase): def test_document_type_parsing_settings_view_no_permission(self): response = self._request_test_document_type_parsing_settings() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_type_parsing_settings_view_with_access(self): self.grant_access( diff --git a/mayan/apps/document_parsing/views.py b/mayan/apps/document_parsing/views.py index 67baeb71e4..89d4b2cbee 100644 --- a/mayan/apps/document_parsing/views.py +++ b/mayan/apps/document_parsing/views.py @@ -10,6 +10,7 @@ from mayan.apps.common.generics import ( FormView, MultipleObjectConfirmActionView, SingleObjectDetailView, SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.forms import DocumentTypeFilteredSelectForm from mayan.apps.documents.models import Document, DocumentPage, DocumentType @@ -93,7 +94,7 @@ class DocumentParsingErrorsListView(SingleObjectListView): ) % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().latest_version.parsing_errors.all() @@ -136,19 +137,21 @@ class DocumentSubmitView(MultipleObjectConfirmActionView): instance.submit_for_parsing() -class DocumentTypeSettingsEditView(SingleObjectEditView): +class DocumentTypeSettingsEditView(ExternalObjectMixin, SingleObjectEditView): + external_object_class = DocumentType + external_object_permission = permission_document_type_parsing_setup + external_object_pk_url_kwarg = 'pk' fields = ('auto_parsing',) - object_permission = permission_document_type_parsing_setup - post_action_redirect = reverse_lazy('documents:document_type_list') + post_action_redirect = reverse_lazy(viewname='documents:document_type_list') def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['pk']) + return self.external_object def get_extra_context(self): return { 'object': self.get_document_type(), 'title': _( - 'Edit parsing settings for document type: %s' + 'Edit parsing settings for document type: %s.' ) % self.get_document_type() } @@ -195,5 +198,5 @@ class ParseErrorListView(SingleObjectListView): } view_permission = permission_document_type_parsing_setup - def get_object_list(self): + def get_source_queryset(self): return DocumentVersionParseError.objects.all() diff --git a/mayan/apps/document_signatures/tests/test_views.py b/mayan/apps/document_signatures/tests/test_views.py index b020764845..d9b0ffd0d6 100644 --- a/mayan/apps/document_signatures/tests/test_views.py +++ b/mayan/apps/document_signatures/tests/test_views.py @@ -79,7 +79,7 @@ class SignaturesViewTestCase(SignaturesTestMixin, GenericDocumentViewTestCase): self._create_test_detached_signature() response = self._request_document_version_signature_details_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_signature_detail_view_with_access(self): self._create_test_key() @@ -186,7 +186,7 @@ class SignaturesViewTestCase(SignaturesTestMixin, GenericDocumentViewTestCase): ) response = self._request_document_version_signature_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(DetachedSignature.objects.count(), 1) def test_signature_delete_view_with_access(self): diff --git a/mayan/apps/document_signatures/views.py b/mayan/apps/document_signatures/views.py index d0826c1f68..4f436bc0c5 100644 --- a/mayan/apps/document_signatures/views.py +++ b/mayan/apps/document_signatures/views.py @@ -232,7 +232,6 @@ class DocumentVersionEmbeddedSignatureCreateView(FormView): class DocumentVersionSignatureDeleteView(SingleObjectDeleteView): model = DetachedSignature object_permission = permission_document_version_signature_delete - object_permission_related = 'document_version.document' def get_extra_context(self): return { @@ -251,7 +250,6 @@ class DocumentVersionSignatureDeleteView(SingleObjectDeleteView): class DocumentVersionSignatureDetailView(SingleObjectDetailView): form_class = DocumentVersionSignatureDetailForm object_permission = permission_document_version_signature_view - object_permission_related = 'document_version.document' def get_extra_context(self): return { @@ -263,14 +261,13 @@ class DocumentVersionSignatureDetailView(SingleObjectDetailView): ) % self.get_object(), } - def get_queryset(self): + def get_source_queryset(self): return SignatureBaseModel.objects.select_subclasses() class DocumentVersionSignatureDownloadView(SingleObjectDownloadView): model = DetachedSignature object_permission = permission_document_version_signature_download - object_permission_related = 'document_version.document' def get_file(self): signature = self.get_object() @@ -335,7 +332,7 @@ class DocumentVersionSignatureListView(SingleObjectListView): ) % self.get_document_version(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document_version().signatures.all() diff --git a/mayan/apps/document_states/tests/test_views.py b/mayan/apps/document_states/tests/test_views.py index 6a16fb7933..011ad583b4 100644 --- a/mayan/apps/document_states/tests/test_views.py +++ b/mayan/apps/document_states/tests/test_views.py @@ -54,7 +54,7 @@ class WorkflowViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow() response = self._request_workflow_delete_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertTrue(self.test_workflow in Workflow.objects.all()) @@ -84,7 +84,7 @@ class WorkflowViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow() response = self._request_workflow_edit_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.test_workflow.refresh_from_db() self.assertEqual(self.test_workflow.label, TEST_WORKFLOW_LABEL) @@ -137,7 +137,7 @@ class WorkflowViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow() response = self._request_workflow_preview_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertTrue(self.test_workflow in Workflow.objects.all()) @@ -169,7 +169,7 @@ class WorkflowStateViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow() response = self._request_workflow_state_create_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertEquals(WorkflowState.objects.count(), 0) @@ -223,7 +223,7 @@ class WorkflowStateViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow_states() response = self._request_workflow_state_delete_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertEquals(WorkflowState.objects.count(), 2) @@ -254,7 +254,7 @@ class WorkflowStateViewTestCase(WorkflowTestMixin, GenericViewTestCase): workflow_state_label = self.test_workflow_state_1.label response = self._request_workflow_state_edit_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.test_workflow_state_1.refresh_from_db() self.assertEquals( @@ -290,7 +290,7 @@ class WorkflowStateViewTestCase(WorkflowTestMixin, GenericViewTestCase): self._create_test_workflow_states() response = self._request_workflow_state_list_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) def test_workflow_state_list_with_access(self): self._create_test_workflow() @@ -357,7 +357,7 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC self._create_test_workflow_states() response = self._request_workflow_transition_create_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertEquals(WorkflowTransition.objects.count(), 0) @@ -392,13 +392,13 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC kwargs={'pk': self.test_workflow_transition.pk} ) - def test_delete_workflow_transition_no_access(self): + def test_delete_workflow_transition_no_permissions(self): self._create_test_workflow() self._create_test_workflow_states() self._create_test_workflow_transition() response = self._request_workflow_transition_delete_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.assertTrue( self.test_workflow_transition in WorkflowTransition.objects.all() @@ -434,7 +434,7 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC self._create_test_workflow_transition() response = self._request_workflow_transition_edit_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) self.test_workflow_transition.refresh_from_db() self.assertEqual( @@ -471,9 +471,10 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC self._create_test_workflow_transition() response = self._request_workflow_transition_list_view() - - self.assertEquals(response.status_code, 200) - self.assertNotContains(response, text=self.test_workflow_transition.label) + self.assertNotContains( + response=response, text=self.test_workflow_transition.label, + status_code=404 + ) def test_workflow_transition_list_with_access(self): self._create_test_workflow() @@ -485,8 +486,10 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC ) response = self._request_workflow_transition_list_view() - self.assertEquals(response.status_code, 200) - self.assertContains(response, text=self.test_workflow_transition.label) + self.assertContains( + response=response, text=self.test_workflow_transition.label, + status_code=200 + ) def _request_workflow_transition(self): return self.post( @@ -510,6 +513,7 @@ class WorkflowTransitionViewTestCase(WorkflowTestMixin, GenericDocumentViewTestC response = self._request_workflow_transition() self.assertEqual(response.status_code, 200) + # Workflow should remain in the same initial state self.assertEqual( self.test_workflow_instance.get_current_state(), @@ -574,7 +578,7 @@ class WorkflowTransitionEventViewTestCase(WorkflowTestMixin, GenericDocumentView self._create_test_workflow_transition() response = self._request_workflow_transition_event_list_view() - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 404) def test_workflow_transition_event_list_with_access(self): self._create_test_workflow() diff --git a/mayan/apps/document_states/views/workflow_instance_views.py b/mayan/apps/document_states/views/workflow_instance_views.py index 28d52d3bb6..b63b839454 100644 --- a/mayan/apps/document_states/views/workflow_instance_views.py +++ b/mayan/apps/document_states/views/workflow_instance_views.py @@ -51,7 +51,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView): ) % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().workflows.all() @@ -77,7 +77,7 @@ class WorkflowInstanceDetailView(SingleObjectListView): 'workflow_instance': self.get_workflow_instance(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_workflow_instance().log_entries.order_by('-datetime') def get_workflow_instance(self): diff --git a/mayan/apps/document_states/views/workflow_proxy_views.py b/mayan/apps/document_states/views/workflow_proxy_views.py index a26ee49e0e..3cfe155204 100644 --- a/mayan/apps/document_states/views/workflow_proxy_views.py +++ b/mayan/apps/document_states/views/workflow_proxy_views.py @@ -75,7 +75,7 @@ class WorkflowListView(SingleObjectListView): 'title': _('Workflows'), } - def get_object_list(self): + def get_source_queryset(self): return WorkflowRuntimeProxy.objects.all() @@ -148,7 +148,7 @@ class WorkflowStateListView(SingleObjectListView): 'title': _('States of workflow: %s') % self.get_workflow() } - def get_object_list(self): + def get_source_queryset(self): return WorkflowStateRuntimeProxy.objects.filter( workflow=self.get_workflow() ) diff --git a/mayan/apps/document_states/views/workflow_views.py b/mayan/apps/document_states/views/workflow_views.py index cfbefd337a..b5985d7efc 100644 --- a/mayan/apps/document_states/views/workflow_views.py +++ b/mayan/apps/document_states/views/workflow_views.py @@ -3,20 +3,19 @@ from __future__ import absolute_import, unicode_literals from django.contrib import messages from django.core.files.base import ContentFile from django.db import transaction -from django.db.utils import IntegrityError from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.template import RequestContext from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ -from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( AddRemoveView, ConfirmView, FormView, SingleObjectCreateView, SingleObjectDeleteView, SingleObjectDetailView, SingleObjectDynamicFormCreateView, SingleObjectDynamicFormEditView, SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.events import event_document_type_edited from mayan.apps.documents.models import DocumentType from mayan.apps.documents.permissions import permission_document_type_edit @@ -366,7 +365,7 @@ class SetupWorkflowStateActionListView(SingleObjectListView): def get_form_schema(self): return {'fields': self.get_class().fields} - def get_object_list(self): + def get_source_queryset(self): return self.get_workflow_state().actions.all() def get_workflow_state(self): @@ -403,7 +402,10 @@ class SetupWorkflowStateActionSelectionView(FormView): # Workflow states -class SetupWorkflowStateCreateView(SingleObjectCreateView): +class SetupWorkflowStateCreateView(ExternalObjectMixin, SingleObjectCreateView): + external_object_class = Workflow + external_object_permission = permission_workflow_edit + external_object_pk_url_kwarg = 'pk' form_class = WorkflowStateForm def get_extra_context(self): @@ -414,34 +416,26 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView): ) % self.get_workflow() } - def get_object_list(self): + def get_instance_extra_data(self): + return {'workflow': self.get_workflow()} + + def get_source_queryset(self): return self.get_workflow().states.all() def get_success_url(self): return reverse( - viewname='document_states:setup_workflow_state_list', kwargs={ - 'pk': self.kwargs['pk'] - } + viewname='document_states:setup_workflow_state_list', + kwargs={'pk': self.kwargs['pk']} ) def get_workflow(self): - workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) - AccessControlList.objects.check_access( - obj=workflow, permissions=(permission_workflow_edit,), - user=self.request.user - ) - return workflow - - def form_valid(self, form): - self.object = form.save(commit=False) - self.object.workflow = self.get_workflow() - self.object.save() - return super(SetupWorkflowStateCreateView, self).form_valid(form) + return self.get_external_object() class SetupWorkflowStateDeleteView(SingleObjectDeleteView): model = WorkflowState object_permission = permission_workflow_edit + pk_url_kwarg = 'pk' def get_extra_context(self): return { @@ -450,28 +444,18 @@ class SetupWorkflowStateDeleteView(SingleObjectDeleteView): 'workflow_instance': self.get_object().workflow, } - def get_object_list(self): - return self.get_workflow().states.all() - def get_success_url(self): return reverse( viewname='document_states:setup_workflow_state_list', kwargs={'pk': self.get_object().workflow.pk} ) - def get_workflow(self): - workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) - AccessControlList.objects.check_access( - obj=workflow, permissions=(permission_workflow_edit,), - user=self.request.user - ) - return workflow - class SetupWorkflowStateEditView(SingleObjectEditView): form_class = WorkflowStateForm model = WorkflowState object_permission = permission_workflow_edit + pk_url_kwarg = 'pk' def get_extra_context(self): return { @@ -487,27 +471,19 @@ class SetupWorkflowStateEditView(SingleObjectEditView): ) -class SetupWorkflowStateListView(SingleObjectListView): +class SetupWorkflowStateListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = Workflow + external_object_permission = permission_workflow_view + external_object_pk_url_kwarg = 'pk' object_permission = permission_workflow_view - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_workflow(), permissions=(permission_workflow_view,), - user=request.user - ) - - return super( - SetupWorkflowStateListView, self - ).dispatch(request, *args, **kwargs) - def get_extra_context(self): return { - 'hide_link': True, 'hide_object': True, 'no_results_icon': icon_workflow_state, 'no_results_main_link': link_setup_workflow_state_create.resolve( context=RequestContext( - request=self.request, dict_={'object': self.get_workflow()} + self.request, {'object': self.get_workflow()} ) ), 'no_results_text': _( @@ -520,35 +496,22 @@ class SetupWorkflowStateListView(SingleObjectListView): 'title': _('States of workflow: %s') % self.get_workflow() } - def get_object_list(self): + def get_source_queryset(self): return self.get_workflow().states.all() def get_workflow(self): - return get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) + return self.get_external_object() # Transitions -class SetupWorkflowTransitionCreateView(SingleObjectCreateView): +class SetupWorkflowTransitionCreateView(ExternalObjectMixin, SingleObjectCreateView): + external_object_class = Workflow + external_object_permission = permission_workflow_edit + external_object_pk_url_kwarg = 'pk' form_class = WorkflowTransitionForm - def form_valid(self, form): - self.object = form.save(commit=False) - self.object.workflow = self.get_workflow() - try: - self.object.save() - except IntegrityError: - messages.error( - message=_('Unable to save transition; integrity error.'), - request=self.request - ) - return super( - SetupWorkflowTransitionCreateView, self - ).form_invalid(form) - else: - return HttpResponseRedirect(self.get_success_url()) - def get_extra_context(self): return { 'object': self.get_workflow(), @@ -564,7 +527,10 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView): kwargs['workflow'] = self.get_workflow() return kwargs - def get_object_list(self): + def get_instance_extra_data(self): + return {'workflow': self.get_workflow()} + + def get_source_queryset(self): return self.get_workflow().transitions.all() def get_success_url(self): @@ -574,17 +540,13 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView): ) def get_workflow(self): - workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) - AccessControlList.objects.check_access( - obj=workflow, permissions=(permission_workflow_edit,), - user=self.request.user - ) - return workflow + return self.get_external_object() class SetupWorkflowTransitionDeleteView(SingleObjectDeleteView): model = WorkflowTransition object_permission = permission_workflow_edit + pk_url_kwarg = 'pk' def get_extra_context(self): return { @@ -604,6 +566,7 @@ class SetupWorkflowTransitionEditView(SingleObjectEditView): form_class = WorkflowTransitionForm model = WorkflowTransition object_permission = permission_workflow_edit + pk_url_kwarg = 'pk' def get_extra_context(self): return { @@ -626,17 +589,19 @@ class SetupWorkflowTransitionEditView(SingleObjectEditView): ) -class SetupWorkflowTransitionListView(SingleObjectListView): +class SetupWorkflowTransitionListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = Workflow + external_object_permission = permission_workflow_view + external_object_pk_url_kwarg = 'pk' object_permission = permission_workflow_view def get_extra_context(self): return { - 'hide_link': True, 'hide_object': True, 'no_results_icon': icon_workflow_transition, 'no_results_main_link': link_setup_workflow_transition_create.resolve( context=RequestContext( - request=self.request, dict_={'object': self.get_workflow()} + self.request, {'object': self.get_workflow()} ) ), 'no_results_text': _( @@ -652,24 +617,20 @@ class SetupWorkflowTransitionListView(SingleObjectListView): ) % self.get_workflow() } - def get_object_list(self): + def get_source_queryset(self): return self.get_workflow().transitions.all() def get_workflow(self): - return get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) + return self.get_external_object() -class SetupWorkflowTransitionTriggerEventListView(FormView): +class SetupWorkflowTransitionTriggerEventListView(ExternalObjectMixin, FormView): + external_object_class = WorkflowTransition + external_object_permission = permission_workflow_edit + external_object_pk_url_kwarg = 'pk' form_class = WorkflowTransitionTriggerEventRelationshipFormSet - submodel = StoredEventType def dispatch(self, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_object().workflow, - permissions=(permission_workflow_edit,), - user=self.request.user - ) - EventType.refresh() return super( SetupWorkflowTransitionTriggerEventListView, self @@ -684,6 +645,7 @@ class SetupWorkflowTransitionTriggerEventListView(FormView): message=_( 'Error updating workflow transition trigger events; %s' ) % exception, request=self.request + ) else: messages.success( @@ -697,7 +659,7 @@ class SetupWorkflowTransitionTriggerEventListView(FormView): ).form_valid(form=form) def get_object(self): - return get_object_or_404(klass=WorkflowTransition, pk=self.kwargs['pk']) + return self.get_external_object() def get_extra_context(self): return { diff --git a/mayan/apps/documents/tests/test_document_type_views.py b/mayan/apps/documents/tests/test_document_type_views.py index 894deefb24..3d4fc70463 100644 --- a/mayan/apps/documents/tests/test_document_type_views.py +++ b/mayan/apps/documents/tests/test_document_type_views.py @@ -56,7 +56,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): def test_document_type_delete_view_no_permission(self): response = self._request_document_type_delete() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(DocumentType.objects.count(), 1) @@ -84,7 +84,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase): def test_document_type_edit_view_no_permission(self): response = self._request_document_type_edit() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_document_type.refresh_from_db() self.assertEqual( @@ -123,7 +123,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener ) response = self._request_quick_label_create() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(self.test_document_type.filenames.count(), 0) @@ -146,7 +146,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener def test_document_type_quick_label_delete_no_access(self): self._create_test_quick_label() response = self._request_quick_label_delete() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual( self.test_document_type.filenames.count(), 1 @@ -178,7 +178,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener self._create_test_quick_label() response = self._request_quick_label_edit() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_document_type_filename.refresh_from_db() self.assertEqual( @@ -212,7 +212,7 @@ class DocumentTypeQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, Gener self._create_test_quick_label() response = self._request_quick_label_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_type_quick_label_list_with_access(self): self._create_test_quick_label() diff --git a/mayan/apps/documents/tests/test_document_version_views.py b/mayan/apps/documents/tests/test_document_version_views.py index 42158439e4..db2d60bace 100644 --- a/mayan/apps/documents/tests/test_document_version_views.py +++ b/mayan/apps/documents/tests/test_document_version_views.py @@ -20,7 +20,7 @@ class DocumentVersionTestCase(DocumentVersionTestMixin, GenericDocumentViewTestC self._upload_new_version() response = self._request_document_version_list_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_version_list_with_access(self): self._upload_new_version() @@ -46,7 +46,7 @@ class DocumentVersionTestCase(DocumentVersionTestMixin, GenericDocumentViewTestC response = self._request_document_version_revert_view( document_version=first_version ) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(self.document.versions.count(), 2) diff --git a/mayan/apps/documents/tests/test_document_views.py b/mayan/apps/documents/tests/test_document_views.py index 871b12822a..fd6d65566c 100644 --- a/mayan/apps/documents/tests/test_document_views.py +++ b/mayan/apps/documents/tests/test_document_views.py @@ -34,7 +34,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): def test_document_view_no_permissions(self): response = self._request_document_properties_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_view_with_permissions(self): self.grant_access( @@ -84,7 +84,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): response = self._request_document_type_edit( document_type=document_type_2 ) - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( Document.objects.get(pk=self.test_document.pk).document_type, @@ -138,7 +138,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): response = self._request_multiple_document_type_edit( document_type=document_type_2 ) - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( Document.objects.first().document_type, self.test_document_type @@ -327,7 +327,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): self.assertEqual(self.test_document.pages.count(), 0) response = self._request_document_update_page_count_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(self.test_document.pages.count(), 0) @@ -356,7 +356,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): self.assertEqual(self.test_document.pages.count(), 0) response = self._request_document_multiple_update_page_count_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(self.test_document.pages.count(), 0) @@ -397,7 +397,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): ) response = self._request_document_clear_transformations_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertQuerysetEqual( Transformation.objects.get_for_object(obj=document_page), @@ -454,7 +454,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase): self.grant_permission(permission=permission_document_view) response = self._request_document_multiple_clear_transformations() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertQuerysetEqual( Transformation.objects.get_for_object(obj=document_page), (repr(transformation),) @@ -578,7 +578,7 @@ class DocumentsQuickLabelViewsTestCase(DocumentTypeQuickLabelTestMixin, GenericD self._create_test_quick_label() response = self._request_document_quick_label_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_quick_label_with_access(self): self._create_test_quick_label() diff --git a/mayan/apps/documents/tests/test_events.py b/mayan/apps/documents/tests/test_events.py index 2982ebc08e..2f9428054b 100644 --- a/mayan/apps/documents/tests/test_events.py +++ b/mayan/apps/documents/tests/test_events.py @@ -66,7 +66,7 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase): Action.objects.all().delete() response = self._request_test_document_preview_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(list(Action.objects.any(obj=self.test_document)), []) diff --git a/mayan/apps/documents/tests/test_favorite_document_views.py b/mayan/apps/documents/tests/test_favorite_document_views.py index df3e981aa0..ea1c9fedce 100644 --- a/mayan/apps/documents/tests/test_favorite_document_views.py +++ b/mayan/apps/documents/tests/test_favorite_document_views.py @@ -15,7 +15,7 @@ class FavoriteDocumentsTestCase(GenericDocumentViewTestCase): def test_document_add_to_favorites_view_no_permission(self): response = self._request_document_add_to_favorites_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(FavoriteDocument.objects.count(), 0) def test_document_add_to_favorites_view_with_access(self): @@ -62,7 +62,7 @@ class FavoriteDocumentsTestCase(GenericDocumentViewTestCase): def test_document_remove_from_favorites_view_no_permission(self): self._document_add_to_favorites() response = self._request_document_remove_from_favorites() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(FavoriteDocument.objects.count(), 1) def test_document_remove_from_favorites_view_with_access(self): diff --git a/mayan/apps/documents/tests/test_trashed_document_views.py b/mayan/apps/documents/tests/test_trashed_document_views.py index 180ed538ce..8be806f36e 100644 --- a/mayan/apps/documents/tests/test_trashed_document_views.py +++ b/mayan/apps/documents/tests/test_trashed_document_views.py @@ -10,24 +10,25 @@ from .base import GenericDocumentViewTestCase class TrashedDocumentTestCase(GenericDocumentViewTestCase): - def _request_document_restore_view(self): - return self.post( + def _request_document_restore_get_view(self): + return self.get( viewname='documents:document_restore', kwargs={ 'pk': self.test_document.pk } ) - def test_document_restore_view_no_permission(self): + def test_document_restore_get_view_no_permission(self): self.test_document.delete() self.assertEqual(Document.objects.count(), 0) - response = self._request_document_restore_view() - self.assertEqual(response.status_code, 302) + document_count = Document.objects.count() - self.assertEqual(DeletedDocument.objects.count(), 1) - self.assertEqual(Document.objects.count(), 0) + response = self._request_document_restore_get_view() + self.assertEqual(response.status_code, 404) - def test_document_restore_view_with_access(self): + self.assertEqual(Document.objects.count(), document_count) + + def test_document_restore_get_view_with_access(self): self.test_document.delete() self.assertEqual(Document.objects.count(), 0) @@ -35,56 +36,118 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase): obj=self.test_document, permission=permission_document_restore ) - response = self._request_document_restore_view() + document_count = Document.objects.count() + + response = self._request_document_restore_get_view() + self.assertEqual(response.status_code, 200) + + self.assertEqual(Document.objects.count(), document_count) + + def _request_document_restore_post_view(self): + return self.post( + viewname='documents:document_restore', kwargs={ + 'pk': self.test_document.pk + } + ) + + def test_document_restore_post_view_no_permission(self): + self.test_document.delete() + self.assertEqual(Document.objects.count(), 0) + + response = self._request_document_restore_post_view() + self.assertEqual(response.status_code, 404) + + self.assertEqual(DeletedDocument.objects.count(), 1) + self.assertEqual(Document.objects.count(), 0) + + def test_document_restore_post_view_with_access(self): + self.test_document.delete() + self.assertEqual(Document.objects.count(), 0) + + self.grant_access( + obj=self.test_document, permission=permission_document_restore + ) + + response = self._request_document_restore_post_view() self.assertEqual(response.status_code, 302) self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 1) - def _request_document_trash_view(self): + def _request_document_trash_get_view(self): + return self.get( + viewname='documents:document_trash', kwargs={ + 'pk': self.test_document.pk + } + ) + + def test_document_trash_get_view_no_permissions(self): + document_count = Document.objects.count() + + response = self._request_document_trash_get_view() + self.assertEqual(response.status_code, 404) + + self.assertEqual(Document.objects.count(), document_count) + + def test_document_trash_get_view_with_access(self): + self.grant_access( + obj=self.test_document, permission=permission_document_trash + ) + + document_count = Document.objects.count() + + response = self._request_document_trash_get_view() + self.assertEqual(response.status_code, 200) + + self.assertEqual(Document.objects.count(), document_count) + + def _request_document_trash_post_view(self): return self.post( viewname='documents:document_trash', kwargs={ 'pk': self.test_document.pk } ) - def test_document_trash_no_permissions(self): - response = self._request_document_trash_view() - self.assertEqual(response.status_code, 302) + def test_document_trash_post_view_no_permissions(self): + response = self._request_document_trash_post_view() + self.assertEqual(response.status_code, 404) self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(Document.objects.count(), 1) - def test_document_trash_with_access(self): + def test_document_trash_post_view_with_access(self): self.grant_access( obj=self.test_document, permission=permission_document_trash ) - response = self._request_document_trash_view() + response = self._request_document_trash_post_view() self.assertEqual(response.status_code, 302) self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(Document.objects.count(), 0) - def _request_document_delete_view(self): - return self.post( + def _request_document_delete_get_view(self): + return self.get( viewname='documents:document_delete', kwargs={ 'pk': self.test_document.pk } ) - def test_document_delete_no_permissions(self): + def test_document_delete_get_view_no_permissions(self): self.test_document.delete() self.assertEqual(Document.objects.count(), 0) self.assertEqual(DeletedDocument.objects.count(), 1) - response = self._request_document_delete_view() - self.assertEqual(response.status_code, 302) + trashed_document_count = DeletedDocument.objects.count() - self.assertEqual(Document.objects.count(), 0) - self.assertEqual(DeletedDocument.objects.count(), 1) + response = self._request_document_delete_get_view() + self.assertEqual(response.status_code, 404) - def test_document_delete_with_access(self): + self.assertEqual( + DeletedDocument.objects.count(), trashed_document_count + ) + + def test_document_delete_get_view_with_access(self): self.test_document.delete() self.assertEqual(Document.objects.count(), 0) self.assertEqual(DeletedDocument.objects.count(), 1) @@ -93,7 +156,43 @@ class TrashedDocumentTestCase(GenericDocumentViewTestCase): obj=self.test_document, permission=permission_document_delete ) - response = self._request_document_delete_view() + trashed_document_count = DeletedDocument.objects.count() + + response = self._request_document_delete_get_view() + self.assertEqual(response.status_code, 200) + + self.assertEqual( + DeletedDocument.objects.count(), trashed_document_count + ) + + def _request_document_delete_post_view(self): + return self.post( + viewname='documents:document_delete', kwargs={ + 'pk': self.test_document.pk + } + ) + + def test_document_delete_post_view_no_permissions(self): + self.test_document.delete() + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(DeletedDocument.objects.count(), 1) + + response = self._request_document_delete_post_view() + self.assertEqual(response.status_code, 404) + + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(DeletedDocument.objects.count(), 1) + + def test_document_delete_post_view_with_access(self): + self.test_document.delete() + self.assertEqual(Document.objects.count(), 0) + self.assertEqual(DeletedDocument.objects.count(), 1) + + self.grant_access( + obj=self.test_document, permission=permission_document_delete + ) + + response = self._request_document_delete_post_view() self.assertEqual(response.status_code, 302) self.assertEqual(DeletedDocument.objects.count(), 0) diff --git a/mayan/apps/documents/views/document_page_views.py b/mayan/apps/documents/views/document_page_views.py index 4b9df1c9d2..1ee6d7de91 100644 --- a/mayan/apps/documents/views/document_page_views.py +++ b/mayan/apps/documents/views/document_page_views.py @@ -56,7 +56,7 @@ class DocumentPageListView(SingleObjectListView): 'title': _('Pages for document: %s') % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().pages.all() diff --git a/mayan/apps/documents/views/document_type_views.py b/mayan/apps/documents/views/document_type_views.py index dd37e71444..2f1b7861f2 100644 --- a/mayan/apps/documents/views/document_type_views.py +++ b/mayan/apps/documents/views/document_type_views.py @@ -7,11 +7,11 @@ from django.template import RequestContext from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ -from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( SingleObjectCreateView, SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from ..forms import DocumentTypeFilenameForm_create from ..icons import ( @@ -133,22 +133,14 @@ class DocumentTypeEditView(SingleObjectEditView): } -class DocumentTypeFilenameCreateView(SingleObjectCreateView): +class DocumentTypeFilenameCreateView(ExternalObjectMixin, SingleObjectCreateView): + external_object_class = DocumentType + external_object_permission = permission_document_type_edit + external_object_pk_url_kwarg = 'pk' form_class = DocumentTypeFilenameForm_create - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_document_type(), - permissions=(permission_document_type_edit,), - user=request.user - ) - - return super(DocumentTypeFilenameCreateView, self).dispatch( - request, *args, **kwargs - ) - def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['pk']) + return self.get_external_object() def get_extra_context(self): return { @@ -163,10 +155,37 @@ class DocumentTypeFilenameCreateView(SingleObjectCreateView): return {'document_type': self.get_document_type()} +class DocumentTypeFilenameDeleteView(SingleObjectDeleteView): + model = DocumentTypeFilename + object_permission = permission_document_type_edit + pk_url_kwarg = 'pk' + + def get_extra_context(self): + return { + 'document_type': self.get_object().document_type, + 'filename': self.get_object(), + 'navigation_object_list': ('document_type', 'filename',), + 'title': _( + 'Delete the quick label: %(label)s, from document type ' + '"%(document_type)s"?' + ) % { + 'document_type': self.get_object().document_type, + 'label': self.get_object() + }, + } + + def get_post_action_redirect(self): + return reverse( + viewname='documents:document_type_filename_list', + kwargs={'pk': self.get_object().document_type.pk} + ) + + class DocumentTypeFilenameEditView(SingleObjectEditView): fields = ('enabled', 'filename',) model = DocumentTypeFilename object_permission = permission_document_type_edit + pk_url_kwarg = 'pk' def get_extra_context(self): document_type_filename = self.get_object() @@ -186,42 +205,18 @@ class DocumentTypeFilenameEditView(SingleObjectEditView): def get_post_action_redirect(self): return reverse( - 'documents:document_type_filename_list', - args=(self.get_object().document_type.pk,) + viewname='documents:document_type_filename_list', + kwargs={'pk': self.get_object().document_type.pk} ) -class DocumentTypeFilenameDeleteView(SingleObjectDeleteView): - model = DocumentTypeFilename - object_permission = permission_document_type_edit - - def get_extra_context(self): - return { - 'document_type': self.get_object().document_type, - 'filename': self.get_object(), - 'navigation_object_list': ('document_type', 'filename',), - 'title': _( - 'Delete the quick label: %(label)s, from document type ' - '"%(document_type)s"?' - ) % { - 'document_type': self.get_object().document_type, - 'label': self.get_object() - }, - } - - def get_post_action_redirect(self): - return reverse( - 'documents:document_type_filename_list', - args=(self.get_object().document_type.pk,) - ) - - -class DocumentTypeFilenameListView(SingleObjectListView): - access_object_retrieve_method = 'get_document_type' - object_permission = permission_document_type_view +class DocumentTypeFilenameListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = DocumentType + external_object_permission = permission_document_type_view + external_object_pk_url_kwarg = 'pk' def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['pk']) + return self.get_external_object() def get_extra_context(self): return { @@ -251,5 +246,5 @@ class DocumentTypeFilenameListView(SingleObjectListView): ) % self.get_document_type(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document_type().filenames.all() diff --git a/mayan/apps/documents/views/document_version_views.py b/mayan/apps/documents/views/document_version_views.py index 111c46b827..a90e9b57fe 100644 --- a/mayan/apps/documents/views/document_version_views.py +++ b/mayan/apps/documents/views/document_version_views.py @@ -10,6 +10,7 @@ from mayan.apps.acls.models import AccessControlList from mayan.apps.common.generics import ( ConfirmView, SingleObjectDetailView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from ..events import event_document_view from ..forms import DocumentVersionDownloadForm, DocumentVersionPreviewForm @@ -22,71 +23,13 @@ from ..permissions import ( from .document_views import DocumentDownloadFormView, DocumentDownloadView __all__ = ( - 'DocumentVersionListView', 'DocumentVersionRevertView', 'DocumentVersionDownloadFormView', 'DocumentVersionDownloadView', + 'DocumentVersionListView', 'DocumentVersionRevertView', 'DocumentVersionView' ) logger = logging.getLogger(__name__) -class DocumentVersionListView(SingleObjectListView): - def dispatch(self, request, *args, **kwargs): - AccessControlList.objects.check_access( - obj=self.get_document(), - permissions=(permission_document_version_view,), - user=request.user - ) - - self.get_document().add_as_recent_document_for_user(request.user) - - return super( - DocumentVersionListView, self - ).dispatch(request, *args, **kwargs) - - def get_document(self): - return get_object_or_404(klass=Document, pk=self.kwargs['pk']) - - def get_extra_context(self): - return { - 'hide_object': True, - 'list_as_items': True, - 'object': self.get_document(), - 'title': _('Versions of document: %s') % self.get_document(), - } - - def get_object_list(self): - return self.get_document().versions.order_by('-timestamp') - - -class DocumentVersionRevertView(ConfirmView): - object_permission = permission_document_version_revert - object_permission_related = 'document' - - def get_extra_context(self): - return { - 'message': _( - 'All later version after this one will be deleted too.' - ), - 'object': self.get_object().document, - 'title': _('Revert to this version?'), - } - - def get_object(self): - return get_object_or_404(klass=DocumentVersion, pk=self.kwargs['pk']) - - def view_action(self): - try: - self.get_object().revert(_user=self.request.user) - messages.success( - self.request, _('Document version reverted successfully') - ) - except Exception as exception: - messages.error( - self.request, - _('Error reverting document version; %s') % exception - ) - - class DocumentVersionDownloadFormView(DocumentDownloadFormView): form_class = DocumentVersionDownloadForm model = DocumentVersion @@ -146,6 +89,61 @@ class DocumentVersionDownloadView(DocumentDownloadView): return self.get_object().mimetype +class DocumentVersionListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = Document + external_object_permission = permission_document_version_view + external_object_pk_url_kwarg = 'pk' + + def get_document(self): + document = self.get_external_object() + document.add_as_recent_document_for_user(user=self.request.user) + return document + + def get_extra_context(self): + return { + 'hide_object': True, + 'list_as_items': True, + 'object': self.get_document(), + 'table_cell_container_classes': 'td-container-thumbnail', + 'title': _('Versions of document: %s') % self.get_document(), + } + + def get_source_queryset(self): + return self.get_document().versions.order_by('-timestamp') + + +class DocumentVersionRevertView(ExternalObjectMixin, ConfirmView): + external_object_class = DocumentVersion + external_object_permission = permission_document_version_revert + external_object_pk_url_kwarg = 'pk' + + def get_extra_context(self): + return { + 'message': _( + 'All later version after this one will be deleted too.' + ), + 'object': self.get_object().document, + 'title': _('Revert to this version?'), + } + + def get_object(self): + return self.get_external_object() + + def view_action(self): + try: + self.get_object().revert(_user=self.request.user) + messages.success( + message=_( + 'Document version reverted successfully' + ), request=self.request + ) + except Exception as exception: + messages.error( + message=_('Error reverting document version; %s') % exception, + request=self.request + ) + + class DocumentVersionView(SingleObjectDetailView): form_class = DocumentVersionPreviewForm model = DocumentVersion diff --git a/mayan/apps/documents/views/document_views.py b/mayan/apps/documents/views/document_views.py index c80200e76c..abdc74a285 100644 --- a/mayan/apps/documents/views/document_views.py +++ b/mayan/apps/documents/views/document_views.py @@ -95,7 +95,7 @@ class DocumentListView(SingleObjectListView): 'title': _('All documents'), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document_queryset() @@ -347,7 +347,7 @@ class DocumentDuplicatesListView(DocumentListView): ) return context - def get_object_list(self): + def get_source_queryset(self): try: return DuplicatedDocument.objects.get( document=self.get_document() diff --git a/mayan/apps/documents/views/trashed_document_views.py b/mayan/apps/documents/views/trashed_document_views.py index 1e2d3a033f..64b53301ed 100644 --- a/mayan/apps/documents/views/trashed_document_views.py +++ b/mayan/apps/documents/views/trashed_document_views.py @@ -41,11 +41,11 @@ class DocumentTrashView(MultipleObjectConfirmActionView): ) def get_extra_context(self): - queryset = self.get_object_list() + queryset = self.get_queryset() result = { 'title': ungettext( - single='Move the selected document to the trash?', + singular='Move the selected document to the trash?', plural='Move the selected documents to the trash?', number=queryset.count() ) @@ -72,7 +72,9 @@ class EmptyTrashCanView(ConfirmView): kwargs={'trashed_document_id': deleted_document.pk} ) - messages.success(self.request, _('Trash emptied successfully')) + messages.success( + message=_('Trash emptied successfully'), request=self.request + ) class TrashedDocumentDeleteView(MultipleObjectConfirmActionView): @@ -87,11 +89,11 @@ class TrashedDocumentDeleteView(MultipleObjectConfirmActionView): ) def get_extra_context(self): - queryset = self.get_object_list() + queryset = self.get_queryset() result = { 'title': ungettext( - single='Delete the selected trashed document?', + singular='Delete the selected trashed document?', plural='Delete the selected trashed documents?', number=queryset.count() ) @@ -147,11 +149,11 @@ class TrashedDocumentRestoreView(MultipleObjectConfirmActionView): ) def get_extra_context(self): - queryset = self.get_object_list() + queryset = self.get_queryset() result = { 'title': ungettext( - single='Restore the selected trashed document?', + singular='Restore the selected trashed document?', plural='Restore the selected trashed documents?', number=queryset.count() ) diff --git a/mayan/apps/dynamic_search/views.py b/mayan/apps/dynamic_search/views.py index 13fb7a545f..9bbc79c741 100644 --- a/mayan/apps/dynamic_search/views.py +++ b/mayan/apps/dynamic_search/views.py @@ -31,7 +31,7 @@ class ResultsView(SearchModelMixin, SingleObjectListView): return context - def get_object_list(self): + def get_source_queryset(self): self.search_model = self.get_search_model() if self.request.GET: diff --git a/mayan/apps/events/views.py b/mayan/apps/events/views.py index 3b0ce90233..2289b34e0c 100644 --- a/mayan/apps/events/views.py +++ b/mayan/apps/events/views.py @@ -36,7 +36,7 @@ class EventListView(SingleObjectListView): 'title': _('Events'), } - def get_object_list(self): + def get_source_queryset(self): return Action.objects.all() @@ -119,7 +119,7 @@ class NotificationListView(SingleObjectListView): 'title': _('Notifications'), } - def get_object_list(self): + def get_source_queryset(self): return self.request.user.notifications.all() @@ -186,7 +186,7 @@ class ObjectEventListView(EventListView): klass=queryset, pk=self.kwargs['object_id'] ) - def get_object_list(self): + def get_source_queryset(self): return any_stream(self.object) @@ -281,5 +281,5 @@ class VerbEventListView(SingleObjectListView): ) % EventType.get(name=self.kwargs['verb']), } - def get_object_list(self): + def get_source_queryset(self): return Action.objects.filter(verb=self.kwargs['verb']) diff --git a/mayan/apps/file_metadata/tests/test_views.py b/mayan/apps/file_metadata/tests/test_views.py index 81376db3ee..ebed4dd71f 100644 --- a/mayan/apps/file_metadata/tests/test_views.py +++ b/mayan/apps/file_metadata/tests/test_views.py @@ -72,7 +72,7 @@ class FileMetadataViewsTestCase(GenericDocumentViewTestCase): self.document.latest_version.file_metadata_drivers.all().delete() response = self._request_document_submit_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( self.document.latest_version.file_metadata_drivers.count(), 0 @@ -103,7 +103,7 @@ class FileMetadataViewsTestCase(GenericDocumentViewTestCase): self.document.latest_version.file_metadata_drivers.all().delete() response = self._request_multiple_document_submit_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( self.document.latest_version.file_metadata_drivers.count(), 0 diff --git a/mayan/apps/file_metadata/views.py b/mayan/apps/file_metadata/views.py index 8456917d88..8e5a3c6a6e 100644 --- a/mayan/apps/file_metadata/views.py +++ b/mayan/apps/file_metadata/views.py @@ -47,7 +47,7 @@ class DocumentDriverListView(ExternalObjectMixin, SingleObjectListView): ) % self.external_object, } - def get_object_list(self): + def get_source_queryset(self): return self.external_object.latest_version.file_metadata_drivers.all() @@ -69,7 +69,7 @@ class DocumentVersionDriverEntryFileMetadataListView(ExternalObjectMixin, Single }, } - def get_object_list(self): + def get_source_queryset(self): return self.external_object.entries.all() diff --git a/mayan/apps/linking/tests/test_views.py b/mayan/apps/linking/tests/test_views.py index 2f47f8a37b..7546ccfd90 100644 --- a/mayan/apps/linking/tests/test_views.py +++ b/mayan/apps/linking/tests/test_views.py @@ -39,7 +39,7 @@ class SmartLinkViewTestCase(SmartLinkTestMixin, SmartLinkViewTestMixin, GenericV self._create_test_smart_link() response = self._request_test_smart_link_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(SmartLink.objects.count(), 1) @@ -58,7 +58,7 @@ class SmartLinkViewTestCase(SmartLinkTestMixin, SmartLinkViewTestMixin, GenericV self._create_test_smart_link() response = self._request_test_smart_link_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_smart_link.refresh_from_db() self.assertEqual(self.test_smart_link.label, TEST_SMART_LINK_LABEL) diff --git a/mayan/apps/linking/views.py b/mayan/apps/linking/views.py index eb50adc54a..9d9f72c04a 100644 --- a/mayan/apps/linking/views.py +++ b/mayan/apps/linking/views.py @@ -197,7 +197,7 @@ class SmartLinkListView(SingleObjectListView): 'title': _('Smart links'), } - def get_object_list(self): + def get_source_queryset(self): return self.get_smart_link_queryset() def get_smart_link_queryset(self): @@ -311,7 +311,7 @@ class SmartLinkConditionListView(SingleObjectListView): ) % self.get_smart_link(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_smart_link().conditions.all() def get_smart_link(self): diff --git a/mayan/apps/mailer/tests/test_views.py b/mayan/apps/mailer/tests/test_views.py index 662cf5ff88..95b8443b9b 100644 --- a/mayan/apps/mailer/tests/test_views.py +++ b/mayan/apps/mailer/tests/test_views.py @@ -26,10 +26,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV self._create_test_user_mailer() response = self._request_test_document_link_send_view() - - self.assertContains( - response=response, text='Select a valid choice', status_code=200 - ) + self.assertEqual(response.status_code, 404) def test_mail_link_view_with_access(self): self._create_test_user_mailer() @@ -41,7 +38,9 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV obj=self.test_user_mailer, permission=permission_user_mailer_use ) - self._request_test_document_link_send_view() + response = self._request_test_document_link_send_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) @@ -50,9 +49,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV self._create_test_user_mailer() response = self._request_test_document_send_view() - self.assertContains( - response=response, text='Select a valid choice', status_code=200 - ) + self.assertEqual(response.status_code, 404) def test_mail_document_view_with_access(self): self._create_test_user_mailer() @@ -64,7 +61,9 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV obj=self.test_user_mailer, permission=permission_user_mailer_use ) - self._request_test_document_send_view() + response = self._request_test_document_send_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) @@ -92,7 +91,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV self._create_test_user_mailer() response = self._request_test_user_mailer_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertQuerysetEqual( UserMailer.objects.all(), (repr(self.test_user_mailer),) @@ -150,6 +149,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV response = self._request_test_user_mailer_test_view() self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) @@ -164,6 +164,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA response = self._request_test_user_mailer_test_view() self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual( @@ -180,6 +181,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED response = self._request_test_user_mailer_test_view() self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual( @@ -194,8 +196,10 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON + response = self._request_test_user_mailer_test_view() self.assertEqual(response.status_code, 302) + self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual( @@ -213,6 +217,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA + response = self._request_test_document_link_send_view() self.assertEqual(response.status_code, 302) @@ -233,6 +238,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED + response = self._request_test_document_link_send_view() self.assertEqual(response.status_code, 302) @@ -253,6 +259,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON + response = self._request_test_document_link_send_view() self.assertEqual(response.status_code, 302) @@ -273,6 +280,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_COMMA + response = self._request_test_document_send_view() self.assertEqual(response.status_code, 302) @@ -293,6 +301,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_MIXED + response = self._request_test_document_send_view() self.assertEqual(response.status_code, 302) @@ -313,6 +322,7 @@ class MailerViewsTestCase(MailerTestMixin, MailerViewTestMixin, GenericDocumentV ) self.test_email_address = TEST_RECIPIENTS_MULTIPLE_SEMICOLON + response = self._request_test_document_send_view() self.assertEqual(response.status_code, 302) diff --git a/mayan/apps/mailer/views.py b/mayan/apps/mailer/views.py index 5413763652..d524828dd6 100644 --- a/mayan/apps/mailer/views.py +++ b/mayan/apps/mailer/views.py @@ -211,7 +211,7 @@ class UserMailerLogEntryListView(SingleObjectListView): 'title': _('%s error log') % self.get_user_mailer(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_user_mailer().error_log.all() def get_user_mailer(self): diff --git a/mayan/apps/mayan_statistics/views.py b/mayan/apps/mayan_statistics/views.py index 1b82c1ea38..b5846a913d 100644 --- a/mayan/apps/mayan_statistics/views.py +++ b/mayan/apps/mayan_statistics/views.py @@ -20,7 +20,7 @@ class NamespaceListView(SingleObjectListView): template_name = 'appearance/generic_list.html' view_permission = permission_statistics_view - def get_object_list(self): + def get_source_queryset(self): return StatisticNamespace.get_all() @@ -37,7 +37,7 @@ class NamespaceDetailView(SingleObjectListView): def get_namespace(self): return StatisticNamespace.get(slug=self.kwargs['slug']) - def get_object_list(self): + def get_source_queryset(self): return self.get_namespace().statistics diff --git a/mayan/apps/metadata/api_views.py b/mayan/apps/metadata/api_views.py index 2a87e8e3ff..e343caff59 100644 --- a/mayan/apps/metadata/api_views.py +++ b/mayan/apps/metadata/api_views.py @@ -14,8 +14,8 @@ from mayan.apps.rest_api.permissions import MayanPermission from .models import MetadataType from .permissions import ( - permission_metadata_document_add, permission_metadata_document_remove, - permission_metadata_document_edit, permission_metadata_document_view, + permission_document_metadata_add, permission_document_metadata_remove, + permission_document_metadata_edit, permission_document_metadata_view, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -34,9 +34,9 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView): """ def get_document(self): if self.request.method == 'GET': - permission_required = permission_metadata_document_view + permission_required = permission_document_metadata_view else: - permission_required = permission_metadata_document_add + permission_required = permission_document_metadata_add document = get_object_or_404( klass=Document, pk=self.kwargs['document_pk'] @@ -90,13 +90,13 @@ class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView): def get_document(self): if self.request.method == 'GET': - permission_required = permission_metadata_document_view + permission_required = permission_document_metadata_view elif self.request.method == 'PUT': - permission_required = permission_metadata_document_edit + permission_required = permission_document_metadata_edit elif self.request.method == 'PATCH': - permission_required = permission_metadata_document_edit + permission_required = permission_document_metadata_edit elif self.request.method == 'DELETE': - permission_required = permission_metadata_document_remove + permission_required = permission_document_metadata_remove document = get_object_or_404( klass=Document, pk=self.kwargs['document_pk'] diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py index 7aeaf617db..9aae81f458 100644 --- a/mayan/apps/metadata/apps.py +++ b/mayan/apps/metadata/apps.py @@ -49,8 +49,8 @@ from .links import ( link_setup_metadata_type_edit, link_setup_metadata_type_list, ) from .permissions import ( - permission_metadata_document_add, permission_metadata_document_edit, - permission_metadata_document_remove, permission_metadata_document_view, + permission_document_metadata_add, permission_document_metadata_edit, + permission_document_metadata_remove, permission_document_metadata_view, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -139,10 +139,10 @@ class MetadataApp(MayanAppConfig): ModelPermission.register( model=Document, permissions=( - permission_metadata_document_add, - permission_metadata_document_edit, - permission_metadata_document_remove, - permission_metadata_document_view, + permission_document_metadata_add, + permission_document_metadata_edit, + permission_document_metadata_remove, + permission_document_metadata_view, ) ) ModelPermission.register( diff --git a/mayan/apps/metadata/links.py b/mayan/apps/metadata/links.py index 947bf08022..faae1c6e48 100644 --- a/mayan/apps/metadata/links.py +++ b/mayan/apps/metadata/links.py @@ -6,8 +6,8 @@ from mayan.apps.documents.permissions import permission_document_type_edit from mayan.apps.navigation.classes import Link from .permissions import ( - permission_metadata_document_add, permission_metadata_document_edit, - permission_metadata_document_remove, permission_metadata_document_view, + permission_document_metadata_add, permission_document_metadata_edit, + permission_document_metadata_remove, permission_document_metadata_view, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -15,13 +15,13 @@ from .permissions import ( link_metadata_add = Link( args='object.pk', icon_class_path='mayan.apps.metadata.icons.icon_document_metadata_add', - permissions=(permission_metadata_document_add,), text=_('Add metadata'), + permissions=(permission_document_metadata_add,), text=_('Add metadata'), view='metadata:metadata_add', ) link_metadata_edit = Link( args='object.pk', icon_class_path='mayan.apps.metadata.icons.icon_document_metadata_edit', - permissions=(permission_metadata_document_edit,), + permissions=(permission_document_metadata_edit,), text=_('Edit metadata'), view='metadata:metadata_edit' ) link_metadata_multiple_add = Link( @@ -39,13 +39,13 @@ link_metadata_multiple_remove = Link( link_metadata_remove = Link( args='object.pk', icon_class_path='mayan.apps.metadata.icons.icon_document_metadata_remove', - permissions=(permission_metadata_document_remove,), + permissions=(permission_document_metadata_remove,), text=_('Remove metadata'), view='metadata:metadata_remove', ) link_metadata_view = Link( args='resolved_object.pk', icon_class_path='mayan.apps.metadata.icons.icon_document_metadata_view', - permissions=(permission_metadata_document_view,), text=_('Metadata'), + permissions=(permission_document_metadata_view,), text=_('Metadata'), view='metadata:metadata_view', ) link_setup_document_type_metadata_types = Link( diff --git a/mayan/apps/metadata/permissions.py b/mayan/apps/metadata/permissions.py index 03904f03c5..999a3f6937 100644 --- a/mayan/apps/metadata/permissions.py +++ b/mayan/apps/metadata/permissions.py @@ -6,17 +6,17 @@ from mayan.apps.permissions import PermissionNamespace namespace = PermissionNamespace(label=_('Metadata'), name='metadata') -permission_metadata_document_edit = namespace.add_permission( - label=_('Edit a document\'s metadata'), name='metadata_document_edit' -) -permission_metadata_document_add = namespace.add_permission( +permission_document_metadata_add = namespace.add_permission( label=_('Add metadata to a document'), name='metadata_document_add' ) -permission_metadata_document_remove = namespace.add_permission( +permission_document_metadata_edit = namespace.add_permission( + label=_('Edit a document\'s metadata'), name='metadata_document_edit' +) +permission_document_metadata_remove = namespace.add_permission( label=_('Remove metadata from a document'), name='metadata_document_remove' ) -permission_metadata_document_view = namespace.add_permission( +permission_document_metadata_view = namespace.add_permission( label=_('View metadata from a document'), name='metadata_document_view' ) diff --git a/mayan/apps/metadata/tests/mixins.py b/mayan/apps/metadata/tests/mixins.py index 1a4ae6206f..9caa6925f6 100644 --- a/mayan/apps/metadata/tests/mixins.py +++ b/mayan/apps/metadata/tests/mixins.py @@ -11,19 +11,19 @@ from .literals import ( class MetadataTypeTestMixin(object): def setUp(self): super(MetadataTypeTestMixin, self).setUp() - self.metadata_type = MetadataType.objects.create( + self.test_metadata_type = MetadataType.objects.create( name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL ) class MetadataTestsMixin(object): - def _create_metadata_type(self): - self.metadata_type = MetadataType.objects.create( + def _create_test_metadata_type(self): + self.test_metadata_type = MetadataType.objects.create( label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME ) - def _request_metadata_type_create_view(self): + def _request_test_metadata_type_create_view(self): return self.post( viewname='metadata:setup_metadata_type_create', data={ 'label': TEST_METADATA_TYPE_LABEL, @@ -31,30 +31,30 @@ class MetadataTestsMixin(object): } ) - def _request_metadata_type_delete_view(self): + def _request_test_metadata_type_delete_view(self): return self.post( viewname='metadata:setup_metadata_type_delete', kwargs={ - 'pk': self.metadata_type.pk + 'pk': self.test_metadata_type.pk } ) - def _request_metadata_type_edit_view(self): + def _request_test_metadata_type_edit_view(self): return self.post( viewname='metadata:setup_metadata_type_edit', kwargs={ - 'pk': self.metadata_type.pk + 'pk': self.test_metadata_type.pk }, data={ 'label': TEST_METADATA_TYPE_LABEL_EDITED, 'name': TEST_METADATA_TYPE_NAME_EDITED } ) - def _request_metadata_type_relationship_edit_view(self): + def _request_test_metadata_type_relationship_edit_view(self): # This request assumes there is only one document type and # blindly sets the first form of the formset. return self.post( viewname='metadata:setup_metadata_type_document_types', - kwargs={'pk': self.metadata_type.pk}, data={ + kwargs={'pk': self.test_metadata_type.pk}, data={ 'form-TOTAL_FORMS': '1', 'form-INITIAL_FORMS': '0', 'form-0-relationship_type': 'required' diff --git a/mayan/apps/metadata/tests/test_api.py b/mayan/apps/metadata/tests/test_api.py index 763529453f..be845b8174 100644 --- a/mayan/apps/metadata/tests/test_api.py +++ b/mayan/apps/metadata/tests/test_api.py @@ -10,8 +10,8 @@ from mayan.apps.rest_api.tests import BaseAPITestCase from ..models import DocumentTypeMetadataType, MetadataType from ..permissions import ( - permission_metadata_document_add, permission_metadata_document_edit, - permission_metadata_document_remove, permission_metadata_document_view, + permission_document_metadata_add, permission_document_metadata_edit, + permission_document_metadata_remove, permission_document_metadata_view, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -24,12 +24,12 @@ from .literals import ( class MetadataTypeAPITestCase(BaseAPITestCase): - def _create_metadata_type(self): - self.metadata_type = MetadataType.objects.create( + def _create_test_metadata_type(self): + self.test_metadata_type = MetadataType.objects.create( label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME ) - def _request_metadata_type_create_view(self): + def _request_test_metadata_type_create_view(self): return self.post( viewname='rest_api:metadatatype-list', data={ 'label': TEST_METADATA_TYPE_LABEL, @@ -38,13 +38,13 @@ class MetadataTypeAPITestCase(BaseAPITestCase): ) def test_metadata_type_create_no_permission(self): - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(MetadataType.objects.count(), 0) def test_metadata_type_create_with_permission(self): self.grant_permission(permission=permission_metadata_type_create) - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, status.HTTP_201_CREATED) metadata_type = MetadataType.objects.first() @@ -55,26 +55,26 @@ class MetadataTypeAPITestCase(BaseAPITestCase): self.assertEqual(metadata_type.label, TEST_METADATA_TYPE_LABEL) self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME) - def _request_metadata_type_delete_view(self): + def _request_test_metadata_type_delete_view(self): return self.delete( viewname='rest_api:metadatatype-detail', - kwargs={'metadata_type_pk': self.metadata_type.pk} + kwargs={'metadata_type_pk': self.test_metadata_type.pk} ) def test_metadata_type_delete_no_access(self): - self._create_metadata_type() - response = self._request_metadata_type_delete_view() + self._create_test_metadata_type() + response = self._request_test_metadata_type_delete_view() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(MetadataType.objects.count(), 1) def test_metadata_type_delete_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_delete + obj=self.test_metadata_type, permission=permission_metadata_type_delete ) - response = self._request_metadata_type_delete_view() + response = self._request_test_metadata_type_delete_view() self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(MetadataType.objects.count(), 0) @@ -82,19 +82,19 @@ class MetadataTypeAPITestCase(BaseAPITestCase): def _request_metadata_type_detail_view(self): return self.get( viewname='rest_api:metadatatype-detail', - kwargs={'metadata_type_pk': self.metadata_type.pk} + kwargs={'metadata_type_pk': self.test_metadata_type.pk} ) def test_metadata_type_detail_view_no_access(self): - self._create_metadata_type() + self._create_test_metadata_type() response = self._request_metadata_type_detail_view() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) def test_metadata_type_detail_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_view + obj=self.test_metadata_type, permission=permission_metadata_type_view ) response = self._request_metadata_type_detail_view() @@ -104,83 +104,83 @@ class MetadataTypeAPITestCase(BaseAPITestCase): response.data['label'], TEST_METADATA_TYPE_LABEL ) - def _request_metadata_type_edit_view_via_patch(self): + def _request_test_metadata_type_edit_view_via_patch(self): return self.patch( viewname='rest_api:metadatatype-detail', - kwargs={'metadata_type_pk': self.metadata_type.pk}, data={ + kwargs={'metadata_type_pk': self.test_metadata_type.pk}, data={ 'label': TEST_METADATA_TYPE_LABEL_2, 'name': TEST_METADATA_TYPE_NAME_2 } ) def test_metadata_type_patch_view_no_access(self): - self._create_metadata_type() + self._create_test_metadata_type() - response = self._request_metadata_type_edit_view_via_patch() + response = self._request_test_metadata_type_edit_view_via_patch() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.metadata_type.refresh_from_db() - self.assertEqual(self.metadata_type.label, TEST_METADATA_TYPE_LABEL) - self.assertEqual(self.metadata_type.name, TEST_METADATA_TYPE_NAME) + self.test_metadata_type.refresh_from_db() + self.assertEqual(self.test_metadata_type.label, TEST_METADATA_TYPE_LABEL) + self.assertEqual(self.test_metadata_type.name, TEST_METADATA_TYPE_NAME) def test_metadata_type_patch_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_edit + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) - response = self._request_metadata_type_edit_view_via_patch() + response = self._request_test_metadata_type_edit_view_via_patch() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.metadata_type.refresh_from_db() - self.assertEqual(self.metadata_type.label, TEST_METADATA_TYPE_LABEL_2) - self.assertEqual(self.metadata_type.name, TEST_METADATA_TYPE_NAME_2) + self.test_metadata_type.refresh_from_db() + self.assertEqual(self.test_metadata_type.label, TEST_METADATA_TYPE_LABEL_2) + self.assertEqual(self.test_metadata_type.name, TEST_METADATA_TYPE_NAME_2) - def _request_metadata_type_edit_view_via_put(self): + def _request_test_metadata_type_edit_view_via_put(self): return self.put( viewname='rest_api:metadatatype-detail', - kwargs={'metadata_type_pk': self.metadata_type.pk}, data={ + kwargs={'metadata_type_pk': self.test_metadata_type.pk}, data={ 'label': TEST_METADATA_TYPE_LABEL_2, 'name': TEST_METADATA_TYPE_NAME_2 } ) def test_metadata_type_put_view_no_access(self): - self._create_metadata_type() - response = self._request_metadata_type_edit_view_via_put() + self._create_test_metadata_type() + response = self._request_test_metadata_type_edit_view_via_put() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.metadata_type.refresh_from_db() - self.assertEqual(self.metadata_type.label, TEST_METADATA_TYPE_LABEL) - self.assertEqual(self.metadata_type.name, TEST_METADATA_TYPE_NAME) + self.test_metadata_type.refresh_from_db() + self.assertEqual(self.test_metadata_type.label, TEST_METADATA_TYPE_LABEL) + self.assertEqual(self.test_metadata_type.name, TEST_METADATA_TYPE_NAME) def test_metadata_type_put_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_edit + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) - response = self._request_metadata_type_edit_view_via_put() + response = self._request_test_metadata_type_edit_view_via_put() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.metadata_type.refresh_from_db() - self.assertEqual(self.metadata_type.label, TEST_METADATA_TYPE_LABEL_2) - self.assertEqual(self.metadata_type.name, TEST_METADATA_TYPE_NAME_2) + self.test_metadata_type.refresh_from_db() + self.assertEqual(self.test_metadata_type.label, TEST_METADATA_TYPE_LABEL_2) + self.assertEqual(self.test_metadata_type.name, TEST_METADATA_TYPE_NAME_2) def _request_metadata_type_list_view(self): return self.get(viewname='rest_api:metadatatype-list') def test_metadata_type_list_view_no_access(self): - self._create_metadata_type() + self._create_test_metadata_type() response = self._request_metadata_type_list_view() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 0) def test_metadata_type_list_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_view + obj=self.test_metadata_type, permission=permission_metadata_type_view ) response = self._request_metadata_type_list_view() @@ -195,20 +195,20 @@ class DocumentTypeMetadataTypeAPITestCase(DocumentTestMixin, BaseAPITestCase): def setUp(self): super(DocumentTypeMetadataTypeAPITestCase, self).setUp() - self.metadata_type = MetadataType.objects.create( + self.test_metadata_type = MetadataType.objects.create( label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME ) def _create_document_type_metadata_type(self): self.test_document_type_metadata_type = self.test_document_type.metadata.create( - metadata_type=self.metadata_type, required=False + metadata_type=self.test_metadata_type, required=False ) def _request_document_type_metadata_type_create_view(self): return self.post( viewname='rest_api:documenttypemetadatatype-list', kwargs={'document_type_pk': self.test_document_type.pk}, data={ - 'metadata_type_pk': self.metadata_type.pk, 'required': False + 'metadata_type_pk': self.test_metadata_type.pk, 'required': False } ) @@ -363,23 +363,23 @@ class DocumentTypeMetadataTypeAPITestCase(DocumentTestMixin, BaseAPITestCase): class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): def setUp(self): super(DocumentMetadataAPITestCase, self).setUp() - self.metadata_type = MetadataType.objects.create( + self.test_metadata_type = MetadataType.objects.create( label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME ) self.test_document_type.metadata.create( - metadata_type=self.metadata_type, required=False + metadata_type=self.test_metadata_type, required=False ) def _create_document_metadata(self): self.test_document_metadata = self.test_document.metadata.create( - metadata_type=self.metadata_type, value=TEST_METADATA_VALUE + metadata_type=self.test_metadata_type, value=TEST_METADATA_VALUE ) def _request_document_metadata_create_view(self): return self.post( viewname='rest_api:documentmetadata-list', kwargs={'document_pk': self.test_document.pk}, data={ - 'metadata_type_pk': self.metadata_type.pk, + 'metadata_type_pk': self.test_metadata_type.pk, 'value': TEST_METADATA_VALUE } ) @@ -392,7 +392,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): def test_document_metadata_create_view_with_access(self): self.grant_access( - obj=self.test_document, permission=permission_metadata_document_add + obj=self.test_document, permission=permission_document_metadata_add ) response = self._request_document_metadata_create_view() @@ -400,21 +400,21 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): document_metadata = self.test_document.metadata.first() self.assertEqual(response.data['id'], document_metadata.pk) - self.assertEqual(document_metadata.metadata_type, self.metadata_type) + self.assertEqual(document_metadata.metadata_type, self.test_metadata_type) self.assertEqual(document_metadata.value, TEST_METADATA_VALUE) def test_document_metadata_create_duplicate_view(self): self._create_document_metadata() - self.grant_permission(permission=permission_metadata_document_add) + self.grant_permission(permission=permission_document_metadata_add) response = self._request_document_metadata_create_view() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(list(response.data.keys())[0], 'non_field_errors') def test_document_metadata_create_invalid_lookup_value_view(self): - self.metadata_type.lookup = 'invalid,lookup,values,on,purpose' - self.metadata_type.save() - self.grant_permission(permission=permission_metadata_document_add) + self.test_metadata_type.lookup = 'invalid,lookup,values,on,purpose' + self.test_metadata_type.save() + self.grant_permission(permission=permission_document_metadata_add) response = self._request_document_metadata_create_view() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -440,7 +440,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): self._create_document_metadata() self.grant_access( obj=self.test_document, - permission=permission_metadata_document_remove + permission=permission_document_metadata_remove ) response = self._request_document_metadata_delete_view() @@ -464,7 +464,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): self._create_document_metadata() self.grant_access( obj=self.test_document, - permission=permission_metadata_document_view + permission=permission_document_metadata_view ) response = self._request_document_metadata_list_view() @@ -474,7 +474,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): ) self.assertEqual( response.data['results'][0]['metadata_type']['id'], - self.metadata_type.pk + self.test_metadata_type.pk ) self.assertEqual( response.data['results'][0]['value'], TEST_METADATA_VALUE @@ -507,7 +507,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): self._create_document_metadata() self.grant_access( obj=self.test_document, - permission=permission_metadata_document_edit + permission=permission_document_metadata_edit ) response = self._request_document_metadata_edit_view_via_patch() @@ -545,7 +545,7 @@ class DocumentMetadataAPITestCase(DocumentTestMixin, BaseAPITestCase): self._create_document_metadata() self.grant_access( obj=self.test_document, - permission=permission_metadata_document_edit + permission=permission_document_metadata_edit ) response = self._request_document_metadata_edit_view_via_put() diff --git a/mayan/apps/metadata/tests/test_events.py b/mayan/apps/metadata/tests/test_events.py index cfb730eb34..fa8b759bd8 100644 --- a/mayan/apps/metadata/tests/test_events.py +++ b/mayan/apps/metadata/tests/test_events.py @@ -19,7 +19,7 @@ class MetadataTypeEventsTestCase(MetadataTestsMixin, GenericViewTestCase): def test_metadata_type_create_event_no_permissions(self): Action.objects.all().delete() - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, 403) self.assertEqual(Action.objects.count(), 0) @@ -29,7 +29,7 @@ class MetadataTypeEventsTestCase(MetadataTestsMixin, GenericViewTestCase): self.grant_permission(permission=permission_metadata_type_create) - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, 302) event = Action.objects.first() @@ -41,30 +41,30 @@ class MetadataTypeEventsTestCase(MetadataTestsMixin, GenericViewTestCase): self.assertEqual(event.verb, event_metadata_type_created.id) def test_metadata_type_edit_event_no_permissions(self): - self._create_metadata_type() + self._create_test_metadata_type() Action.objects.all().delete() - response = self._request_metadata_type_edit_view() - self.assertEqual(response.status_code, 403) + response = self._request_test_metadata_type_edit_view() + self.assertEqual(response.status_code, 404) self.assertEqual(Action.objects.count(), 0) def test_metadata_type_edit_event_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() Action.objects.all().delete() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_edit + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) - response = self._request_metadata_type_edit_view() + response = self._request_test_metadata_type_edit_view() self.assertEqual(response.status_code, 302) event = Action.objects.first() self.assertEqual(event.actor, self._test_case_user) - self.assertEqual(event.target, self.metadata_type) + self.assertEqual(event.target, self.test_metadata_type) self.assertEqual(event.verb, event_metadata_type_edited.id) diff --git a/mayan/apps/metadata/tests/test_models.py b/mayan/apps/metadata/tests/test_models.py index 75892e95f3..7aea6dc03f 100644 --- a/mayan/apps/metadata/tests/test_models.py +++ b/mayan/apps/metadata/tests/test_models.py @@ -21,12 +21,12 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): def setUp(self): super(MetadataTestCase, self).setUp() self.test_document_type.metadata.create( - metadata_type=self.metadata_type + metadata_type=self.test_metadata_type ) def test_no_default(self): document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type + document=self.test_document, metadata_type=self.test_metadata_type ) document_metadata.full_clean() @@ -35,11 +35,11 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): self.assertEqual(self.test_document.metadata_value_of.test, None) def test_default(self): - self.metadata_type.default = TEST_DEFAULT_VALUE - self.metadata_type.save() + self.test_metadata_type.default = TEST_DEFAULT_VALUE + self.test_metadata_type.save() document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type + document=self.test_document, metadata_type=self.test_metadata_type ) document_metadata.full_clean() @@ -50,11 +50,11 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): ) def test_lookup_with_incorrect_value(self): - self.metadata_type.lookup = TEST_LOOKUP_TEMPLATE - self.metadata_type.save() + self.test_metadata_type.lookup = TEST_LOOKUP_TEMPLATE + self.test_metadata_type.save() document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_INCORRECT_LOOKUP_VALUE ) @@ -64,11 +64,11 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): document_metadata.save() def test_lookup_with_correct_value(self): - self.metadata_type.lookup = TEST_LOOKUP_TEMPLATE - self.metadata_type.save() + self.test_metadata_type.lookup = TEST_LOOKUP_TEMPLATE + self.test_metadata_type.save() document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_CORRECT_LOOKUP_VALUE ) @@ -84,21 +84,21 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): Checks for GitLab issue #250 Empty optional lookup metadata trigger validation error """ - self.metadata_type.lookup = TEST_LOOKUP_TEMPLATE - self.metadata_type.save() + self.test_metadata_type.lookup = TEST_LOOKUP_TEMPLATE + self.test_metadata_type.save() document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type + document=self.test_document, metadata_type=self.test_metadata_type ) document_metadata.full_clean() document_metadata.save() def test_validation(self): - self.metadata_type.validation = TEST_DATE_VALIDATOR + self.test_metadata_type.validation = TEST_DATE_VALIDATOR document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_INVALID_DATE ) @@ -115,10 +115,10 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): self.assertEqual(self.test_document.metadata_value_of.test, TEST_VALID_DATE) def test_parsing(self): - self.metadata_type.parser = TEST_DATE_PARSER + self.test_metadata_type.parser = TEST_DATE_PARSER document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_INVALID_DATE ) @@ -140,53 +140,53 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): self.test_document_type.metadata.all().delete() self.assertFalse( - self.metadata_type.get_required_for(self.test_document_type) + self.test_metadata_type.get_required_for(self.test_document_type) ) self.test_document_type.metadata.create( - metadata_type=self.metadata_type, required=False + metadata_type=self.test_metadata_type, required=False ) self.assertFalse( - self.metadata_type.get_required_for(self.test_document_type) + self.test_metadata_type.get_required_for(self.test_document_type) ) self.test_document_type.metadata.all().delete() self.test_document_type.metadata.create( - metadata_type=self.metadata_type, required=True + metadata_type=self.test_metadata_type, required=True ) self.assertTrue( - self.metadata_type.get_required_for(self.test_document_type) + self.test_metadata_type.get_required_for(self.test_document_type) ) def test_unicode_lookup(self): # Should NOT return a ValidationError, otherwise test fails - self.metadata_type.lookup = '测试1,测试2,test1,test2' - self.metadata_type.save() - self.metadata_type.validate_value(document_type=None, value='测试1') + self.test_metadata_type.lookup = '测试1,测试2,test1,test2' + self.test_metadata_type.save() + self.test_metadata_type.validate_value(document_type=None, value='测试1') def test_non_unicode_lookup(self): # Should NOT return a ValidationError, otherwise test fails - self.metadata_type.lookup = 'test1,test2' - self.metadata_type.save() - self.metadata_type.validate_value(document_type=None, value='test1') + self.test_metadata_type.lookup = 'test1,test2' + self.test_metadata_type.save() + self.test_metadata_type.validate_value(document_type=None, value='test1') def test_add_new_metadata_type_on_document_type_change(self): """ When switching document types, add the required metadata of the new document type, the value to the default of the metadata type. """ - self.metadata_type.default = TEST_DEFAULT_VALUE - self.metadata_type.save() + self.test_metadata_type.default = TEST_DEFAULT_VALUE + self.test_metadata_type.save() self.test_document_type_2 = DocumentType.objects.create( label=TEST_DOCUMENT_TYPE_2_LABEL ) self.test_document_type_2.metadata.create( - metadata_type=self.metadata_type, required=True + metadata_type=self.test_metadata_type, required=True ) self.test_document.set_document_type(document_type=self.test_document_type_2) @@ -202,7 +202,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): old and new document types """ document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_DEFAULT_VALUE ) @@ -213,7 +213,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): label=TEST_DOCUMENT_TYPE_2_LABEL ) - self.test_document_type_2.metadata.create(metadata_type=self.metadata_type) + self.test_document_type_2.metadata.create(metadata_type=self.test_metadata_type) self.test_document.set_document_type(document_type=self.test_document_type_2) @@ -222,7 +222,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): self.test_document.metadata.first().value, TEST_DEFAULT_VALUE ) self.assertEqual( - self.test_document.metadata.first().metadata_type, self.metadata_type + self.test_document.metadata.first().metadata_type, self.test_metadata_type ) def test_delete_metadata_value_on_document_type_change(self): @@ -231,7 +231,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): new document type """ document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_DEFAULT_VALUE ) @@ -252,7 +252,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): new document type """ document_metadata = DocumentMetadata( - document=self.test_document, metadata_type=self.metadata_type, + document=self.test_document, metadata_type=self.test_metadata_type, value=TEST_DEFAULT_VALUE ) @@ -264,7 +264,7 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): ) self.test_document_type_2.metadata.create( - metadata_type=self.metadata_type, required=True + metadata_type=self.test_metadata_type, required=True ) self.test_document.set_document_type(document_type=self.test_document_type_2) @@ -274,5 +274,5 @@ class MetadataTestCase(DocumentTestMixin, MetadataTypeTestMixin, BaseTestCase): self.test_document.metadata.first().value, TEST_DEFAULT_VALUE ) self.assertEqual( - self.test_document.metadata.first().metadata_type, self.metadata_type + self.test_document.metadata.first().metadata_type, self.test_metadata_type ) diff --git a/mayan/apps/metadata/tests/test_views.py b/mayan/apps/metadata/tests/test_views.py index d9345ed36c..55a41df41c 100644 --- a/mayan/apps/metadata/tests/test_views.py +++ b/mayan/apps/metadata/tests/test_views.py @@ -1,7 +1,5 @@ from __future__ import unicode_literals -from django.core.files.base import File - from mayan.apps.common.tests import GenericViewTestCase from mayan.apps.documents.models import DocumentType from mayan.apps.documents.permissions import ( @@ -9,14 +7,13 @@ from mayan.apps.documents.permissions import ( permission_document_view ) from mayan.apps.documents.tests import ( - DocumentTestMixin, GenericDocumentViewTestCase, - TEST_DOCUMENT_TYPE_2_LABEL, TEST_SMALL_DOCUMENT_PATH, + DocumentTestMixin, GenericDocumentViewTestCase, TEST_DOCUMENT_TYPE_2_LABEL ) from ..models import MetadataType from ..permissions import ( - permission_metadata_document_add, permission_metadata_document_remove, - permission_metadata_document_edit, permission_metadata_type_create, + permission_document_metadata_add, permission_document_metadata_remove, + permission_document_metadata_edit, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -34,50 +31,102 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase): def setUp(self): super(DocumentMetadataTestCase, self).setUp() - self.metadata_type = MetadataType.objects.create( + self.test_metadata_type = MetadataType.objects.create( name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL ) self.test_document_type.metadata.create( - metadata_type=self.metadata_type + metadata_type=self.test_metadata_type ) - def test_metadata_add_view_no_permission(self): - self.grant_permission(permission=permission_document_view) - - response = self.post( + def _request_test_document_metadata_add_post_view(self): + return self.post( viewname='metadata:metadata_add', kwargs={ 'pk': self.test_document.pk - }, data={'metadata_type': self.metadata_type.pk} - ) - self.assertNotContains( - response, text=self.metadata_type.label, status_code=200 + }, data={'metadata_type': self.test_metadata_type.pk} ) - self.assertEqual(len(self.test_document.metadata.all()), 0) + def test_document_metadata_add_post_view_no_permission(self): + response = self._request_test_document_metadata_add_post_view() + self.assertEqual(response.status_code, 404) - def test_metadata_add_view_with_permission(self): + self.assertEqual(self.test_document.metadata.count(), 0) + + def test_document_metadata_add_post_view_with_document_access(self): + self.grant_access( + obj=self.test_document, permission=permission_document_metadata_add + ) + + response = self._request_test_document_metadata_add_post_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(self.test_document.metadata.count(), 1) + + def _request_test_document_multiple_metadata_add_post_view(self): + return self.post( + viewname='metadata:metadata_multiple_add', data={ + 'id_list': '{},{}'.format( + self.test_documents[0].pk, self.test_documents[1].pk + ), + 'metadata_type': self.test_metadata_type.pk + } + ) + + def test_document_multiple_metadata_add_post_view_with_document_access(self): + self.upload_document() + + self.grant_access( + obj=self.test_documents[0], + permission=permission_document_metadata_add + ) + self.grant_access( + obj=self.test_documents[1], + permission=permission_document_metadata_add + ) + + response = self._request_test_document_multiple_metadata_add_post_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(self.test_documents[0].metadata.count(), 1) + self.assertEqual(self.test_documents[1].metadata.count(), 1) + + def test_single_document_multiple_metadata_add_view(self): self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_add) - self.grant_permission(permission=permission_metadata_document_edit) + self.grant_permission(permission=permission_document_metadata_add) + self.grant_permission(permission=permission_document_metadata_edit) - response = self.post( + metadata_type_2 = MetadataType.objects.create( + name=TEST_METADATA_TYPE_NAME_2, label=TEST_METADATA_TYPE_LABEL_2 + ) + + self.test_document_type.metadata.create( + metadata_type=metadata_type_2 + ) + + self.post( viewname='metadata:metadata_add', kwargs={ 'pk': self.test_document.pk - }, data={'metadata_type': self.metadata_type.pk}, follow=True - ) - self.assertContains( - response=response, text='successfully', status_code=200 + }, data={ + 'metadata_type': [self.test_metadata_type.pk, metadata_type_2.pk], + } ) - self.assertEqual(len(self.test_document.metadata.all()), 1) + document_metadata_types = self.test_document.metadata.values_list( + 'metadata_type', flat=True + ) + self.assertTrue( + self.test_metadata_type.pk in document_metadata_types + ) + self.assertTrue( + metadata_type_2.pk in document_metadata_types + ) def test_metadata_edit_after_document_type_change(self): # Gitlab issue #204 # Problems to add required metadata after changing the document type self.grant_permission(permission=permission_document_properties_edit) - self.grant_permission(permission=permission_metadata_document_edit) + self.grant_permission(permission=permission_document_metadata_edit) document_type_2 = DocumentType.objects.create( label=TEST_DOCUMENT_TYPE_2_LABEL @@ -120,101 +169,142 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase): TEST_DOCUMENT_METADATA_VALUE_2 ) - def test_metadata_remove_view_no_permission(self): - document_metadata = self.test_document.metadata.create( - metadata_type=self.metadata_type, value='' + def _request_document_metadata_remove_get_view(self): + return self.get( + viewname='metadata:metadata_remove', + kwargs={'pk': self.test_document.pk} ) - self.assertEqual(len(self.test_document.metadata.all()), 1) - - self.grant_permission(permission=permission_document_view) - - # Test display of metadata removal form - response = self.get( - viewname='metadata:metadata_remove', kwargs={ - 'pk': self.test_document.pk - } + def _create_test_document_metadata(self): + self.test_document_metadata = self.test_document.metadata.create( + metadata_type=self.test_metadata_type, value='' ) + + def test_document_metadata_remove_get_view_no_permission(self): + self._create_test_document_metadata() + + response = self._request_document_metadata_remove_get_view() + self.assertNotContains( - response, text=self.metadata_type.label, status_code=200 + response=response, text=self.test_metadata_type.label, status_code=404 + ) + self.assertTrue( + self.test_document_metadata in self.test_document.metadata.all() ) - # Test post to metadata removal view - response = self.post( - viewname='metadata:metadata_remove', kwargs={ - 'pk': self.test_document.pk - }, data={ - 'form-0-id': document_metadata.metadata_type.pk, - 'form-0-update': True, - 'form-TOTAL_FORMS': '1', - 'form-INITIAL_FORMS': '0', - 'form-MAX_NUM_FORMS': '', - }, follow=True + def test_document_metadata_remove_get_view_with_full_access(self): + self._create_test_document_metadata() + + self.grant_access( + obj=self.document, permission=permission_document_metadata_remove, ) - self.assertEqual(response.status_code, 200) - self.assertEqual(len(self.test_document.metadata.all()), 1) - - def test_metadata_remove_view_with_permission(self): # Silence unrelated logging self._silence_logger(name='mayan.apps.navigation.classes') - document_metadata = self.test_document.metadata.create( - metadata_type=self.metadata_type, value='' + response = self._request_document_metadata_remove_get_view() + self.assertContains( + response, text=self.test_metadata_type.label, status_code=200 + ) + self.assertTrue( + self.test_document_metadata in self.test_document.metadata.all() ) - self.assertEqual(len(self.test_document.metadata.all()), 1) - - self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_remove) - - # Test display of metadata removal form - response = self.get( - viewname='metadata:metadata_remove', kwargs={ - 'pk': self.test_document.pk + def _request_document_metadata_remove_post_view(self): + return self.post( + viewname='metadata:metadata_remove', + kwargs={'pk': self.test_document.pk}, data={ + 'form-0-id': self.test_document_metadata.metadata_type.pk, + 'form-0-update': True, + 'form-TOTAL_FORMS': '1', + 'form-INITIAL_FORMS': '0', + 'form-MAX_NUM_FORMS': '', } ) - self.assertContains( - response=response, text=self.metadata_type.label, status_code=200 + + def test_document_metadata_remove_post_view_no_permission(self): + self._create_test_document_metadata() + + response = self._request_document_metadata_remove_post_view() + self.assertNotContains( + response=response, text=self.test_metadata_type.label, status_code=404 ) - self.assertContains(response=response, text='emove', status_code=200) + + self.assertTrue( + self.test_document_metadata in self.test_document.metadata.all() + ) + + def test_document_metadata_remove_post_view_with_access(self): + self._create_test_document_metadata() + + self.grant_access( + obj=self.test_document, + permission=permission_document_metadata_remove + ) + response = self._request_document_metadata_remove_post_view() + self.assertEqual(response.status_code, 302) + + self.assertTrue( + self.test_document_metadata not in self.test_document.metadata.all() + ) + + def test_document_multiple_metadata_remove_view_with_access(self): + self.grant_permission(permission=permission_document_view) + self.grant_permission(permission=permission_document_metadata_remove) + + self.upload_document() + + document_metadata = self.test_documents[0].metadata.create( + metadata_type=self.test_metadata_type + ) + self.test_documents[1].metadata.create(metadata_type=self.test_metadata_type) + + response = self.get( + viewname='metadata:metadata_multiple_remove', data={ + 'id_list': '{},{}'.format( + self.test_documents[0].pk, self.test_documents[0].pk + ) + } + ) + self.assertEquals(response.status_code, 200) # Test post to metadata removal view response = self.post( - viewname='metadata:metadata_remove', kwargs={ - 'pk': self.test_document.pk - }, data={ + viewname='metadata:metadata_multiple_remove', data={ + 'id_list': '{},{}'.format( + self.test_documents[0].pk, self.test_documents[1].pk + ), 'form-0-id': document_metadata.metadata_type.pk, 'form-0-update': True, 'form-TOTAL_FORMS': '1', 'form-INITIAL_FORMS': '0', 'form-MAX_NUM_FORMS': '', - }, follow=True - ) - self.assertContains( - response=response, text='Success', status_code=200 + } ) + self.assertEqual(response.status_code, 302) - self.assertEqual(len(self.test_document.metadata.all()), 0) + self.assertEqual(self.test_documents[0].metadata.count(), 0) + self.assertEqual(self.test_documents[1].metadata.count(), 0) def test_multiple_document_metadata_edit(self): + self.upload_document() + self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_add) - self.grant_permission(permission=permission_metadata_document_edit) + self.grant_permission(permission=permission_document_metadata_add) + self.grant_permission(permission=permission_document_metadata_edit) - with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: - document_2 = self.test_document_type.new_document( - file_object=File(file_object) - ) - - document_metadata = self.test_document.metadata.create( - metadata_type=self.metadata_type + document_metadata = self.test_documents[0].metadata.create( + metadata_type=self.test_metadata_type + ) + self.test_documents[1].metadata.create( + metadata_type=self.test_metadata_type ) - document_2.metadata.create(metadata_type=self.metadata_type) response = self.get( viewname='metadata:metadata_multiple_edit', data={ - 'id_list': '{},{}'.format(self.test_document.pk, document_2.pk) + 'id_list': '{},{}'.format( + self.test_documents[0].pk, self.test_documents[1].pk + ) } ) self.assertContains(response=response, text='Edit', status_code=200) @@ -222,7 +312,9 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase): # Test post to metadata removal view response = self.post( viewname='metadata:metadata_multiple_edit', data={ - 'id_list': '{},{}'.format(self.test_document.pk, document_2.pk), + 'id_list': '{},{}'.format( + self.test_documents[0].pk, self.test_documents[1].pk + ), 'form-0-id': document_metadata.metadata_type.pk, 'form-0-value': TEST_METADATA_VALUE_EDITED, 'form-0-update': True, @@ -234,96 +326,10 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase): self.assertEqual(response.status_code, 200) self.assertEqual( - self.test_document.metadata.first().value, TEST_METADATA_VALUE_EDITED + self.test_documents[0].metadata.first().value, TEST_METADATA_VALUE_EDITED ) self.assertEqual( - document_2.metadata.first().value, TEST_METADATA_VALUE_EDITED - ) - - def test_multiple_document_metadata_remove(self): - self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_remove) - - with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: - document_2 = self.test_document_type.new_document( - file_object=File(file_object) - ) - - document_metadata = self.test_document.metadata.create( - metadata_type=self.metadata_type - ) - document_2.metadata.create(metadata_type=self.metadata_type) - - response = self.get( - viewname='metadata:metadata_multiple_remove', data={ - 'id_list': '{},{}'.format(self.test_document.pk, document_2.pk) - } - ) - self.assertEquals(response.status_code, 200) - - # Test post to metadata removal view - response = self.post( - viewname='metadata:metadata_multiple_remove', data={ - 'id_list': '{},{}'.format(self.test_document.pk, document_2.pk), - 'form-0-id': document_metadata.metadata_type.pk, - 'form-0-update': True, - 'form-TOTAL_FORMS': '1', - 'form-INITIAL_FORMS': '0', - 'form-MAX_NUM_FORMS': '', - }, follow=True - ) - self.assertEqual(response.status_code, 200) - - self.assertEqual(self.test_document.metadata.count(), 0) - self.assertEqual(document_2.metadata.count(), 0) - - def test_multiple_document_metadata_add(self): - self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_add) - self.grant_permission(permission=permission_metadata_document_edit) - - with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object: - document_2 = self.test_document_type.new_document( - file_object=File(file_object) - ) - - response = self.post( - viewname='metadata:metadata_multiple_add', data={ - 'id_list': '{},{}'.format(self.test_document.pk, document_2.pk), - 'metadata_type': self.metadata_type.pk - }, follow=True - ) - self.assertContains(response, 'Edit', status_code=200) - - def test_single_document_multiple_metadata_add_view(self): - self.grant_permission(permission=permission_document_view) - self.grant_permission(permission=permission_metadata_document_add) - self.grant_permission(permission=permission_metadata_document_edit) - - metadata_type_2 = MetadataType.objects.create( - name=TEST_METADATA_TYPE_NAME_2, label=TEST_METADATA_TYPE_LABEL_2 - ) - - self.test_document_type.metadata.create( - metadata_type=metadata_type_2 - ) - - self.post( - viewname='metadata:metadata_add', kwargs={ - 'pk': self.test_document.pk - }, data={ - 'metadata_type': [self.metadata_type.pk, metadata_type_2.pk], - } - ) - - document_metadata_types = self.test_document.metadata.values_list( - 'metadata_type', flat=True - ) - self.assertTrue( - self.metadata_type.pk in document_metadata_types - ) - self.assertTrue( - metadata_type_2.pk in document_metadata_types + self.test_documents[1].metadata.first().value, TEST_METADATA_VALUE_EDITED ) @@ -332,13 +338,13 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie auto_upload_document = False def test_metadata_type_create_view_no_permission(self): - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, 403) def test_metadata_type_create_view_with_access(self): self.grant_permission(permission=permission_metadata_type_create) - response = self._request_metadata_type_create_view() + response = self._request_test_metadata_type_create_view() self.assertEqual(response.status_code, 302) self.assertQuerysetEqual( @@ -352,10 +358,10 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie ) def test_metadata_type_delete_view_no_permission(self): - self._create_metadata_type() + self._create_test_metadata_type() - response = self._request_metadata_type_delete_view() - self.assertEqual(response.status_code, 403) + response = self._request_test_metadata_type_delete_view() + self.assertEqual(response.status_code, 404) self.assertQuerysetEqual( qs=MetadataType.objects.values('label', 'name'), @@ -368,23 +374,23 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie ) def test_metadata_type_delete_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, + obj=self.test_metadata_type, permission=permission_metadata_type_delete ) - response = self._request_metadata_type_delete_view() + response = self._request_test_metadata_type_delete_view() self.assertEqual(response.status_code, 302) self.assertEqual(MetadataType.objects.count(), 0) def test_metadata_type_edit_view_no_permission(self): - self._create_metadata_type() + self._create_test_metadata_type() - response = self._request_metadata_type_edit_view() - self.assertEqual(response.status_code, 403) + response = self._request_test_metadata_type_edit_view() + self.assertEqual(response.status_code, 404) self.assertQuerysetEqual( qs=MetadataType.objects.values('label', 'name'), @@ -397,14 +403,14 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie ) def test_metadata_type_edit_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) - response = self._request_metadata_type_edit_view() + response = self._request_test_metadata_type_edit_view() self.assertEqual(response.status_code, 302) self.assertQuerysetEqual( @@ -418,39 +424,39 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie ) def test_metadata_type_list_view_no_permission(self): - self._create_metadata_type() + self._create_test_metadata_type() response = self._request_metadata_type_list_view() self.assertNotContains( - response=response, text=self.metadata_type, status_code=200 + response=response, text=self.test_metadata_type, status_code=200 ) def test_metadata_type_list_view_with_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self.grant_access( - obj=self.metadata_type, + obj=self.test_metadata_type, permission=permission_metadata_type_view ) response = self._request_metadata_type_list_view() self.assertContains( - response=response, text=self.metadata_type, status_code=200 + response=response, text=self.test_metadata_type, status_code=200 ) def test_metadata_type_relationship_view_no_permission(self): - self._create_metadata_type() + self._create_test_metadata_type() self._create_document_type() self.upload_document() - response = self._request_metadata_type_relationship_edit_view() + response = self._request_test_metadata_type_relationship_edit_view() self.assertEqual(response.status_code, 403) self.test_document_type.refresh_from_db() self.assertEqual(self.test_document_type.metadata.count(), 0) def test_metadata_type_relationship_view_with_document_type_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self._create_document_type() self.upload_document() @@ -459,41 +465,41 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie permission=permission_document_type_edit ) - response = self._request_metadata_type_relationship_edit_view() + response = self._request_test_metadata_type_relationship_edit_view() self.assertEqual(response.status_code, 403) self.test_document_type.refresh_from_db() self.assertEqual(self.test_document_type.metadata.count(), 0) def test_metadata_type_relationship_view_with_metadata_type_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self._create_document_type() self.upload_document() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_edit + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) - response = self._request_metadata_type_relationship_edit_view() + response = self._request_test_metadata_type_relationship_edit_view() self.assertEqual(response.status_code, 302) self.test_document_type.refresh_from_db() self.assertEqual(self.test_document_type.metadata.count(), 0) def test_metadata_type_relationship_view_with_metadata_type_and_document_type_access(self): - self._create_metadata_type() + self._create_test_metadata_type() self._create_document_type() self.upload_document() self.grant_access( - obj=self.metadata_type, permission=permission_metadata_type_edit + obj=self.test_metadata_type, permission=permission_metadata_type_edit ) self.grant_access( obj=self.test_document_type, permission=permission_document_type_edit ) - response = self._request_metadata_type_relationship_edit_view() + response = self._request_test_metadata_type_relationship_edit_view() self.assertEqual(response.status_code, 302) self.test_document_type.refresh_from_db() @@ -501,7 +507,7 @@ class MetadataTypeViewTestCase(DocumentTestMixin, MetadataTestsMixin, GenericVie qs=self.test_document_type.metadata.values('metadata_type', 'required'), values=[ { - 'metadata_type': self.metadata_type.pk, + 'metadata_type': self.test_metadata_type.pk, 'required': True, } ], transform=dict diff --git a/mayan/apps/metadata/tests/test_wizard_steps.py b/mayan/apps/metadata/tests/test_wizard_steps.py index c70fddb2e6..809297c7fc 100644 --- a/mayan/apps/metadata/tests/test_wizard_steps.py +++ b/mayan/apps/metadata/tests/test_wizard_steps.py @@ -31,12 +31,12 @@ class DocumentUploadMetadataTestCase(MetadataTypeTestMixin, GenericDocumentViewT ) self.test_document_type.metadata.create( - metadata_type=self.metadata_type, required=True + metadata_type=self.test_metadata_type, required=True ) def test_upload_interactive_with_unicode_metadata(self): url = furl(reverse('sources:upload_interactive')) - url.args['metadata0_id'] = self.metadata_type.pk + url.args['metadata0_id'] = self.test_metadata_type.pk url.args['metadata0_value'] = TEST_METADATA_VALUE_UNICODE self.grant_access( @@ -61,7 +61,7 @@ class DocumentUploadMetadataTestCase(MetadataTypeTestMixin, GenericDocumentViewT def test_upload_interactive_with_ampersand_metadata(self): url = furl(reverse('sources:upload_interactive')) - url.args['metadata0_id'] = self.metadata_type.pk + url.args['metadata0_id'] = self.test_metadata_type.pk url.args['metadata0_value'] = TEST_METADATA_VALUE_WITH_AMPERSAND self.grant_access( diff --git a/mayan/apps/metadata/views.py b/mayan/apps/metadata/views.py index 13606e948a..f2ff2f6e01 100644 --- a/mayan/apps/metadata/views.py +++ b/mayan/apps/metadata/views.py @@ -37,8 +37,8 @@ from .links import ( ) from .models import DocumentMetadata, MetadataType from .permissions import ( - permission_metadata_document_add, permission_metadata_document_edit, - permission_metadata_document_remove, permission_metadata_document_view, + permission_document_metadata_add, permission_document_metadata_edit, + permission_document_metadata_remove, permission_document_metadata_view, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) @@ -47,7 +47,7 @@ from .permissions import ( class DocumentMetadataAddView(MultipleObjectFormActionView): form_class = DocumentAddMetadataForm model = Document - object_permission = permission_metadata_document_add + object_permission = permission_document_metadata_add success_message = _('Metadata add request performed on %(count)d document') success_message_plural = _( 'Metadata add request performed on %(count)d documents' @@ -213,7 +213,7 @@ class DocumentMetadataAddView(MultipleObjectFormActionView): class DocumentMetadataEditView(MultipleObjectFormActionView): form_class = DocumentMetadataFormSet model = Document - object_permission = permission_metadata_document_edit + object_permission = permission_document_metadata_edit success_message = _( 'Metadata edit request performed on %(count)d document' ) @@ -398,7 +398,7 @@ class DocumentMetadataListView(SingleObjectListView): def dispatch(self, request, *args, **kwargs): AccessControlList.objects.check_access( obj=self.get_document(), - permissions=(permission_metadata_document_view,), + permissions=(permission_document_metadata_view,), user=self.request.user ) @@ -431,14 +431,14 @@ class DocumentMetadataListView(SingleObjectListView): 'title': _('Metadata for document: %s') % document, } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().metadata.all() class DocumentMetadataRemoveView(MultipleObjectFormActionView): form_class = DocumentMetadataRemoveFormSet model = Document - object_permission = permission_metadata_document_remove + object_permission = permission_document_metadata_remove success_message = _( 'Metadata remove request performed on %(count)d document' ) @@ -657,7 +657,7 @@ class MetadataTypeListView(SingleObjectListView): 'title': _('Metadata types'), } - def get_object_list(self): + def get_source_queryset(self): return MetadataType.objects.all() diff --git a/mayan/apps/ocr/tests/test_views.py b/mayan/apps/ocr/tests/test_views.py index 7a557a2cc3..ccc327ea6b 100644 --- a/mayan/apps/ocr/tests/test_views.py +++ b/mayan/apps/ocr/tests/test_views.py @@ -27,7 +27,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase): self.test_document.submit_for_ocr() response = self._request_document_content_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_content_view_with_access(self): self.test_document.submit_for_ocr() @@ -51,7 +51,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase): self.test_document.submit_for_ocr() response = self._request_document_page_content_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_page_content_view_with_access(self): self.test_document.submit_for_ocr() @@ -73,7 +73,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase): def test_document_submit_view_no_permission(self): response = self._request_document_submit_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( ''.join(self.test_document.latest_version.ocr_content()), '' @@ -102,7 +102,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase): def test_multiple_document_submit_view_no_permission(self): response = self._request_multiple_document_submit_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( ''.join(self.test_document.latest_version.ocr_content()), '' @@ -159,7 +159,7 @@ class OCRViewsTestCase(GenericDocumentViewTestCase): def test_document_type_ocr_settings_view_no_permission(self): response = self._request_document_type_ocr_settings_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) def test_document_type_ocr_settings_view_with_access(self): self.grant_access( diff --git a/mayan/apps/ocr/views.py b/mayan/apps/ocr/views.py index 8174f2b267..b447e927cf 100644 --- a/mayan/apps/ocr/views.py +++ b/mayan/apps/ocr/views.py @@ -10,6 +10,7 @@ from mayan.apps.common.generics import ( FormView, MultipleObjectConfirmActionView, SingleObjectDetailView, SingleObjectDownloadView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.forms import DocumentTypeFilteredSelectForm from mayan.apps.documents.models import Document, DocumentPage, DocumentType @@ -126,21 +127,23 @@ class DocumentTypeSubmitView(FormView): return reverse(viewname='common:tools_list') -class DocumentTypeSettingsEditView(SingleObjectEditView): +class DocumentTypeSettingsEditView(ExternalObjectMixin, SingleObjectEditView): + external_object_class = DocumentType + external_object_permission = permission_document_type_ocr_setup + external_object_pk_url_kwarg = 'pk' fields = ('auto_ocr',) - object_permission = permission_document_type_ocr_setup post_action_redirect = reverse_lazy( viewname='documents:document_type_list' ) def get_document_type(self): - return get_object_or_404(klass=DocumentType, pk=self.kwargs['pk']) + return self.external_object def get_extra_context(self): return { 'object': self.get_document_type(), 'title': _( - 'Edit OCR settings for document type: %s' + 'Edit OCR settings for document type: %s.' ) % self.get_document_type() } @@ -155,7 +158,7 @@ class EntryListView(SingleObjectListView): } view_permission = permission_document_type_ocr_setup - def get_object_list(self): + def get_source_queryset(self): return DocumentVersionOCRError.objects.all() @@ -172,7 +175,7 @@ class DocumentOCRErrorsListView(SingleObjectListView): 'title': _('OCR errors for document: %s') % self.get_document(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_document().latest_version.ocr_errors.all() diff --git a/mayan/apps/permissions/tests/test_api.py b/mayan/apps/permissions/tests/test_api.py index 78a499501a..816eb49b48 100644 --- a/mayan/apps/permissions/tests/test_api.py +++ b/mayan/apps/permissions/tests/test_api.py @@ -30,10 +30,9 @@ class PermissionAPIViewTestCase(PermissionAPIViewTestMixin, BaseAPITestCase): class RoleAPIViewTestCase(GroupTestMixin, PermissionTestMixin, RoleAPIViewTestMixin, RoleTestMixin, BaseAPITestCase): def test_role_create_api_view_no_permission(self): - response = self._request_test_role_create_api_view() - role_count = Role.objects.count() + response = self._request_test_role_create_api_view() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(Role.objects.count(), role_count) diff --git a/mayan/apps/permissions/tests/test_events.py b/mayan/apps/permissions/tests/test_events.py index f2bb8563c5..9fa5057bc6 100644 --- a/mayan/apps/permissions/tests/test_events.py +++ b/mayan/apps/permissions/tests/test_events.py @@ -36,7 +36,7 @@ class RoleEventsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): Action.objects.all().delete() response = self._request_test_role_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Action.objects.count(), 0) def test_role_edited_event_with_access(self): diff --git a/mayan/apps/permissions/tests/test_views.py b/mayan/apps/permissions/tests/test_views.py index ca06b19e42..bdc7399d17 100644 --- a/mayan/apps/permissions/tests/test_views.py +++ b/mayan/apps/permissions/tests/test_views.py @@ -31,13 +31,13 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): self.assertEqual(Role.objects.count(), role_count + 1) - def test_role_delete_view_no_access(self): + def test_role_delete_view_no_permission(self): self._create_test_role() role_count = Role.objects.count() response = self._request_test_role_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Role.objects.count(), role_count) @@ -52,13 +52,13 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): self.assertEqual(Role.objects.count(), role_count - 1) - def test_role_edit_view_no_access(self): + def test_role_edit_view_no_permission(self): self._create_test_role() role_label = self.test_role.label response = self._request_test_role_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_role.refresh_from_db() self.assertEqual(self.test_role.label, role_label) @@ -75,7 +75,7 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): self.test_role.refresh_from_db() self.assertNotEqual(self.test_role.label, role_label) - def test_role_list_view_no_access(self): + def test_role_list_view_no_permission(self): self._create_test_role() response = self._request_test_role_list_view() @@ -93,7 +93,7 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): response=response, text=self.test_role.label, status_code=200 ) - def test_role_permissions_view_no_access(self): + def test_role_permissions_view_no_permission(self): self._create_test_role() response = self._request_test_role_permissions_view() @@ -108,7 +108,7 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): response = self._request_test_role_permissions_view() self.assertEqual(response.status_code, 200) - def test_role_groups_view_no_access(self): + def test_role_groups_view_no_permission(self): self._create_test_role() response = self._request_test_role_groups_view() @@ -123,7 +123,7 @@ class RoleViewsTestCase(RoleTestMixin, RoleViewTestMixin, GenericViewTestCase): class GroupRoleViewTestCase(GroupTestMixin, GroupRoleViewTestMixin, RoleTestMixin, GenericViewTestCase): - def test_group_roles_view_no_access(self): + def test_group_roles_view_no_permission(self): self._create_test_group() response = self._request_test_group_roles_view() diff --git a/mayan/apps/smart_settings/views.py b/mayan/apps/smart_settings/views.py index 5669cd1076..2c8f2e86f1 100644 --- a/mayan/apps/smart_settings/views.py +++ b/mayan/apps/smart_settings/views.py @@ -34,7 +34,7 @@ class NamespaceDetailView(SingleObjectListView): _('Namespace: %s, not found') % self.kwargs['namespace_name'] ) - def get_object_list(self): + def get_source_queryset(self): return self.get_namespace().settings @@ -45,7 +45,7 @@ class NamespaceListView(SingleObjectListView): } view_permission = permission_settings_view - def get_object_list(self): + def get_source_queryset(self): return Namespace.get_all() diff --git a/mayan/apps/sources/views.py b/mayan/apps/sources/views.py index d7313d3e6b..0e5395bfd0 100644 --- a/mayan/apps/sources/views.py +++ b/mayan/apps/sources/views.py @@ -70,7 +70,7 @@ class SourceLogListView(SingleObjectListView): 'title': _('Log entries for source: %s') % self.get_source(), } - def get_object_list(self): + def get_source_queryset(self): return self.get_source().logs.all() def get_source(self): @@ -592,7 +592,7 @@ class SetupSourceEditView(SingleObjectEditView): class SetupSourceListView(SingleObjectListView): - queryset = Source.objects.select_subclasses() + source_queryset = Source.objects.select_subclasses() view_permission = permission_sources_setup_view def get_extra_context(self): diff --git a/mayan/apps/tags/tests/test_events.py b/mayan/apps/tags/tests/test_events.py index 00a271ac7e..82e8dc1327 100644 --- a/mayan/apps/tags/tests/test_events.py +++ b/mayan/apps/tags/tests/test_events.py @@ -44,7 +44,7 @@ class TagEventsTestCase(TagTestMixin, TagViewTestMixin, GenericViewTestCase): action_count = Action.objects.count() response = self._request_test_tag_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Action.objects.count(), action_count) diff --git a/mayan/apps/tags/tests/test_views.py b/mayan/apps/tags/tests/test_views.py index b6788be27e..f0181bcb13 100644 --- a/mayan/apps/tags/tests/test_views.py +++ b/mayan/apps/tags/tests/test_views.py @@ -38,7 +38,7 @@ class TagViewTestCase(TagTestMixin, TagViewTestMixin, GenericViewTestCase): tag_count = Tag.objects.count() response = self._request_test_tag_delete_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(Tag.objects.count(), tag_count) @@ -60,7 +60,7 @@ class TagViewTestCase(TagTestMixin, TagViewTestMixin, GenericViewTestCase): tag_count = Tag.objects.count() response = self._request_test_tag_delete_multiple_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(Tag.objects.count(), tag_count) @@ -82,7 +82,7 @@ class TagViewTestCase(TagTestMixin, TagViewTestMixin, GenericViewTestCase): tag_label = self.test_tag.label response = self._request_test_tag_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_tag.refresh_from_db() self.assertEqual(self.test_tag.label, tag_label) @@ -131,9 +131,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self._create_test_tag() response = self._request_test_document_tag_attach_view() - # Show same view with a warning message that ID XX is not one of the - # available choices. - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag not in self.test_document.tags.all()) @@ -143,7 +141,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.grant_access(obj=self.test_tag, permission=permission_tag_attach) response = self._request_test_document_tag_attach_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag not in self.test_document.tags.all()) @@ -172,7 +170,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self._create_test_tag() response = self._request_test_document_multiple_tag_multiple_attach_view() - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag not in self.test_document.tags.all()) @@ -182,7 +180,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.grant_access(obj=self.test_tag, permission=permission_tag_attach) response = self._request_test_document_multiple_tag_multiple_attach_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag not in self.test_document.tags.all()) @@ -212,7 +210,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.test_document.tags.add(self.test_tag) response = self._request_test_document_tag_multiple_remove_view() - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag in self.test_document.tags.all()) @@ -223,7 +221,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.grant_access(obj=self.test_tag, permission=permission_tag_remove) response = self._request_test_document_tag_multiple_remove_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag in self.test_document.tags.all()) @@ -255,7 +253,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.test_document.tags.add(self.test_tag) response = self._request_test_document_multiple_tag_remove_view() - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag in self.test_document.tags.all()) @@ -266,7 +264,7 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie self.grant_access(obj=self.test_tag, permission=permission_tag_remove) response = self._request_test_document_multiple_tag_remove_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertTrue(self.test_tag in self.test_document.tags.all()) diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index 4ad385e177..104488e275 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -220,7 +220,7 @@ class TagListView(SingleObjectListView): 'title': _('Tags'), } - def get_object_list(self): + def get_source_queryset(self): return self.get_tag_queryset() def get_tag_queryset(self): diff --git a/mayan/apps/task_manager/views.py b/mayan/apps/task_manager/views.py index cc501da70b..ab229a0c87 100644 --- a/mayan/apps/task_manager/views.py +++ b/mayan/apps/task_manager/views.py @@ -16,7 +16,7 @@ class QueueListView(SingleObjectListView): } view_permission = permission_task_view - def get_object_list(self): + def get_source_queryset(self): return CeleryQueue.all() @@ -33,7 +33,7 @@ class QueueActiveTaskListView(SingleObjectListView): def get_object(self): return CeleryQueue.get(queue_name=self.kwargs['queue_name']) - def get_object_list(self): + def get_source_queryset(self): try: return self.get_task_list() except Exception as exception: diff --git a/mayan/apps/user_management/tests/test_events.py b/mayan/apps/user_management/tests/test_events.py index 98446b3e61..4f0cab8d5f 100644 --- a/mayan/apps/user_management/tests/test_events.py +++ b/mayan/apps/user_management/tests/test_events.py @@ -28,7 +28,8 @@ class GroupEventsViewTestCase(GroupTestMixin, GroupViewTestMixin, UserTestMixin, ) Action.objects.all().delete() - self._request_test_group_create_view() + response = self._request_test_group_create_view() + self.assertEqual(response.status_code, 302) action = Action.objects.last() @@ -43,7 +44,8 @@ class GroupEventsViewTestCase(GroupTestMixin, GroupViewTestMixin, UserTestMixin, ) Action.objects.all().delete() - self._request_test_group_edit_view() + response = self._request_test_group_edit_view() + self.assertEqual(response.status_code, 302) action = Action.objects.last() @@ -58,7 +60,8 @@ class GroupEventsAPITestCase(GroupAPITestMixin, GroupTestMixin, GroupViewTestMix ) Action.objects.all().delete() - self._request_test_group_create_api_view() + response = self._request_test_group_create_api_view() + self.assertEqual(response.status_code, 201) action = Action.objects.last() @@ -76,7 +79,8 @@ class GroupEventsAPITestCase(GroupAPITestMixin, GroupTestMixin, GroupViewTestMix ) Action.objects.all().delete() - self._request_test_group_edit_patch_api_view() + response = self._request_test_group_edit_patch_api_view() + self.assertEqual(response.status_code, 200) action = Action.objects.last() @@ -133,7 +137,8 @@ class UserEventsViewTestCase(UserAPITestMixin, UserTestMixin, UserViewTestMixin, ) Action.objects.all().delete() - self._request_test_user_create_view() + response = self._request_test_user_create_view() + self.assertEqual(response.status_code, 302) action = Action.objects.last() @@ -149,7 +154,8 @@ class UserEventsViewTestCase(UserAPITestMixin, UserTestMixin, UserViewTestMixin, ) Action.objects.all().delete() - self._request_test_user_edit_view() + response = self._request_test_user_edit_view() + self.assertEqual(response.status_code, 302) action = Action.objects.last() @@ -165,7 +171,8 @@ class UserEventsAPITestCase(UserAPITestMixin, UserTestMixin, UserViewTestMixin, ) Action.objects.all().delete() - self._request_test_user_create_api_view() + response = self._request_test_user_create_api_view() + self.assertEqual(response.status_code, 201) action = Action.objects.last() @@ -182,7 +189,8 @@ class UserEventsAPITestCase(UserAPITestMixin, UserTestMixin, UserViewTestMixin, ) Action.objects.all().delete() - self._request_test_user_edit_patch_api_view() + response = self._request_test_user_edit_patch_api_view() + self.assertEqual(response.status_code, 200) action = Action.objects.last() diff --git a/mayan/apps/user_management/tests/test_views.py b/mayan/apps/user_management/tests/test_views.py index c95210da9c..e64654d2fc 100644 --- a/mayan/apps/user_management/tests/test_views.py +++ b/mayan/apps/user_management/tests/test_views.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import Group from mayan.apps.common.tests import GenericViewTestCase from mayan.apps.documents.tests import GenericDocumentViewTestCase from mayan.apps.metadata.models import MetadataType -from mayan.apps.metadata.permissions import permission_metadata_document_edit +from mayan.apps.metadata.permissions import permission_document_metadata_edit from mayan.apps.metadata.tests.literals import ( TEST_METADATA_TYPE_LABEL, TEST_METADATA_TYPE_NAME, ) @@ -48,7 +48,7 @@ class GroupViewsTestCase(GroupTestMixin, GroupViewTestMixin, UserTestMixin, Gene group_count = Group.objects.count() response = self._request_test_group_delete_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.assertEqual(Group.objects.count(), group_count) @@ -71,7 +71,7 @@ class GroupViewsTestCase(GroupTestMixin, GroupViewTestMixin, UserTestMixin, Gene group_name = self.test_group.name response = self._request_test_group_edit_view() - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_group.refresh_from_db() self.assertEqual(self.test_group.name, group_name) @@ -173,7 +173,7 @@ class SuperUserViewTestCase(UserTestMixin, UserViewTestMixin, GenericViewTestCas obj=self.test_superuser, permission=permission_user_delete ) response = self._request_test_superuser_delete_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual( get_user_model().objects.filter(is_superuser=True).count(), superuser_count @@ -228,7 +228,7 @@ class UserViewTestCase(UserTestMixin, UserViewTestMixin, GenericViewTestCase): user_count = get_user_model().objects.count() response = self._request_test_user_delete_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(get_user_model().objects.count(), user_count) @@ -250,7 +250,7 @@ class UserViewTestCase(UserTestMixin, UserViewTestMixin, GenericViewTestCase): user_count = get_user_model().objects.count() response = self._request_test_user_delete_multiple_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(get_user_model().objects.count(), user_count) @@ -274,7 +274,7 @@ class UserViewTestCase(UserTestMixin, UserViewTestMixin, GenericViewTestCase): response = self._request_test_user_password_set_view( password=TEST_USER_PASSWORD_EDITED ) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_user.refresh_from_db() self.assertEqual(self.test_user.password, password_hash) @@ -300,7 +300,7 @@ class UserViewTestCase(UserTestMixin, UserViewTestMixin, GenericViewTestCase): response = self._request_test_user_password_set_multiple_view( password=TEST_USER_PASSWORD_EDITED ) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 404) self.test_user.refresh_from_db() self.assertEqual(self.test_user.password, password_hash) @@ -388,7 +388,7 @@ class MetadataLookupIntegrationTestCase(GenericDocumentViewTestCase): self.metadata_type.save() self.document.metadata.create(metadata_type=self.metadata_type) self.grant_access( - obj=self.document, permission=permission_metadata_document_edit + obj=self.document, permission=permission_document_metadata_edit ) response = self.get( @@ -407,7 +407,7 @@ class MetadataLookupIntegrationTestCase(GenericDocumentViewTestCase): self.metadata_type.save() self.document.metadata.create(metadata_type=self.metadata_type) self.grant_access( - obj=self.document, permission=permission_metadata_document_edit + obj=self.document, permission=permission_document_metadata_edit ) response = self.get( diff --git a/mayan/apps/user_management/views.py b/mayan/apps/user_management/views.py index 9c3a795457..d5964e20cd 100644 --- a/mayan/apps/user_management/views.py +++ b/mayan/apps/user_management/views.py @@ -173,7 +173,7 @@ class UserCreateView(SingleObjectCreateView): class UserDeleteView(MultipleObjectConfirmActionView): object_permission = permission_user_delete - queryset = get_user_queryset() + source_queryset = get_user_queryset() success_message = _('User delete request performed on %(count)d user') success_message_plural = _( 'User delete request performed on %(count)d users' @@ -232,7 +232,7 @@ class UserDetailsView(SingleObjectDetailView): ) object_permission = permission_user_view pk_url_kwarg = 'pk' - queryset = get_user_queryset() + source_queryset = get_user_queryset() def get_extra_context(self, **kwargs): return { @@ -247,7 +247,7 @@ class UserEditView(SingleObjectEditView): post_action_redirect = reverse_lazy( viewname='user_management:user_list' ) - queryset = get_user_queryset() + source_queryset = get_user_queryset() def get_extra_context(self): return { @@ -306,7 +306,7 @@ class UserListView(SingleObjectListView): 'title': _('Users'), } - def get_object_list(self): + def get_source_queryset(self): return get_user_queryset()