diff --git a/mayan/apps/linking/admin.py b/mayan/apps/linking/admin.py index 24b25ad2f9..1485ffa842 100644 --- a/mayan/apps/linking/admin.py +++ b/mayan/apps/linking/admin.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals from django.contrib import admin +from organizations.admin import OrganizationAdminMixin + from .models import SmartLink, SmartLinkCondition @@ -13,7 +15,7 @@ class SmartLinkConditionInline(admin.StackedInline): @admin.register(SmartLink) -class SmartLinkAdmin(admin.ModelAdmin): +class SmartLinkAdmin(OrganizationAdminMixin, admin.ModelAdmin): def document_type_list(self, instance): return ','.join( instance.document_types.values_list('label', flat=True) diff --git a/mayan/apps/linking/managers.py b/mayan/apps/linking/managers.py new file mode 100644 index 0000000000..7bea3fa951 --- /dev/null +++ b/mayan/apps/linking/managers.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals + +from django.apps import apps +from django.db import models + + +class OrganizationSmartLinkConditionManager(models.Manager): + def get_queryset(self): + SmartLink = apps.get_model('linking', 'SmartLink') + + return super( + OrganizationSmartLinkConditionManager, self + ).get_queryset().filter( + smart_link__in=SmartLink.on_organization.all(), + ) diff --git a/mayan/apps/linking/migrations/0006_smartlink_organization.py b/mayan/apps/linking/migrations/0006_smartlink_organization.py new file mode 100644 index 0000000000..36eb950fa0 --- /dev/null +++ b/mayan/apps/linking/migrations/0006_smartlink_organization.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import organizations.shortcuts + + +class Migration(migrations.Migration): + + dependencies = [ + ('organizations', '0002_add_data_default_organization'), + ('linking', '0005_auto_20150729_2344'), + ] + + operations = [ + migrations.AddField( + model_name='smartlink', + name='organization', + field=models.ForeignKey( + default=organizations.shortcuts.get_current_organization, + to='organizations.Organization' + ), + ), + ] diff --git a/mayan/apps/linking/models.py b/mayan/apps/linking/models.py index 776f84fbe6..677dff74ce 100644 --- a/mayan/apps/linking/models.py +++ b/mayan/apps/linking/models.py @@ -7,14 +7,21 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from documents.models import Document, DocumentType +from organizations.models import Organization +from organizations.managers import CurrentOrganizationManager +from organizations.shortcuts import get_current_organization from .literals import ( INCLUSION_AND, INCLUSION_CHOICES, INCLUSION_OR, OPERATOR_CHOICES ) +from .managers import OrganizationSmartLinkConditionManager @python_2_unicode_compatible class SmartLink(models.Model): + organization = models.ForeignKey( + Organization, default=get_current_organization + ) label = models.CharField(max_length=96, verbose_name=_('Label')) dynamic_label = models.CharField( blank=True, max_length=96, help_text=_( @@ -29,6 +36,9 @@ class SmartLink(models.Model): DocumentType, verbose_name=_('Document types') ) + objects = models.Manager() + on_organization = CurrentOrganizationManager() + def __str__(self): return self.label @@ -75,9 +85,9 @@ class SmartLink(models.Model): smart_link_query |= condition_query if smart_link_query: - return Document.objects.filter(smart_link_query) + return Document.on_organization.filter(smart_link_query) else: - return Document.objects.none() + return Document.on_organization.none() def resolve_for(self, document): return ResolvedSmartLink( @@ -123,6 +133,9 @@ class SmartLinkCondition(models.Model): ) enabled = models.BooleanField(default=True, verbose_name=_('Enabled')) + objects = models.Manager() + on_organization = OrganizationSmartLinkConditionManager() + def __str__(self): return '%s foreign %s %s %s %s' % ( self.get_inclusion_display(), diff --git a/mayan/apps/linking/tests/test_models.py b/mayan/apps/linking/tests/test_models.py index 3d79855654..489e32b67d 100644 --- a/mayan/apps/linking/tests/test_models.py +++ b/mayan/apps/linking/tests/test_models.py @@ -3,10 +3,11 @@ from __future__ import unicode_literals from django.contrib.auth import get_user_model -from django.test import TestCase, override_settings +from django.test import override_settings from documents.models import DocumentType from documents.tests import TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE +from organizations.tests import OrganizationTestCase from user_management.tests.literals import ( TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME ) @@ -17,9 +18,11 @@ from .literals import TEST_SMART_LINK_LABEL, TEST_SMART_LINK_DYNAMIC_LABEL @override_settings(OCR_AUTO_OCR=False) -class SmartLinkTestCase(TestCase): +class SmartLinkTestCase(OrganizationTestCase): def setUp(self): - self.document_type = DocumentType.objects.create( + super(SmartLinkTestCase, self).setUp() + + self.document_type = DocumentType.on_organization.create( label=TEST_DOCUMENT_TYPE ) @@ -28,16 +31,17 @@ class SmartLinkTestCase(TestCase): file_object=file_object ) - self.user = get_user_model().objects.create_superuser( + self.user = get_user_model().on_organization.create_superuser( username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, password=TEST_ADMIN_PASSWORD ) def tearDown(self): self.document_type.delete() + super(SmartLinkTestCase, self).tearDown() def test_dynamic_label(self): - smart_link = SmartLink.objects.create( + smart_link = SmartLink.on_organization.create( label=TEST_SMART_LINK_LABEL, dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL ) diff --git a/mayan/apps/linking/tests/test_organization_views.py b/mayan/apps/linking/tests/test_organization_views.py new file mode 100644 index 0000000000..007977fba2 --- /dev/null +++ b/mayan/apps/linking/tests/test_organization_views.py @@ -0,0 +1,94 @@ +from __future__ import unicode_literals + +from organizations.tests.test_organization_views import OrganizationViewTestCase + +from ..models import SmartLink + +from .literals import ( + TEST_SMART_LINK_DYNAMIC_LABEL, TEST_SMART_LINK_EDITED_LABEL, + TEST_SMART_LINK_LABEL +) + + +class OrganizationIndexViewTestCase(OrganizationViewTestCase): + def create_smart_link(self): + self.smart_link = SmartLink.on_organization.create( + organization=self.organization_a, label=TEST_SMART_LINK_LABEL + ) + + def test_smart_link_create_view(self): + with self.settings(ORGANIZATION_ID=self.organization_a.pk): + self.post( + 'linking:smart_link_create', data={ + 'label': TEST_SMART_LINK_LABEL + } + ) + self.assertEqual(SmartLink.on_organization.count(), 1) + + with self.settings(ORGANIZATION_ID=self.organization_b.pk): + self.assertEqual(SmartLink.on_organization.count(), 0) + + def test_smart_link_delete_view(self): + self.create_smart_link() + + with self.settings(ORGANIZATION_ID=self.organization_b.pk): + response = self.post( + 'linking:smart_link_delete', args=(self.smart_link.pk,) + ) + self.assertEqual(response.status_code, 404) + + with self.settings(ORGANIZATION_ID=self.organization_a.pk): + self.assertEqual(SmartLink.on_organization.count(), 1) + + with self.settings(ORGANIZATION_ID=self.organization_a.pk): + response = self.post( + 'linking:smart_link_delete', args=(self.smart_link.pk,) + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(SmartLink.on_organization.count(), 0) + + def test_smart_link_edit_view(self): + self.create_smart_link() + + with self.settings(ORGANIZATION_ID=self.organization_b.pk): + response = self.post( + 'linking:smart_link_edit', args=(self.smart_link.pk,), + data={ + 'label': TEST_SMART_LINK_EDITED_LABEL + } + ) + + self.assertEqual(response.status_code, 404) + self.assertEqual(self.smart_link.label, TEST_SMART_LINK_LABEL) + + with self.settings(ORGANIZATION_ID=self.organization_a.pk): + response = self.post( + 'linking:smart_link_edit', args=(self.smart_link.pk,), + data={ + 'label': TEST_SMART_LINK_EDITED_LABEL + } + ) + + self.assertEqual(response.status_code, 302) + self.smart_link.refresh_from_db() + + self.assertEqual( + self.smart_link.label, TEST_SMART_LINK_EDITED_LABEL + ) + + def test_smart_link_list_view(self): + self.create_smart_link() + + with self.settings(ORGANIZATION_ID=self.organization_b.pk): + response = self.get('linking:smart_link_list') + + self.assertNotContains( + response, text=self.smart_link.label, status_code=200 + ) + + with self.settings(ORGANIZATION_ID=self.organization_a.pk): + response = self.get('linking:smart_link_list') + + self.assertContains( + response, text=self.smart_link.label, status_code=200 + ) diff --git a/mayan/apps/linking/tests/test_views.py b/mayan/apps/linking/tests/test_views.py index 317e2450b5..b27dbf0c59 100644 --- a/mayan/apps/linking/tests/test_views.py +++ b/mayan/apps/linking/tests/test_views.py @@ -29,7 +29,7 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): ) self.assertEquals(response.status_code, 403) - self.assertEqual(SmartLink.objects.count(), 0) + self.assertEqual(SmartLink.on_organization.count(), 0) def test_smart_link_create_view_with_permission(self): self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) @@ -44,21 +44,23 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): }, follow=True ) self.assertContains(response, text='created', status_code=200) - self.assertEqual(SmartLink.objects.count(), 1) + self.assertEqual(SmartLink.on_organization.count(), 1) self.assertEqual( - SmartLink.objects.first().label, TEST_SMART_LINK_LABEL + SmartLink.on_organization.first().label, TEST_SMART_LINK_LABEL ) def test_smart_link_delete_view_no_permission(self): self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) - smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL) + smart_link = SmartLink.on_organization.create( + label=TEST_SMART_LINK_LABEL + ) response = self.post( 'linking:smart_link_delete', args=(smart_link.pk,) ) self.assertEqual(response.status_code, 403) - self.assertEqual(SmartLink.objects.count(), 1) + self.assertEqual(SmartLink.on_organization.count(), 1) def test_smart_link_delete_view_with_permission(self): self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) @@ -67,19 +69,23 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): permission_smart_link_delete.stored_permission ) - smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL) + smart_link = SmartLink.on_organization.create( + label=TEST_SMART_LINK_LABEL + ) response = self.post( 'linking:smart_link_delete', args=(smart_link.pk,), follow=True ) self.assertContains(response, text='deleted', status_code=200) - self.assertEqual(SmartLink.objects.count(), 0) + self.assertEqual(SmartLink.on_organization.count(), 0) def test_smart_link_edit_view_no_permission(self): self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) - smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL) + smart_link = SmartLink.on_organization.create( + label=TEST_SMART_LINK_LABEL + ) response = self.post( 'linking:smart_link_edit', args=(smart_link.pk,), data={ @@ -87,7 +93,7 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): } ) self.assertEqual(response.status_code, 403) - smart_link = SmartLink.objects.get(pk=smart_link.pk) + smart_link = SmartLink.on_organization.get(pk=smart_link.pk) self.assertEqual(smart_link.label, TEST_SMART_LINK_LABEL) def test_smart_link_edit_view_with_permission(self): @@ -97,7 +103,9 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): permission_smart_link_edit.stored_permission ) - smart_link = SmartLink.objects.create(label=TEST_SMART_LINK_LABEL) + smart_link = SmartLink.on_organization.create( + label=TEST_SMART_LINK_LABEL + ) response = self.post( 'linking:smart_link_edit', args=(smart_link.pk,), data={ @@ -105,18 +113,18 @@ class SmartLinkViewTestCase(GenericDocumentViewTestCase): }, follow=True ) - smart_link = SmartLink.objects.get(pk=smart_link.pk) + smart_link = SmartLink.on_organization.get(pk=smart_link.pk) self.assertContains(response, text='update', status_code=200) self.assertEqual(smart_link.label, TEST_SMART_LINK_EDITED_LABEL) def setup_smart_links(self): - smart_link = SmartLink.objects.create( + smart_link = SmartLink.on_organization.create( label=TEST_SMART_LINK_LABEL, dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL ) smart_link.document_types.add(self.document_type) - smart_link_2 = SmartLink.objects.create( + smart_link_2 = SmartLink.on_organization.create( label=TEST_SMART_LINK_LABEL, dynamic_label=TEST_SMART_LINK_DYNAMIC_LABEL ) diff --git a/mayan/apps/linking/views.py b/mayan/apps/linking/views.py index c7e03b0482..df339bc31d 100644 --- a/mayan/apps/linking/views.py +++ b/mayan/apps/linking/views.py @@ -31,10 +31,10 @@ logger = logging.getLogger(__name__) class ResolvedSmartLinkView(DocumentListView): def dispatch(self, request, *args, **kwargs): self.document = get_object_or_404( - Document, pk=self.kwargs['document_pk'] + Document.on_organization, pk=self.kwargs['document_pk'] ) self.smart_link = get_object_or_404( - SmartLink, pk=self.kwargs['smart_link_pk'] + SmartLink.on_organization, pk=self.kwargs['smart_link_pk'] ) try: @@ -63,7 +63,7 @@ class ResolvedSmartLinkView(DocumentListView): try: queryset = self.smart_link.get_linked_document_for(self.document) except Exception as exception: - queryset = Document.objects.none() + queryset = Document.on_organization.none() if self.request.user.is_staff or self.request.user.is_superuser: messages.error( @@ -110,12 +110,14 @@ class SetupSmartLinkDocumentTypesView(AssignRemoveView): } def get_object(self): - return get_object_or_404(SmartLink, pk=self.kwargs['pk']) + return get_object_or_404( + SmartLink.on_organization, pk=self.kwargs['pk'] + ) def left_list(self): # TODO: filter document type list by user ACL return AssignRemoveView.generate_choices( - DocumentType.objects.exclude( + DocumentType.on_organization.exclude( pk__in=self.get_object().document_types.all() ) ) @@ -144,12 +146,14 @@ class SmartLinkListView(SingleObjectListView): return super(SmartLinkListView, self).get_queryset() def get_smart_link_queryset(self): - return SmartLink.objects.all() + return SmartLink.on_organization.all() class DocumentSmartLinkListView(SmartLinkListView): def dispatch(self, request, *args, **kwargs): - self.document = get_object_or_404(Document, pk=self.kwargs['pk']) + self.document = get_object_or_404( + Document.on_organization, pk=self.kwargs['pk'] + ) try: Permission.check_permissions( @@ -174,7 +178,7 @@ class DocumentSmartLinkListView(SmartLinkListView): } def get_smart_link_queryset(self): - return ResolvedSmartLink.objects.filter( + return ResolvedSmartLink.on_organization.filter( document_types=self.document.document_type, enabled=True ) @@ -188,7 +192,6 @@ class SmartLinkCreateView(SingleObjectCreateView): class SmartLinkEditView(SingleObjectEditView): form_class = SmartLinkForm - model = SmartLink post_action_redirect = reverse_lazy('linking:smart_link_list') view_permission = permission_smart_link_edit @@ -198,9 +201,11 @@ class SmartLinkEditView(SingleObjectEditView): 'title': _('Edit smart link: %s') % self.get_object() } + def get_queryset(self): + return SmartLink.on_organization.all() + class SmartLinkDeleteView(SingleObjectDeleteView): - model = SmartLink post_action_redirect = reverse_lazy('linking:smart_link_list') view_permission = permission_smart_link_delete @@ -210,6 +215,9 @@ class SmartLinkDeleteView(SingleObjectDeleteView): 'title': _('Delete smart link: %s') % self.get_object() } + def get_queryset(self): + return SmartLink.on_organization.all() + class SmartLinkConditionListView(SingleObjectListView): view_permission = permission_smart_link_edit @@ -227,7 +235,9 @@ class SmartLinkConditionListView(SingleObjectListView): return self.get_smart_link().conditions.all() def get_smart_link(self): - return get_object_or_404(SmartLink, pk=self.kwargs['pk']) + return get_object_or_404( + SmartLink.on_organization, pk=self.kwargs['pk'] + ) class SmartLinkConditionCreateView(SingleObjectCreateView): @@ -269,12 +279,13 @@ class SmartLinkConditionCreateView(SingleObjectCreateView): return self.get_smart_link().conditions.all() def get_smart_link(self): - return get_object_or_404(SmartLink, pk=self.kwargs['pk']) + return get_object_or_404( + SmartLink.on_organization, pk=self.kwargs['pk'] + ) class SmartLinkConditionEditView(SingleObjectEditView): form_class = SmartLinkConditionForm - model = SmartLinkCondition def dispatch(self, request, *args, **kwargs): try: @@ -306,10 +317,11 @@ class SmartLinkConditionEditView(SingleObjectEditView): ) ) + def get_queryset(self): + return SmartLinkCondition.on_organization.all() + class SmartLinkConditionDeleteView(SingleObjectDeleteView): - model = SmartLinkCondition - def dispatch(self, request, *args, **kwargs): try: Permission.check_permissions( @@ -341,3 +353,6 @@ class SmartLinkConditionDeleteView(SingleObjectDeleteView): self.get_object().smart_link.pk, ) ) + + def get_queryset(self): + return SmartLinkCondition.on_organization.all() diff --git a/mayan/apps/organizations/shortcuts.py b/mayan/apps/organizations/shortcuts.py index 8f3cf700b2..d12d7efee9 100644 --- a/mayan/apps/organizations/shortcuts.py +++ b/mayan/apps/organizations/shortcuts.py @@ -4,5 +4,5 @@ from django.apps import apps def get_current_organization(): - Organization = apps.get_model('organizations', 'Organizations') + Organization = apps.get_model('organizations', 'Organization') return Organization.objects.get_current().pk