Compare commits
89 Commits
features/d
...
feature/mu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
971d643759 | ||
|
|
ba493cf984 | ||
|
|
9ffcfeb49b | ||
|
|
41f68cf435 | ||
|
|
072bfcf2aa | ||
|
|
6a258e4a02 | ||
|
|
59fbe2d409 | ||
|
|
aa0f48b1a0 | ||
|
|
a2f8e8b8d8 | ||
|
|
7ae0917564 | ||
|
|
ff30268a4b | ||
|
|
5466dcdad3 | ||
|
|
2035602836 | ||
|
|
326919271a | ||
|
|
331a4da3b3 | ||
|
|
017dc67d3c | ||
|
|
aea00db37e | ||
|
|
629cc24090 | ||
|
|
f799d379d1 | ||
|
|
8221f90fd3 | ||
|
|
f6dfff5949 | ||
|
|
38eb65151a | ||
|
|
8435b8444a | ||
|
|
e5c51749da | ||
|
|
132d66fff8 | ||
|
|
0a545f4b33 | ||
|
|
305aad0bfa | ||
|
|
ccf3795601 | ||
|
|
18bd82ba55 | ||
|
|
3ebadf763b | ||
|
|
f9670ea7c2 | ||
|
|
de40977f5f | ||
|
|
aca93a0deb | ||
|
|
e943588ba2 | ||
|
|
db1673dd0a | ||
|
|
2dcad10805 | ||
|
|
ead86806d4 | ||
|
|
cc360be4a4 | ||
|
|
69bd6cc308 | ||
|
|
395fe0cb98 | ||
|
|
d9137b4361 | ||
|
|
e5c9e91104 | ||
|
|
67f88b79c6 | ||
|
|
266cf5c8a3 | ||
|
|
e228dfc8c5 | ||
|
|
fc86abe951 | ||
|
|
bc7682ab8a | ||
|
|
ba1c5c1b17 | ||
|
|
48d88d39e7 | ||
|
|
de81fc58dd | ||
|
|
b71ebe45f9 | ||
|
|
05a46445d9 | ||
|
|
907744cf18 | ||
|
|
f7fd9634df | ||
|
|
796e4cea04 | ||
|
|
b2304119fc | ||
|
|
07a124187f | ||
|
|
eab5296c7a | ||
|
|
ab83e23f27 | ||
|
|
5e7a62e022 | ||
|
|
e34dffb176 | ||
|
|
3a2d8bac33 | ||
|
|
58d634a395 | ||
|
|
a0df9d260d | ||
|
|
626cc1cd07 | ||
|
|
c49f8b1def | ||
|
|
2ca3a67c9c | ||
|
|
7be1d76f62 | ||
|
|
c5f64d4805 | ||
|
|
27c1a33762 | ||
|
|
ce0b0a9a79 | ||
|
|
90778c709c | ||
|
|
6e6a6073d2 | ||
|
|
a3a03ec095 | ||
|
|
433e295d07 | ||
|
|
c37430ff12 | ||
|
|
0e5521160b | ||
|
|
83046882b1 | ||
|
|
56f1a7d537 | ||
|
|
fba20b0a91 | ||
|
|
ab69031662 | ||
|
|
14bd599387 | ||
|
|
d71357cf81 | ||
|
|
4aea55339f | ||
|
|
9013793b5c | ||
|
|
bd3bdb9b13 | ||
|
|
9efa7c9543 | ||
|
|
8a5a26c0b4 | ||
|
|
6492908c59 |
@@ -20,16 +20,16 @@ variables:
|
|||||||
# - bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
# - bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
||||||
# tags:
|
# tags:
|
||||||
# - mysql
|
# - mysql
|
||||||
test:postgres:
|
#test:postgres:
|
||||||
script:
|
# script:
|
||||||
- pip install -r requirements/testing.txt
|
# - pip install -r requirements/testing.txt
|
||||||
- pip install -q psycopg2
|
# - pip install -q psycopg2
|
||||||
- coverage run manage.py runtests --settings=mayan.settings.testing.gitlab-ci.db_postgres --nomigrations
|
# - coverage run manage.py runtests --settings=mayan.settings.testing.gitlab-ci.db_postgres --nomigrations
|
||||||
- bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
# - bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
||||||
tags:
|
# tags:
|
||||||
- postgres
|
# - postgres
|
||||||
test:sqlite:
|
#test:sqlite:
|
||||||
script:
|
# script:
|
||||||
- pip install -r requirements/testing.txt
|
# - pip install -r requirements/testing.txt
|
||||||
- coverage run manage.py runtests --settings=mayan.settings.testing.gitlab-ci --nomigrations
|
# - coverage run manage.py runtests --settings=mayan.settings.testing.gitlab-ci --nomigrations
|
||||||
- bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
# - bash <(curl https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -t $CODECOV_TOKEN
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -52,10 +52,10 @@ clean-pyc:
|
|||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
test:
|
test:
|
||||||
./manage.py test $(MODULE) --settings=mayan.settings.testing --nomigrations
|
python -Wall ./manage.py test $(MODULE) --settings=mayan.settings.testing --nomigrations
|
||||||
|
|
||||||
test-all:
|
test-all:
|
||||||
./manage.py runtests --settings=mayan.settings.testing --nomigrations
|
python -Wall ./manage.py runtests --settings=mayan.settings.testing --nomigrations
|
||||||
|
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|||||||
@@ -74,14 +74,14 @@ class AccessControlListManager(models.Manager):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
user_roles = []
|
user_roles = []
|
||||||
for group in user.groups.all():
|
for group in user.organization_groups.all():
|
||||||
for role in group.roles.all():
|
for role in group.roles.all():
|
||||||
if set(stored_permissions).intersection(set(self.get_inherited_permissions(role=role, obj=obj))):
|
if set(stored_permissions).intersection(set(self.get_inherited_permissions(role=role, obj=obj))):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
user_roles.append(role)
|
user_roles.append(role)
|
||||||
|
|
||||||
if not self.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.pk, permissions__in=stored_permissions, role__in=user_roles).exists():
|
if not self.model.on_organization.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.pk, permissions__in=stored_permissions, role__in=user_roles).exists():
|
||||||
raise PermissionDenied(ugettext('Insufficient access.'))
|
raise PermissionDenied(ugettext('Insufficient access.'))
|
||||||
|
|
||||||
def filter_by_access(self, permission, user, queryset):
|
def filter_by_access(self, permission, user, queryset):
|
||||||
@@ -89,7 +89,7 @@ class AccessControlListManager(models.Manager):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
user_roles = []
|
user_roles = []
|
||||||
for group in user.groups.all():
|
for group in user.organization_groups.all():
|
||||||
for role in group.roles.all():
|
for role in group.roles.all():
|
||||||
user_roles.append(role)
|
user_roles.append(role)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- 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'),
|
||||||
|
('acls', '0002_auto_20150703_0513'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='accesscontrollist',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -8,6 +8,9 @@ from django.db import models
|
|||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
from permissions.models import Role, StoredPermission
|
from permissions.models import Role, StoredPermission
|
||||||
|
|
||||||
from .managers import AccessControlListManager
|
from .managers import AccessControlListManager
|
||||||
@@ -30,6 +33,9 @@ class AccessControlList(models.Model):
|
|||||||
ct_field='content_type',
|
ct_field='content_type',
|
||||||
fk_field='object_id',
|
fk_field='object_id',
|
||||||
)
|
)
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
# TODO: limit choices to the permissions valid for the content_object
|
# TODO: limit choices to the permissions valid for the content_object
|
||||||
permissions = models.ManyToManyField(
|
permissions = models.ManyToManyField(
|
||||||
StoredPermission, blank=True, related_name='acls',
|
StoredPermission, blank=True, related_name='acls',
|
||||||
@@ -38,6 +44,7 @@ class AccessControlList(models.Model):
|
|||||||
role = models.ForeignKey(Role, related_name='acls', verbose_name=_('Role'))
|
role = models.ForeignKey(Role, related_name='acls', verbose_name=_('Role'))
|
||||||
|
|
||||||
objects = AccessControlListManager()
|
objects = AccessControlListManager()
|
||||||
|
on_organization = CurrentOrganizationManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('content_type', 'object_id', 'role')
|
unique_together = ('content_type', 'object_id', 'role')
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
from documents.models import Document, DocumentType
|
from documents.models import Document, DocumentType
|
||||||
from documents.permissions import permission_document_view
|
from documents.permissions import permission_document_view
|
||||||
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||||
|
from organizations.tests.base import OrganizationTestCase
|
||||||
from permissions.classes import Permission
|
from permissions.classes import Permission
|
||||||
from permissions.models import Role
|
from permissions.models import Role
|
||||||
from permissions.tests.literals import TEST_ROLE_LABEL
|
from permissions.tests.literals import TEST_ROLE_LABEL
|
||||||
|
from user_management.models import MayanGroup
|
||||||
from user_management.tests.literals import TEST_USER_USERNAME, TEST_GROUP
|
from user_management.tests.literals import TEST_USER_USERNAME, TEST_GROUP
|
||||||
|
|
||||||
from ..models import AccessControlList
|
from ..models import AccessControlList
|
||||||
@@ -19,13 +20,15 @@ TEST_DOCUMENT_TYPE_2 = 'test document type 2'
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class PermissionTestCase(TestCase):
|
class PermissionTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type_1 = DocumentType.objects.create(
|
super(PermissionTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.document_type_1 = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
self.document_type_2 = DocumentType.objects.create(
|
self.document_type_2 = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2
|
label=TEST_DOCUMENT_TYPE_2
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,21 +47,23 @@ class PermissionTestCase(TestCase):
|
|||||||
file_object=file_object
|
file_object=file_object
|
||||||
)
|
)
|
||||||
|
|
||||||
self.user = get_user_model().objects.create(
|
self.user = get_user_model().on_organization.create(
|
||||||
username=TEST_USER_USERNAME
|
username=TEST_USER_USERNAME
|
||||||
)
|
)
|
||||||
self.group = Group.objects.create(name=TEST_GROUP)
|
self.group = MayanGroup.on_organization.create(name=TEST_GROUP)
|
||||||
self.role = Role.objects.create(label=TEST_ROLE_LABEL)
|
self.role = Role.on_organization.create(label=TEST_ROLE_LABEL)
|
||||||
|
|
||||||
self.group.user_set.add(self.user)
|
self.group.users.add(self.user)
|
||||||
self.role.groups.add(self.group)
|
self.role.organization_groups.add(self.group)
|
||||||
|
|
||||||
Permission.invalidate_cache()
|
Permission.invalidate_cache()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
for document_type in DocumentType.objects.all():
|
for document_type in DocumentType.on_organization.all():
|
||||||
document_type.delete()
|
document_type.delete()
|
||||||
|
|
||||||
|
super(PermissionTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_check_access_without_permissions(self):
|
def test_check_access_without_permissions(self):
|
||||||
with self.assertRaises(PermissionDenied):
|
with self.assertRaises(PermissionDenied):
|
||||||
AccessControlList.objects.check_access(
|
AccessControlList.objects.check_access(
|
||||||
@@ -75,7 +80,7 @@ class PermissionTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_check_access_with_acl(self):
|
def test_check_access_with_acl(self):
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_1, role=self.role
|
content_object=self.document_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
@@ -91,7 +96,7 @@ class PermissionTestCase(TestCase):
|
|||||||
def test_filtering_with_permissions(self):
|
def test_filtering_with_permissions(self):
|
||||||
self.role.permissions.add(permission_document_view.stored_permission)
|
self.role.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_1, role=self.role
|
content_object=self.document_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
@@ -104,7 +109,7 @@ class PermissionTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_check_access_with_inherited_acl(self):
|
def test_check_access_with_inherited_acl(self):
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_type_1, role=self.role
|
content_object=self.document_type_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
@@ -118,12 +123,12 @@ class PermissionTestCase(TestCase):
|
|||||||
self.fail('PermissionDenied exception was not expected.')
|
self.fail('PermissionDenied exception was not expected.')
|
||||||
|
|
||||||
def test_check_access_with_inherited_acl_and_local_acl(self):
|
def test_check_access_with_inherited_acl_and_local_acl(self):
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_type_1, role=self.role
|
content_object=self.document_type_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_3, role=self.role
|
content_object=self.document_3, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
@@ -139,7 +144,7 @@ class PermissionTestCase(TestCase):
|
|||||||
def test_filtering_with_inherited_permissions(self):
|
def test_filtering_with_inherited_permissions(self):
|
||||||
self.role.permissions.add(permission_document_view.stored_permission)
|
self.role.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_type_1, role=self.role
|
content_object=self.document_type_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
@@ -155,12 +160,12 @@ class PermissionTestCase(TestCase):
|
|||||||
def test_filtering_with_inherited_permissions_and_local_acl(self):
|
def test_filtering_with_inherited_permissions_and_local_acl(self):
|
||||||
self.role.permissions.add(permission_document_view.stored_permission)
|
self.role.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_type_1, role=self.role
|
content_object=self.document_type_1, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document_3, role=self.role
|
content_object=self.document_3, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_view.stored_permission)
|
acl.permissions.add(permission_document_view.stored_permission)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 403)
|
self.assertEquals(response.status_code, 403)
|
||||||
self.assertEqual(AccessControlList.objects.count(), 0)
|
self.assertEqual(AccessControlList.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_acl_create_view_with_permission(self):
|
def test_acl_create_view_with_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
@@ -49,7 +49,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, text='created', status_code=200)
|
self.assertContains(response, text='created', status_code=200)
|
||||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_acl_create_duplicate_view_with_permission(self):
|
def test_acl_create_duplicate_view_with_permission(self):
|
||||||
"""
|
"""
|
||||||
@@ -57,7 +57,7 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
|||||||
Result: Should redirect to existing ACL for object + role combination
|
Result: Should redirect to existing ACL for object + role combination
|
||||||
"""
|
"""
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document, role=self.role
|
content_object=self.document, role=self.role
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,8 +76,8 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertContains(
|
self.assertContains(
|
||||||
response, text='vailable permissions', status_code=200
|
response, text='vailable permissions', status_code=200
|
||||||
)
|
)
|
||||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||||
self.assertEqual(AccessControlList.objects.first().pk, acl.pk)
|
self.assertEqual(AccessControlList.on_organization.first().pk, acl.pk)
|
||||||
|
|
||||||
def test_orphan_acl_create_view_with_permission(self):
|
def test_orphan_acl_create_view_with_permission(self):
|
||||||
"""
|
"""
|
||||||
@@ -108,4 +108,4 @@ class AccessControlListViewTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertNotContains(response, text='optgroup', status_code=200)
|
self.assertNotContains(response, text='optgroup', status_code=200)
|
||||||
self.assertEqual(AccessControlList.objects.count(), 1)
|
self.assertEqual(AccessControlList.on_organization.count(), 1)
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class ACLCreateView(SingleObjectCreateView):
|
class ACLCreateView(SingleObjectCreateView):
|
||||||
fields = ('role',)
|
fields = ('role',)
|
||||||
model = AccessControlList
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.content_type = get_object_or_404(
|
self.content_type = get_object_or_404(
|
||||||
@@ -52,14 +51,9 @@ class ACLCreateView(SingleObjectCreateView):
|
|||||||
|
|
||||||
return super(ACLCreateView, self).dispatch(request, *args, **kwargs)
|
return super(ACLCreateView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_instance_extra_data(self):
|
|
||||||
return {
|
|
||||||
'content_object': self.content_object
|
|
||||||
}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
try:
|
try:
|
||||||
acl = AccessControlList.objects.get(
|
acl = AccessControlList.on_organization.get(
|
||||||
content_type=self.content_type,
|
content_type=self.content_type,
|
||||||
object_id=self.content_object.pk,
|
object_id=self.content_object.pk,
|
||||||
role=form.cleaned_data['role']
|
role=form.cleaned_data['role']
|
||||||
@@ -79,6 +73,14 @@ class ACLCreateView(SingleObjectCreateView):
|
|||||||
) % self.content_object
|
) % self.content_object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_instance_extra_data(self):
|
||||||
|
return {
|
||||||
|
'content_object': self.content_object
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return AccessControlList.on_organization.all()
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
if self.object.pk:
|
if self.object.pk:
|
||||||
return reverse('acls:acl_permissions', args=(self.object.pk,))
|
return reverse('acls:acl_permissions', args=(self.object.pk,))
|
||||||
@@ -87,8 +89,6 @@ class ACLCreateView(SingleObjectCreateView):
|
|||||||
|
|
||||||
|
|
||||||
class ACLDeleteView(SingleObjectDeleteView):
|
class ACLDeleteView(SingleObjectDeleteView):
|
||||||
model = AccessControlList
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
acl = get_object_or_404(AccessControlList, pk=self.kwargs['pk'])
|
acl = get_object_or_404(AccessControlList, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
@@ -118,6 +118,9 @@ class ACLDeleteView(SingleObjectDeleteView):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return AccessControlList.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class ACLListView(SingleObjectListView):
|
class ACLListView(SingleObjectListView):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
@@ -152,7 +155,7 @@ class ACLListView(SingleObjectListView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return AccessControlList.objects.filter(
|
return AccessControlList.on_organization.filter(
|
||||||
content_type=self.content_type, object_id=self.content_object.pk
|
content_type=self.content_type, object_id=self.content_object.pk
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
{% if not request.user.is_authenticated %}
|
{% if not request.user.is_authenticated %}
|
||||||
{% trans 'Anonymous' %}
|
{% trans 'Anonymous' %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{% url 'common:current_user_details' %}" title="{% trans 'User details' %}">{{ request.user.get_full_name|default:request.user }} <i class="fa fa-user"></i></a></li>
|
<li><a href="{% url 'common:current_user_details' %}" title="{% trans 'User details' %}">{{ request.organization }}: {{ request.user.get_full_name|default:request.user }} <i class="fa fa-user"></i></a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class EmailAuthBackend(ModelBackend):
|
|||||||
def authenticate(self, email=None, password=None):
|
def authenticate(self, email=None, password=None):
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
try:
|
try:
|
||||||
user = UserModel.objects.get(email=email)
|
user = UserModel.on_organization.get(email=email)
|
||||||
if user.check_password(password):
|
if user.check_password(password):
|
||||||
return user
|
return user
|
||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
|
|||||||
20
mayan/apps/authentication/auth/model_auth_backend.py
Normal file
20
mayan/apps/authentication/auth/model_auth_backend.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
|
||||||
|
|
||||||
|
class UsernameModelBackend(ModelBackend):
|
||||||
|
def authenticate(self, username=None, password=None, **kwargs):
|
||||||
|
UserModel = get_user_model()
|
||||||
|
if username is None:
|
||||||
|
username = kwargs.get(UserModel.USERNAME_FIELD)
|
||||||
|
try:
|
||||||
|
user = UserModel.on_organization.get(username=username)
|
||||||
|
if user.check_password(password):
|
||||||
|
return user
|
||||||
|
except UserModel.DoesNotExist:
|
||||||
|
# Run the default password hasher once to reduce the timing
|
||||||
|
# difference between an existing and a non-existing user (#20760).
|
||||||
|
UserModel().set_password(password)
|
||||||
@@ -6,6 +6,7 @@ from django.core.urlresolvers import reverse
|
|||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
|
|
||||||
|
from organizations.tests.base import OrganizationTestCase
|
||||||
from user_management.tests.literals import (
|
from user_management.tests.literals import (
|
||||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||||
)
|
)
|
||||||
@@ -13,18 +14,22 @@ from user_management.tests.literals import (
|
|||||||
from .literals import TEST_EMAIL_AUTHENTICATION_BACKEND
|
from .literals import TEST_EMAIL_AUTHENTICATION_BACKEND
|
||||||
|
|
||||||
|
|
||||||
class UserLoginTestCase(TestCase):
|
class UserLoginTestCase(OrganizationTestCase):
|
||||||
"""
|
"""
|
||||||
Test that users can login via the supported authentication methods
|
Test that users can login via the supported authentication methods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super(UserLoginTestCase, self).setUp()
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
self.admin_user = get_user_model().objects.create_superuser(
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||||
password=TEST_ADMIN_PASSWORD
|
password=TEST_ADMIN_PASSWORD
|
||||||
)
|
)
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(UserLoginTestCase, self).tearDown()
|
||||||
|
|
||||||
@override_settings(AUTHENTICATION_LOGIN_METHOD='username')
|
@override_settings(AUTHENTICATION_LOGIN_METHOD='username')
|
||||||
def test_normal_behaviour(self):
|
def test_normal_behaviour(self):
|
||||||
response = self.client.get(reverse('documents:document_list'))
|
response = self.client.get(reverse('documents:document_list'))
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
return DocumentCheckoutSerializer
|
return DocumentCheckoutSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
documents = DocumentCheckout.objects.checked_out_documents()
|
documents = DocumentCheckout.on_organization.checked_out_documents()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -44,7 +44,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
else:
|
else:
|
||||||
filtered_documents = documents
|
filtered_documents = documents
|
||||||
|
|
||||||
return DocumentCheckout.objects.filter(
|
return DocumentCheckout.on_organization.filter(
|
||||||
document__pk__in=filtered_documents.values_list('pk', flat=True)
|
document__pk__in=filtered_documents.values_list('pk', flat=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
document = get_object_or_404(
|
document = get_object_or_404(
|
||||||
Document, pk=serializer.data['document']
|
Document.on_organization, pk=serializer.data['document']
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -80,7 +80,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
timezone = pytz.utc
|
timezone = pytz.utc
|
||||||
|
|
||||||
try:
|
try:
|
||||||
DocumentCheckout.objects.create(
|
DocumentCheckout.on_organization.create(
|
||||||
document=document,
|
document=document,
|
||||||
expiration_datetime=timezone.localize(
|
expiration_datetime=timezone.localize(
|
||||||
serializer.data['expiration_datetime']
|
serializer.data['expiration_datetime']
|
||||||
@@ -104,7 +104,7 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.request.method == 'GET':
|
if self.request.method == 'GET':
|
||||||
documents = DocumentCheckout.objects.checked_out_documents()
|
documents = DocumentCheckout.on_organization.checked_out_documents()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -117,13 +117,13 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
|
|||||||
else:
|
else:
|
||||||
filtered_documents = documents
|
filtered_documents = documents
|
||||||
|
|
||||||
return DocumentCheckout.objects.filter(
|
return DocumentCheckout.on_organization.filter(
|
||||||
document__pk__in=filtered_documents.values_list(
|
document__pk__in=filtered_documents.values_list(
|
||||||
'pk', flat=True
|
'pk', flat=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif self.request.method == 'DELETE':
|
elif self.request.method == 'DELETE':
|
||||||
return DocumentCheckout.objects.all()
|
return DocumentCheckout.on_organization.all()
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -43,23 +43,23 @@ class CheckoutsApp(MayanAppConfig):
|
|||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'check_in',
|
'check_in',
|
||||||
lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user)
|
lambda document, user=None: DocumentCheckout.on_organization.check_in_document(document, user)
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'checkout_info',
|
'checkout_info',
|
||||||
lambda document: DocumentCheckout.objects.document_checkout_info(
|
lambda document: DocumentCheckout.on_organization.document_checkout_info(
|
||||||
document
|
document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'checkout_state',
|
'checkout_state',
|
||||||
lambda document: DocumentCheckout.objects.document_checkout_state(
|
lambda document: DocumentCheckout.on_organization.document_checkout_state(
|
||||||
document
|
document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'is_checked_out',
|
'is_checked_out',
|
||||||
lambda document: DocumentCheckout.objects.is_document_checked_out(
|
lambda document: DocumentCheckout.on_organization.is_document_checked_out(
|
||||||
document
|
document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
@@ -25,15 +26,15 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def checked_out_documents(self):
|
def checked_out_documents(self):
|
||||||
return Document.objects.filter(
|
return Document.on_organization.filter(
|
||||||
pk__in=self.model.objects.all().values_list(
|
pk__in=self.all().values_list(
|
||||||
'document__pk', flat=True
|
'document__pk', flat=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def expired_check_outs(self):
|
def expired_check_outs(self):
|
||||||
expired_list = Document.objects.filter(
|
expired_list = Document.on_organization.filter(
|
||||||
pk__in=self.model.objects.filter(
|
pk__in=self.filter(
|
||||||
expiration_datetime__lte=now()
|
expiration_datetime__lte=now()
|
||||||
).values_list('document__pk', flat=True)
|
).values_list('document__pk', flat=True)
|
||||||
)
|
)
|
||||||
@@ -45,14 +46,14 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
document.check_in()
|
document.check_in()
|
||||||
|
|
||||||
def is_document_checked_out(self, document):
|
def is_document_checked_out(self, document):
|
||||||
if self.model.objects.filter(document=document):
|
if self.model.on_organization.filter(document=document):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_in_document(self, document, user=None):
|
def check_in_document(self, document, user=None):
|
||||||
try:
|
try:
|
||||||
document_checkout = self.model.objects.get(document=document)
|
document_checkout = self.get(document=document)
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
raise DocumentNotCheckedOut
|
raise DocumentNotCheckedOut
|
||||||
else:
|
else:
|
||||||
@@ -70,7 +71,7 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
|
|
||||||
def document_checkout_info(self, document):
|
def document_checkout_info(self, document):
|
||||||
try:
|
try:
|
||||||
return self.model.objects.get(document=document)
|
return self.get(document=document)
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
raise DocumentNotCheckedOut
|
raise DocumentNotCheckedOut
|
||||||
|
|
||||||
@@ -87,3 +88,14 @@ class DocumentCheckoutManager(models.Manager):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return not checkout_info.block_new_version
|
return not checkout_info.block_new_version
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentCheckoutManager(DocumentCheckoutManager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Document = apps.get_model('documents', 'Document')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentCheckoutManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document__in=Document.on_organization.all()
|
||||||
|
)
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ from documents.models import Document, NewVersionBlock
|
|||||||
|
|
||||||
from .events import event_document_check_out
|
from .events import event_document_check_out
|
||||||
from .exceptions import DocumentAlreadyCheckedOut
|
from .exceptions import DocumentAlreadyCheckedOut
|
||||||
from .managers import DocumentCheckoutManager
|
from .managers import (
|
||||||
|
DocumentCheckoutManager, OrganizationDocumentCheckoutManager
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,6 +46,11 @@ class DocumentCheckout(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DocumentCheckoutManager()
|
objects = DocumentCheckoutManager()
|
||||||
|
on_organization = OrganizationDocumentCheckoutManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Document checkout')
|
||||||
|
verbose_name_plural = _('Document checkouts')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return unicode(self.document)
|
return unicode(self.document)
|
||||||
@@ -82,7 +89,3 @@ class DocumentCheckout(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Document checkout')
|
|
||||||
verbose_name_plural = _('Document checkouts')
|
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from documents.serializers import DocumentSerializer
|
||||||
|
|
||||||
from .models import DocumentCheckout
|
from .models import DocumentCheckout
|
||||||
|
|
||||||
|
|
||||||
class DocumentCheckoutSerializer(serializers.ModelSerializer):
|
class DocumentCheckoutSerializer(serializers.ModelSerializer):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Hide this import otherwise strange circular import error occur
|
|
||||||
from documents.serializers import DocumentSerializer
|
|
||||||
|
|
||||||
super(DocumentCheckoutSerializer, self).__init__(*args, **kwargs)
|
super(DocumentCheckoutSerializer, self).__init__(*args, **kwargs)
|
||||||
self.fields['document'] = DocumentSerializer()
|
self.fields['document'] = DocumentSerializer()
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ def task_check_expired_check_outs():
|
|||||||
name=lock_id, timeout=CHECKOUT_EXPIRATION_LOCK_EXPIRE
|
name=lock_id, timeout=CHECKOUT_EXPIRATION_LOCK_EXPIRE
|
||||||
)
|
)
|
||||||
logger.debug('acquired lock: %s', lock_id)
|
logger.debug('acquired lock: %s', lock_id)
|
||||||
DocumentCheckout.objects.check_in_expired_check_outs()
|
DocumentCheckout.on_organization.check_in_expired_check_outs()
|
||||||
lock.release()
|
lock.release()
|
||||||
except LockError:
|
except LockError:
|
||||||
logger.debug('unable to obtain lock')
|
logger.debug('unable to obtain lock')
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from documents.models import DocumentType
|
|||||||
from documents.tests.literals import (
|
from documents.tests.literals import (
|
||||||
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
)
|
)
|
||||||
|
from organizations.tests.base import OrganizationTestCase
|
||||||
from user_management.tests.literals import (
|
from user_management.tests.literals import (
|
||||||
TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD
|
TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD
|
||||||
)
|
)
|
||||||
@@ -21,14 +22,15 @@ from ..models import DocumentCheckout
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class DocumentCheckoutTestCase(TestCase):
|
class DocumentCheckoutTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(DocumentCheckoutTestCase, self).setUp()
|
||||||
|
self.admin_user = get_user_model().on_organization.create_superuser(
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||||
password=TEST_ADMIN_PASSWORD
|
password=TEST_ADMIN_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,18 +41,19 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentCheckoutTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_checkout(self):
|
def test_document_checkout(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertTrue(self.document.is_checked_out())
|
self.assertTrue(self.document.is_checked_out())
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
DocumentCheckout.objects.is_document_checked_out(
|
DocumentCheckout.on_organization.is_document_checked_out(
|
||||||
document=self.document
|
document=self.document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -58,7 +61,7 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
def test_version_creation_blocking(self):
|
def test_version_creation_blocking(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -70,7 +73,7 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
def test_checkin_in(self):
|
def test_checkin_in(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -79,7 +82,7 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertFalse(self.document.is_checked_out())
|
self.assertFalse(self.document.is_checked_out())
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
DocumentCheckout.objects.is_document_checked_out(
|
DocumentCheckout.on_organization.is_document_checked_out(
|
||||||
document=self.document
|
document=self.document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -87,13 +90,13 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
def test_double_checkout(self):
|
def test_double_checkout(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaises(DocumentAlreadyCheckedOut):
|
with self.assertRaises(DocumentAlreadyCheckedOut):
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document,
|
document=self.document,
|
||||||
expiration_datetime=expiration_datetime, user=self.admin_user,
|
expiration_datetime=expiration_datetime, user=self.admin_user,
|
||||||
block_new_version=True
|
block_new_version=True
|
||||||
@@ -106,13 +109,13 @@ class DocumentCheckoutTestCase(TestCase):
|
|||||||
def test_auto_checkin(self):
|
def test_auto_checkin(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(seconds=1)
|
expiration_datetime = now() + datetime.timedelta(seconds=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
DocumentCheckout.objects.check_in_expired_check_outs()
|
DocumentCheckout.on_organization.check_in_expired_check_outs()
|
||||||
|
|
||||||
self.assertFalse(self.document.is_checked_out())
|
self.assertFalse(self.document.is_checked_out())
|
||||||
|
|||||||
97
mayan/apps/checkouts/tests/test_organization_views.py
Normal file
97
mayan/apps/checkouts/tests/test_organization_views.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from common.literals import TIME_DELTA_UNIT_DAYS
|
||||||
|
from documents.models import DocumentType
|
||||||
|
from documents.tests.literals import (
|
||||||
|
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
|
)
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import DocumentCheckout
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentCheckoutTestCase(OrganizationViewTestCase):
|
||||||
|
def create_document(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
|
self.document = self.document_type.new_document(
|
||||||
|
file_object=file_object
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_out_document(self):
|
||||||
|
self.create_document()
|
||||||
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
|
||||||
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
|
document=self.document,
|
||||||
|
expiration_datetime=expiration_datetime, user=self.user,
|
||||||
|
block_new_version=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_checkout_info(self):
|
||||||
|
self.check_out_document()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
response = self.get(
|
||||||
|
'checkouts:checkout_info', args=(self.document.pk,),
|
||||||
|
follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.get(
|
||||||
|
'checkouts:checkout_info', args=(self.document.pk,),
|
||||||
|
follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_check_in(self):
|
||||||
|
self.check_out_document()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.get(
|
||||||
|
'checkouts:checkin_document', args=(self.document.pk,),
|
||||||
|
follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(response.status_code, 404)
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.assertTrue(self.document.is_checked_out())
|
||||||
|
|
||||||
|
def test_check_out(self):
|
||||||
|
self.create_document()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'checkouts:checkout_document', args=(self.document.pk,), data={
|
||||||
|
'expiration_datetime_0': 2,
|
||||||
|
'expiration_datetime_1': TIME_DELTA_UNIT_DAYS,
|
||||||
|
'block_new_version': True
|
||||||
|
}, follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEquals(response.status_code, 404)
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.assertFalse(self.document.is_checked_out())
|
||||||
|
|
||||||
|
def test_checkout_list(self):
|
||||||
|
self.check_out_document()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.get('checkouts:checkout_list')
|
||||||
|
|
||||||
|
self.assertNotContains(
|
||||||
|
response, text=self.document.label, status_code=200
|
||||||
|
)
|
||||||
@@ -27,7 +27,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.user, block_new_version=True
|
user=self.user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -49,7 +49,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.user, block_new_version=True
|
user=self.user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -72,7 +72,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertFalse(self.document.is_checked_out())
|
self.assertFalse(self.document.is_checked_out())
|
||||||
|
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
DocumentCheckout.objects.is_document_checked_out(
|
DocumentCheckout.on_organization.is_document_checked_out(
|
||||||
document=self.document
|
document=self.document
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -134,7 +134,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -169,7 +169,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
@@ -200,7 +200,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
|
|||||||
def test_forcefull_check_in_document_view_with_permission(self):
|
def test_forcefull_check_in_document_view_with_permission(self):
|
||||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
DocumentCheckout.objects.checkout_document(
|
DocumentCheckout.on_organization.checkout_document(
|
||||||
document=self.document, expiration_datetime=expiration_datetime,
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
user=self.admin_user, block_new_version=True
|
user=self.admin_user, block_new_version=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class CheckoutDocumentView(SingleObjectCreateView):
|
|||||||
form_class = DocumentCheckoutForm
|
form_class = DocumentCheckoutForm
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
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:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -103,12 +103,11 @@ class CheckoutListView(DocumentListView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_document_queryset(self):
|
def get_document_queryset(self):
|
||||||
return DocumentCheckout.objects.checked_out_documents()
|
return DocumentCheckout.on_organization.checked_out_documents()
|
||||||
|
|
||||||
|
|
||||||
class CheckoutDetailView(SingleObjectDetailView):
|
class CheckoutDetailView(SingleObjectDetailView):
|
||||||
form_class = DocumentCheckoutDefailForm
|
form_class = DocumentCheckoutDefailForm
|
||||||
model = Document
|
|
||||||
object_permission = permission_document_checkout_detail_view
|
object_permission = permission_document_checkout_detail_view
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
@@ -120,7 +119,7 @@ class CheckoutDetailView(SingleObjectDetailView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
|
||||||
class DocumentCheckinView(ConfirmView):
|
class DocumentCheckinView(ConfirmView):
|
||||||
@@ -142,7 +141,7 @@ class DocumentCheckinView(ConfirmView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_post_action_redirect(self):
|
def get_post_action_redirect(self):
|
||||||
return reverse('checkouts:checkout_info', args=(self.get_object().pk,))
|
return reverse('checkouts:checkout_info', args=(self.get_object().pk,))
|
||||||
|
|||||||
@@ -17,4 +17,5 @@ class Command(management.BaseCommand):
|
|||||||
self.stderr.write(self.style.NOTICE('Unable to migrate the database. The initialsetup command is to be used only on new installations. To upgrade existing installations use the performupgrade command.'))
|
self.stderr.write(self.style.NOTICE('Unable to migrate the database. The initialsetup command is to be used only on new installations. To upgrade existing installations use the performupgrade command.'))
|
||||||
raise
|
raise
|
||||||
management.call_command('createautoadmin', interactive=False)
|
management.call_command('createautoadmin', interactive=False)
|
||||||
|
management.call_command('createorganizationadmin', interactive=False)
|
||||||
post_initial_setup.send(sender=self)
|
post_initial_setup.send(sender=self)
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.core.urlresolvers import clear_url_caches, reverse
|
from django.core.urlresolvers import clear_url_caches, reverse
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from organizations.tests.base import OrganizationTestCase
|
||||||
from permissions import Permission
|
from permissions import Permission
|
||||||
from permissions.models import Role
|
from permissions.models import Role
|
||||||
from permissions.tests.literals import TEST_ROLE_LABEL
|
from permissions.tests.literals import TEST_ROLE_LABEL
|
||||||
|
from user_management.models import MayanGroup
|
||||||
from user_management.tests import (
|
from user_management.tests import (
|
||||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_GROUP,
|
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_GROUP,
|
||||||
TEST_USER_EMAIL, TEST_USER_USERNAME, TEST_USER_PASSWORD
|
TEST_USER_EMAIL, TEST_USER_USERNAME, TEST_USER_PASSWORD
|
||||||
@@ -19,23 +20,24 @@ from user_management.tests import (
|
|||||||
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
|
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
|
||||||
|
|
||||||
|
|
||||||
class GenericViewTestCase(TestCase):
|
class GenericViewTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super(GenericViewTestCase, self).setUp()
|
||||||
self.has_test_view = False
|
self.has_test_view = False
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
self.admin_user = get_user_model().on_organization.create_superuser(
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||||
password=TEST_ADMIN_PASSWORD
|
password=TEST_ADMIN_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.user = get_user_model().objects.create_user(
|
self.user = get_user_model().on_organization.create_user(
|
||||||
username=TEST_USER_USERNAME, email=TEST_USER_EMAIL,
|
username=TEST_USER_USERNAME, email=TEST_USER_EMAIL,
|
||||||
password=TEST_USER_PASSWORD
|
password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.group = Group.objects.create(name=TEST_GROUP)
|
self.group = MayanGroup.on_organization.create(name=TEST_GROUP)
|
||||||
self.role = Role.objects.create(label=TEST_ROLE_LABEL)
|
self.role = Role.on_organization.create(label=TEST_ROLE_LABEL)
|
||||||
self.group.user_set.add(self.user)
|
self.group.users.add(self.user)
|
||||||
self.role.groups.add(self.group)
|
self.role.organization_groups.add(self.group)
|
||||||
Permission.invalidate_cache()
|
Permission.invalidate_cache()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -45,6 +47,8 @@ class GenericViewTestCase(TestCase):
|
|||||||
if self.has_test_view:
|
if self.has_test_view:
|
||||||
urlpatterns.pop(0)
|
urlpatterns.pop(0)
|
||||||
|
|
||||||
|
super(GenericViewTestCase, self).tearDown()
|
||||||
|
|
||||||
def add_test_view(self, test_object):
|
def add_test_view(self, test_object):
|
||||||
from mayan.urls import urlpatterns
|
from mayan.urls import urlpatterns
|
||||||
|
|
||||||
@@ -77,7 +81,7 @@ class GenericViewTestCase(TestCase):
|
|||||||
def login(self, username, password):
|
def login(self, username, password):
|
||||||
logged_in = self.client.login(username=username, password=password)
|
logged_in = self.client.login(username=username, password=password)
|
||||||
|
|
||||||
user = get_user_model().objects.get(username=username)
|
user = get_user_model().on_organization.get(username=username)
|
||||||
|
|
||||||
self.assertTrue(logged_in)
|
self.assertTrue(logged_in)
|
||||||
self.assertTrue(user.is_authenticated())
|
self.assertTrue(user.is_authenticated())
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from organizations.admin import OrganizationAdminMixin
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Workflow, WorkflowInstance, WorkflowInstanceLogEntry, WorkflowState,
|
Workflow, WorkflowInstance, WorkflowInstanceLogEntry, WorkflowState,
|
||||||
WorkflowTransition
|
WorkflowTransition
|
||||||
@@ -22,7 +24,7 @@ class WorkflowTransitionInline(admin.TabularInline):
|
|||||||
|
|
||||||
|
|
||||||
@admin.register(Workflow)
|
@admin.register(Workflow)
|
||||||
class WorkflowAdmin(admin.ModelAdmin):
|
class WorkflowAdmin(OrganizationAdminMixin, admin.ModelAdmin):
|
||||||
def document_types_list(self, instance):
|
def document_types_list(self, instance):
|
||||||
return ','.join(
|
return ','.join(
|
||||||
instance.document_types.values_list('label', flat=True)
|
instance.document_types.values_list('label', flat=True)
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ def launch_workflow(sender, instance, created, **kwargs):
|
|||||||
Workflow = get_model('document_states', 'Workflow')
|
Workflow = get_model('document_states', 'Workflow')
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
Workflow.objects.launch_for(instance)
|
Workflow.on_organization.launch_for(instance)
|
||||||
|
|||||||
@@ -1,7 +1,62 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationWorkflowStateManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Workflow = apps.get_model('document_states', 'Workflow')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationWorkflowStateManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
workflow__in=Workflow.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationWorkflowTransitionManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Workflow = apps.get_model('document_states', 'Workflow')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationWorkflowTransitionManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
workflow__in=Workflow.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationWorkflowInstanceManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Document = apps.get_model('documents', 'Document')
|
||||||
|
Workflow = apps.get_model('document_states', 'Workflow')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationWorkflowInstanceManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
workflow__in=Workflow.on_organization.all(),
|
||||||
|
document__in=Document.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationWorkflowInstanceLogEntryManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Workflow = apps.get_model('document_states', 'Workflow')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationWorkflowInstanceLogEntryManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
workflow_instance__workflow__in=Workflow.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowManager(models.Manager):
|
class WorkflowManager(models.Manager):
|
||||||
def launch_for(self, document):
|
def launch_for(self, document):
|
||||||
for workflow in document.document_type.workflows.all():
|
for workflow in document.document_type.workflows.all():
|
||||||
workflow.launch_for(document)
|
workflow.launch_for(document)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationWorkflowManager(WorkflowManager, CurrentOrganizationManager):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- 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'),
|
||||||
|
('document_states', '0002_workflowstate_completion'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='workflow',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,14 +9,23 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from documents.models import Document, DocumentType
|
from documents.models import Document, DocumentType
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
|
|
||||||
from .managers import WorkflowManager
|
from .managers import (
|
||||||
|
OrganizationWorkflowManager, OrganizationWorkflowStateManager,
|
||||||
|
OrganizationWorkflowTransitionManager, OrganizationWorkflowInstanceManager,
|
||||||
|
OrganizationWorkflowInstanceLogEntryManager, WorkflowManager
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Workflow(models.Model):
|
class Workflow(models.Model):
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=255, unique=True, verbose_name=_('Label')
|
max_length=255, unique=True, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
@@ -26,12 +35,19 @@ class Workflow(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = WorkflowManager()
|
objects = WorkflowManager()
|
||||||
|
on_organization = OrganizationWorkflowManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Workflow')
|
||||||
|
verbose_name_plural = _('Workflows')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
def get_document_types_not_in_workflow(self):
|
def get_document_types_not_in_workflow(self):
|
||||||
return DocumentType.objects.exclude(pk__in=self.document_types.all())
|
return DocumentType.on_organization.exclude(
|
||||||
|
pk__in=self.document_types.all()
|
||||||
|
)
|
||||||
|
|
||||||
def get_initial_state(self):
|
def get_initial_state(self):
|
||||||
try:
|
try:
|
||||||
@@ -54,10 +70,6 @@ class Workflow(models.Model):
|
|||||||
'Workflow %s launched for document %s', self, document
|
'Workflow %s launched for document %s', self, document
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Workflow')
|
|
||||||
verbose_name_plural = _('Workflows')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class WorkflowState(models.Model):
|
class WorkflowState(models.Model):
|
||||||
@@ -79,6 +91,14 @@ class WorkflowState(models.Model):
|
|||||||
), verbose_name=_('Completion')
|
), verbose_name=_('Completion')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationWorkflowStateManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('workflow', 'label')
|
||||||
|
verbose_name = _('Workflow state')
|
||||||
|
verbose_name_plural = _('Workflow states')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
@@ -87,11 +107,6 @@ class WorkflowState(models.Model):
|
|||||||
self.workflow.states.all().update(initial=False)
|
self.workflow.states.all().update(initial=False)
|
||||||
return super(WorkflowState, self).save(*args, **kwargs)
|
return super(WorkflowState, self).save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('workflow', 'label')
|
|
||||||
verbose_name = _('Workflow state')
|
|
||||||
verbose_name_plural = _('Workflow states')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class WorkflowTransition(models.Model):
|
class WorkflowTransition(models.Model):
|
||||||
@@ -109,8 +124,8 @@ class WorkflowTransition(models.Model):
|
|||||||
verbose_name=_('Destination state')
|
verbose_name=_('Destination state')
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
objects = models.Manager()
|
||||||
return self.label
|
on_organization = OrganizationWorkflowTransitionManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (
|
unique_together = (
|
||||||
@@ -119,6 +134,9 @@ class WorkflowTransition(models.Model):
|
|||||||
verbose_name = _('Workflow transition')
|
verbose_name = _('Workflow transition')
|
||||||
verbose_name_plural = _('Workflow transitions')
|
verbose_name_plural = _('Workflow transitions')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.label
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class WorkflowInstance(models.Model):
|
class WorkflowInstance(models.Model):
|
||||||
@@ -129,6 +147,14 @@ class WorkflowInstance(models.Model):
|
|||||||
Document, related_name='workflows', verbose_name=_('Document')
|
Document, related_name='workflows', verbose_name=_('Document')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationWorkflowInstanceManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('document', 'workflow')
|
||||||
|
verbose_name = _('Workflow instance')
|
||||||
|
verbose_name_plural = _('Workflow instances')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return unicode(self.workflow)
|
return unicode(self.workflow)
|
||||||
|
|
||||||
@@ -168,11 +194,6 @@ class WorkflowInstance(models.Model):
|
|||||||
def get_transition_choices(self):
|
def get_transition_choices(self):
|
||||||
return self.get_current_state().origin_transitions.all()
|
return self.get_current_state().origin_transitions.all()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('document', 'workflow')
|
|
||||||
verbose_name = _('Workflow instance')
|
|
||||||
verbose_name_plural = _('Workflow instances')
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class WorkflowInstanceLogEntry(models.Model):
|
class WorkflowInstanceLogEntry(models.Model):
|
||||||
@@ -189,9 +210,12 @@ class WorkflowInstanceLogEntry(models.Model):
|
|||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('User'))
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('User'))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||||
|
|
||||||
def __str__(self):
|
objects = models.Manager()
|
||||||
return unicode(self.transition)
|
on_organization = OrganizationWorkflowInstanceLogEntryManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Workflow instance log entry')
|
verbose_name = _('Workflow instance log entry')
|
||||||
verbose_name_plural = _('Workflow instance log entries')
|
verbose_name_plural = _('Workflow instance log entries')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return unicode(self.transition)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
TEST_WORKFLOW_LABEL = 'test workflow'
|
TEST_WORKFLOW_LABEL = 'test workflow'
|
||||||
|
TEST_WORKFLOW_LABEL_EDITED = 'test workflow edited'
|
||||||
TEST_WORKFLOW_INITIAL_STATE_LABEL = 'test initial state'
|
TEST_WORKFLOW_INITIAL_STATE_LABEL = 'test initial state'
|
||||||
TEST_WORKFLOW_INITIAL_STATE_COMPLETION = 33
|
TEST_WORKFLOW_INITIAL_STATE_COMPLETION = 33
|
||||||
TEST_WORKFLOW_STATE_LABEL = 'test state'
|
TEST_WORKFLOW_STATE_LABEL = 'test state'
|
||||||
|
|||||||
59
mayan/apps/document_states/tests/test_organization_views.py
Normal file
59
mayan/apps/document_states/tests/test_organization_views.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.test import override_settings
|
||||||
|
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import Workflow
|
||||||
|
|
||||||
|
from .literals import (
|
||||||
|
TEST_WORKFLOW_LABEL, TEST_WORKFLOW_LABEL_EDITED,
|
||||||
|
TEST_WORKFLOW_INITIAL_STATE_LABEL, TEST_WORKFLOW_INITIAL_STATE_COMPLETION,
|
||||||
|
TEST_WORKFLOW_STATE_LABEL, TEST_WORKFLOW_STATE_COMPLETION,
|
||||||
|
TEST_WORKFLOW_TRANSITION_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
|
class OrganizationWorkflowiewTestCase(OrganizationViewTestCase):
|
||||||
|
def create_workflow(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.workflow = Workflow.on_organization.create(label=TEST_WORKFLOW_LABEL)
|
||||||
|
|
||||||
|
def test_workflow_create_view(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_create',
|
||||||
|
data={'label': TEST_WORKFLOW_LABEL_EDITED}, follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(Workflow.on_organization.count(), 1)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
self.assertEqual(Workflow.on_organization.count(), 0)
|
||||||
|
|
||||||
|
def test_workflow_delete_view(self):
|
||||||
|
self.create_workflow()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_delete',
|
||||||
|
args=(self.workflow.pk,), follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_workflow_edit_view(self):
|
||||||
|
self.create_workflow()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_edit',
|
||||||
|
args=(self.workflow.pk,),
|
||||||
|
data={'label': TEST_WORKFLOW_LABEL_EDITED}, follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.assertEqual(
|
||||||
|
Workflow.on_organization.first().label, TEST_WORKFLOW_LABEL
|
||||||
|
)
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from documents.tests.test_views import GenericDocumentViewTestCase
|
||||||
from django.core.urlresolvers import reverse
|
from user_management.tests.literals import (
|
||||||
from django.test.client import Client
|
TEST_USER_PASSWORD, TEST_USER_USERNAME
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from documents.models import DocumentType
|
|
||||||
from documents.tests.literals import (
|
|
||||||
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
|
||||||
)
|
|
||||||
from user_management.tests import (
|
|
||||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..models import Workflow, WorkflowState, WorkflowTransition
|
from ..models import Workflow, WorkflowState, WorkflowTransition
|
||||||
|
from ..permissions import (
|
||||||
|
permission_workflow_create, permission_workflow_delete,
|
||||||
|
permission_workflow_edit
|
||||||
|
)
|
||||||
|
|
||||||
from .literals import (
|
from .literals import (
|
||||||
TEST_WORKFLOW_LABEL, TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
TEST_WORKFLOW_LABEL, TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
||||||
@@ -22,70 +18,67 @@ from .literals import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DocumentStateViewTestCase(TestCase):
|
class DocumentStateViewTestCase(GenericDocumentViewTestCase):
|
||||||
def setUp(self):
|
def create_workflow(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
self.workflow = Workflow.on_organization.create(
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
label=TEST_WORKFLOW_LABEL
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
self.client = Client()
|
|
||||||
# Login the admin user
|
|
||||||
logged_in = self.client.login(
|
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
self.assertTrue(logged_in)
|
|
||||||
self.assertTrue(self.admin_user.is_authenticated())
|
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
|
||||||
label=TEST_DOCUMENT_TYPE
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
def test_workflow_create_view_with_permission(self):
|
||||||
self.document = self.document_type.new_document(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
file_object=file_object
|
|
||||||
)
|
|
||||||
|
|
||||||
def tearDown(self):
|
self.role.permissions.add(
|
||||||
self.document_type.delete()
|
permission_workflow_create.stored_permission
|
||||||
|
)
|
||||||
|
|
||||||
def test_creating_workflow(self):
|
response = self.post(
|
||||||
response = self.client.post(
|
'document_states:setup_workflow_create', data={
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_create'
|
|
||||||
), data={
|
|
||||||
'label': TEST_WORKFLOW_LABEL,
|
'label': TEST_WORKFLOW_LABEL,
|
||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(Workflow.objects.count(), 1)
|
self.assertEquals(Workflow.on_organization.count(), 1)
|
||||||
self.assertEquals(Workflow.objects.all()[0].label, TEST_WORKFLOW_LABEL)
|
self.assertEquals(
|
||||||
|
Workflow.on_organization.all()[0].label, TEST_WORKFLOW_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
def test_delete_workflow(self):
|
def test_workflow_delete_view_with_permission(self):
|
||||||
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
|
self.create_workflow()
|
||||||
|
|
||||||
self.assertEquals(Workflow.objects.count(), 1)
|
self.assertEquals(Workflow.on_organization.count(), 1)
|
||||||
self.assertEquals(Workflow.objects.all()[0].label, TEST_WORKFLOW_LABEL)
|
self.assertEquals(
|
||||||
|
Workflow.on_organization.all()[0].label, TEST_WORKFLOW_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_delete', args=(workflow.pk,)
|
self.role.permissions.add(
|
||||||
), follow=True
|
permission_workflow_delete.stored_permission
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_delete', args=(self.workflow.pk,),
|
||||||
|
follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(Workflow.objects.count(), 0)
|
self.assertEquals(Workflow.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_create_workflow_state(self):
|
def test_workflow_state_create_view_with_permission(self):
|
||||||
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
|
self.create_workflow()
|
||||||
|
|
||||||
response = self.client.post(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_state_create',
|
self.role.permissions.add(
|
||||||
args=(workflow.pk,)
|
permission_workflow_edit.stored_permission
|
||||||
), data={
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_state_create',
|
||||||
|
args=(self.workflow.pk,), data={
|
||||||
'label': TEST_WORKFLOW_STATE_LABEL,
|
'label': TEST_WORKFLOW_STATE_LABEL,
|
||||||
'completion': TEST_WORKFLOW_STATE_COMPLETION,
|
'completion': TEST_WORKFLOW_STATE_COMPLETION,
|
||||||
}, follow=True
|
}, follow=True
|
||||||
@@ -93,50 +86,61 @@ class DocumentStateViewTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(WorkflowState.objects.count(), 1)
|
self.assertEquals(WorkflowState.on_organization.count(), 1)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
WorkflowState.objects.all()[0].label, TEST_WORKFLOW_STATE_LABEL
|
WorkflowState.on_organization.all()[0].label,
|
||||||
|
TEST_WORKFLOW_STATE_LABEL
|
||||||
)
|
)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
WorkflowState.objects.all()[0].completion,
|
WorkflowState.on_organization.all()[0].completion,
|
||||||
TEST_WORKFLOW_STATE_COMPLETION
|
TEST_WORKFLOW_STATE_COMPLETION
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delete_workflow_state(self):
|
def test_workflow_state_delete_view_with_permission(self):
|
||||||
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
|
self.create_workflow()
|
||||||
workflow_state = WorkflowState.objects.create(
|
|
||||||
workflow=workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
workflow_state = WorkflowState.on_organization.create(
|
||||||
|
workflow=self.workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
||||||
completion=TEST_WORKFLOW_STATE_COMPLETION
|
completion=TEST_WORKFLOW_STATE_COMPLETION
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_state_delete',
|
self.role.permissions.add(
|
||||||
args=(workflow_state.pk,)
|
permission_workflow_edit.stored_permission
|
||||||
), follow=True
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_state_delete',
|
||||||
|
args=(workflow_state.pk,), follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(WorkflowState.objects.count(), 0)
|
self.assertEquals(WorkflowState.on_organization.count(), 0)
|
||||||
self.assertEquals(Workflow.objects.count(), 1)
|
self.assertEquals(Workflow.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_create_workflow_transition(self):
|
def test_workflow_transition_create_view_with_permission(self):
|
||||||
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
|
self.create_workflow()
|
||||||
workflow_initial_state = WorkflowState.objects.create(
|
|
||||||
workflow=workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
workflow_initial_state = WorkflowState.on_organization.create(
|
||||||
|
workflow=self.workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
||||||
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
|
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
|
||||||
)
|
)
|
||||||
workflow_state = WorkflowState.objects.create(
|
workflow_state = WorkflowState.on_organization.create(
|
||||||
workflow=workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
workflow=self.workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
||||||
completion=TEST_WORKFLOW_STATE_COMPLETION
|
completion=TEST_WORKFLOW_STATE_COMPLETION
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_transition_create',
|
self.role.permissions.add(
|
||||||
args=(workflow.pk,)
|
permission_workflow_edit.stored_permission
|
||||||
), data={
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_transition_create',
|
||||||
|
args=(self.workflow.pk,), data={
|
||||||
'label': TEST_WORKFLOW_TRANSITION_LABEL,
|
'label': TEST_WORKFLOW_TRANSITION_LABEL,
|
||||||
'origin_state': workflow_initial_state.pk,
|
'origin_state': workflow_initial_state.pk,
|
||||||
'destination_state': workflow_state.pk,
|
'destination_state': workflow_state.pk,
|
||||||
@@ -145,47 +149,52 @@ class DocumentStateViewTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(WorkflowTransition.objects.count(), 1)
|
self.assertEquals(WorkflowTransition.on_organization.count(), 1)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
WorkflowTransition.objects.all()[0].label,
|
WorkflowTransition.on_organization.all()[0].label,
|
||||||
TEST_WORKFLOW_TRANSITION_LABEL
|
TEST_WORKFLOW_TRANSITION_LABEL
|
||||||
)
|
)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
WorkflowTransition.objects.all()[0].origin_state,
|
WorkflowTransition.on_organization.all()[0].origin_state,
|
||||||
workflow_initial_state
|
workflow_initial_state
|
||||||
)
|
)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
WorkflowTransition.objects.all()[0].destination_state,
|
WorkflowTransition.on_organization.all()[0].destination_state,
|
||||||
workflow_state
|
workflow_state
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delete_workflow_transition(self):
|
def test_workflow_transition_delete_view_with_permission(self):
|
||||||
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
|
self.create_workflow()
|
||||||
workflow_initial_state = WorkflowState.objects.create(
|
|
||||||
workflow=workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
workflow_initial_state = WorkflowState.on_organization.create(
|
||||||
|
workflow=self.workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
|
||||||
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
|
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
|
||||||
)
|
)
|
||||||
workflow_state = WorkflowState.objects.create(
|
workflow_state = WorkflowState.on_organization.create(
|
||||||
workflow=workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
workflow=self.workflow, label=TEST_WORKFLOW_STATE_LABEL,
|
||||||
completion=TEST_WORKFLOW_STATE_COMPLETION
|
completion=TEST_WORKFLOW_STATE_COMPLETION
|
||||||
)
|
)
|
||||||
workflow_transition = WorkflowTransition.objects.create(
|
workflow_transition = WorkflowTransition.on_organization.create(
|
||||||
workflow=workflow, label=TEST_WORKFLOW_TRANSITION_LABEL,
|
workflow=self.workflow, label=TEST_WORKFLOW_TRANSITION_LABEL,
|
||||||
origin_state=workflow_initial_state,
|
origin_state=workflow_initial_state,
|
||||||
destination_state=workflow_state
|
destination_state=workflow_state
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(WorkflowTransition.objects.count(), 1)
|
self.assertEquals(WorkflowTransition.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.client.post(
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
reverse(
|
|
||||||
'document_states:setup_workflow_transition_delete',
|
self.role.permissions.add(
|
||||||
args=(workflow_transition.pk,)
|
permission_workflow_edit.stored_permission
|
||||||
), follow=True
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'document_states:setup_workflow_transition_delete',
|
||||||
|
args=(workflow_transition.pk,), follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(WorkflowState.objects.count(), 2)
|
self.assertEquals(WorkflowState.on_organization.count(), 2)
|
||||||
self.assertEquals(Workflow.objects.count(), 1)
|
self.assertEquals(Workflow.on_organization.count(), 1)
|
||||||
self.assertEquals(WorkflowTransition.objects.count(), 0)
|
self.assertEquals(WorkflowTransition.on_organization.count(), 0)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
|
|||||||
).dispatch(request, *args, **kwargs)
|
).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_document(self):
|
def get_document(self):
|
||||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
@@ -64,7 +64,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
|
|||||||
|
|
||||||
class WorkflowDocumentListView(DocumentListView):
|
class WorkflowDocumentListView(DocumentListView):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.workflow = get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
self.workflow = get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -80,7 +80,7 @@ class WorkflowDocumentListView(DocumentListView):
|
|||||||
).dispatch(request, *args, **kwargs)
|
).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_document_queryset(self):
|
def get_document_queryset(self):
|
||||||
return Document.objects.filter(
|
return Document.on_organization.filter(
|
||||||
document_type__in=self.workflow.document_types.all()
|
document_type__in=self.workflow.document_types.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ class WorkflowInstanceDetailView(SingleObjectListView):
|
|||||||
return self.get_workflow_instance().log_entries.order_by('-datetime')
|
return self.get_workflow_instance().log_entries.order_by('-datetime')
|
||||||
|
|
||||||
def get_workflow_instance(self):
|
def get_workflow_instance(self):
|
||||||
return get_object_or_404(WorkflowInstance, pk=self.kwargs['pk'])
|
return get_object_or_404(WorkflowInstance.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
|
||||||
class WorkflowInstanceTransitionView(FormView):
|
class WorkflowInstanceTransitionView(FormView):
|
||||||
@@ -175,7 +175,7 @@ class WorkflowInstanceTransitionView(FormView):
|
|||||||
return self.get_workflow_instance().get_absolute_url()
|
return self.get_workflow_instance().get_absolute_url()
|
||||||
|
|
||||||
def get_workflow_instance(self):
|
def get_workflow_instance(self):
|
||||||
return get_object_or_404(WorkflowInstance, pk=self.kwargs['pk'])
|
return get_object_or_404(WorkflowInstance.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
@@ -185,29 +185,37 @@ class SetupWorkflowListView(SingleObjectListView):
|
|||||||
'title': _('Workflows'),
|
'title': _('Workflows'),
|
||||||
'hide_link': True,
|
'hide_link': True,
|
||||||
}
|
}
|
||||||
model = Workflow
|
|
||||||
view_permission = permission_workflow_view
|
view_permission = permission_workflow_view
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Workflow.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class SetupWorkflowCreateView(SingleObjectCreateView):
|
class SetupWorkflowCreateView(SingleObjectCreateView):
|
||||||
form_class = WorkflowForm
|
form_class = WorkflowForm
|
||||||
model = Workflow
|
|
||||||
view_permission = permission_workflow_create
|
view_permission = permission_workflow_create
|
||||||
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Workflow.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class SetupWorkflowEditView(SingleObjectEditView):
|
class SetupWorkflowEditView(SingleObjectEditView):
|
||||||
form_class = WorkflowForm
|
form_class = WorkflowForm
|
||||||
model = Workflow
|
|
||||||
view_permission = permission_workflow_edit
|
view_permission = permission_workflow_edit
|
||||||
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Workflow.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class SetupWorkflowDeleteView(SingleObjectDeleteView):
|
class SetupWorkflowDeleteView(SingleObjectDeleteView):
|
||||||
model = Workflow
|
|
||||||
view_permission = permission_workflow_delete
|
view_permission = permission_workflow_delete
|
||||||
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Workflow.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class SetupWorkflowDocumentTypesView(AssignRemoveView):
|
class SetupWorkflowDocumentTypesView(AssignRemoveView):
|
||||||
decode_content_type = True
|
decode_content_type = True
|
||||||
@@ -229,7 +237,7 @@ class SetupWorkflowDocumentTypesView(AssignRemoveView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
return get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def left_list(self):
|
def left_list(self):
|
||||||
return AssignRemoveView.generate_choices(
|
return AssignRemoveView.generate_choices(
|
||||||
@@ -273,7 +281,7 @@ class SetupWorkflowStateListView(SingleObjectListView):
|
|||||||
return self.get_workflow().states.all()
|
return self.get_workflow().states.all()
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self):
|
||||||
return get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
return get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
|
||||||
class SetupWorkflowStateCreateView(SingleObjectCreateView):
|
class SetupWorkflowStateCreateView(SingleObjectCreateView):
|
||||||
@@ -289,7 +297,7 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self):
|
||||||
return get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
return get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.get_workflow().states.all()
|
return self.get_workflow().states.all()
|
||||||
@@ -350,7 +358,7 @@ class SetupWorkflowTransitionListView(SingleObjectListView):
|
|||||||
view_permission = permission_workflow_view
|
view_permission = permission_workflow_view
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self):
|
||||||
return get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
return get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.get_workflow().transitions.all()
|
return self.get_workflow().transitions.all()
|
||||||
@@ -385,7 +393,7 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView):
|
|||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_workflow(self):
|
def get_workflow(self):
|
||||||
return get_object_or_404(Workflow, pk=self.kwargs['pk'])
|
return get_object_or_404(Workflow.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.get_workflow().transitions.all()
|
return self.get_workflow().transitions.all()
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from __future__ import unicode_literals
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
DeletedDocument, Document, DocumentPage, DocumentType,
|
Document, DocumentPage, DocumentType, DocumentTypeFilename,
|
||||||
DocumentTypeFilename, DocumentVersion, RecentDocument
|
DocumentVersion, RecentDocument, TrashedDocument
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -29,14 +29,6 @@ class DocumentVersionInline(admin.StackedInline):
|
|||||||
allow_add = True
|
allow_add = True
|
||||||
|
|
||||||
|
|
||||||
@admin.register(DeletedDocument)
|
|
||||||
class DeletedDocumentAdmin(admin.ModelAdmin):
|
|
||||||
date_hierarchy = 'deleted_date_time'
|
|
||||||
list_filter = ('document_type',)
|
|
||||||
list_display = ('uuid', 'label', 'document_type', 'deleted_date_time')
|
|
||||||
readonly_fields = ('uuid', 'document_type')
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Document)
|
@admin.register(Document)
|
||||||
class DocumentAdmin(admin.ModelAdmin):
|
class DocumentAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = 'date_added'
|
date_hierarchy = 'date_added'
|
||||||
@@ -62,3 +54,11 @@ class RecentDocumentAdmin(admin.ModelAdmin):
|
|||||||
list_display_links = ('document', 'datetime_accessed')
|
list_display_links = ('document', 'datetime_accessed')
|
||||||
list_filter = ('user',)
|
list_filter = ('user',)
|
||||||
readonly_fields = ('user', 'document', 'datetime_accessed')
|
readonly_fields = ('user', 'document', 'datetime_accessed')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(TrashedDocument)
|
||||||
|
class TrashedDocumentAdmin(admin.ModelAdmin):
|
||||||
|
date_hierarchy = 'deleted_date_time'
|
||||||
|
list_filter = ('document_type',)
|
||||||
|
list_display = ('uuid', 'label', 'document_type', 'deleted_date_time')
|
||||||
|
readonly_fields = ('uuid', 'document_type')
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from .permissions import (
|
|||||||
permission_document_type_edit, permission_document_type_view
|
permission_document_type_edit, permission_document_type_view
|
||||||
)
|
)
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
DeletedDocumentSerializer, DocumentPageImageSerializer,
|
TrashedDocumentSerializer, DocumentPageImageSerializer,
|
||||||
DocumentPageSerializer, DocumentSerializer,
|
DocumentPageSerializer, DocumentSerializer,
|
||||||
DocumentTypeSerializer, DocumentVersionSerializer,
|
DocumentTypeSerializer, DocumentVersionSerializer,
|
||||||
DocumentVersionRevertSerializer, NewDocumentSerializer,
|
DocumentVersionRevertSerializer, NewDocumentSerializer,
|
||||||
@@ -37,7 +37,7 @@ from .serializers import (
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class APIDeletedDocumentListView(generics.ListAPIView):
|
class APITrashedDocumentListView(generics.ListAPIView):
|
||||||
"""
|
"""
|
||||||
Returns a list of all the trashed documents.
|
Returns a list of all the trashed documents.
|
||||||
"""
|
"""
|
||||||
@@ -46,10 +46,10 @@ class APIDeletedDocumentListView(generics.ListAPIView):
|
|||||||
mayan_object_permissions = {'GET': (permission_document_view,)}
|
mayan_object_permissions = {'GET': (permission_document_view,)}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.trash.all()
|
queryset = Document.trash.all()
|
||||||
serializer_class = DeletedDocumentSerializer
|
serializer_class = TrashedDocumentSerializer
|
||||||
|
|
||||||
|
|
||||||
class APIDeletedDocumentView(generics.RetrieveDestroyAPIView):
|
class APITrashedDocumentView(generics.RetrieveDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
Returns the selected trashed document details.
|
Returns the selected trashed document details.
|
||||||
"""
|
"""
|
||||||
@@ -59,17 +59,17 @@ class APIDeletedDocumentView(generics.RetrieveDestroyAPIView):
|
|||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.trash.all()
|
queryset = Document.trash.all()
|
||||||
serializer_class = DeletedDocumentSerializer
|
serializer_class = TrashedDocumentSerializer
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Delete the trashed document.
|
Delete the trashed document.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return super(APIDeletedDocumentView, self).delete(*args, **kwargs)
|
return super(APITrashedDocumentView, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class APIDeletedDocumentRestoreView(generics.GenericAPIView):
|
class APITrashedDocumentRestoreView(generics.GenericAPIView):
|
||||||
"""
|
"""
|
||||||
Restore a trashed document.
|
Restore a trashed document.
|
||||||
"""
|
"""
|
||||||
@@ -80,7 +80,7 @@ class APIDeletedDocumentRestoreView(generics.GenericAPIView):
|
|||||||
|
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.trash.all()
|
queryset = Document.trash.all()
|
||||||
serializer_class = DeletedDocumentSerializer
|
serializer_class = TrashedDocumentSerializer
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
self.get_object().restore()
|
self.get_object().restore()
|
||||||
@@ -103,7 +103,7 @@ class APIDocumentDownloadView(generics.RetrieveAPIView):
|
|||||||
'GET': (permission_document_download,)
|
'GET': (permission_document_download,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.objects.all()
|
queryset = Document.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
return None
|
return None
|
||||||
@@ -127,7 +127,7 @@ class APIDocumentListView(generics.ListCreateAPIView):
|
|||||||
mayan_object_permissions = {'GET': (permission_document_view,)}
|
mayan_object_permissions = {'GET': (permission_document_view,)}
|
||||||
mayan_view_permissions = {'POST': (permission_document_create,)}
|
mayan_view_permissions = {'POST': (permission_document_create,)}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.objects.all()
|
queryset = Document.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.request.method == 'GET':
|
if self.request.method == 'GET':
|
||||||
@@ -162,7 +162,7 @@ class APIDocumentVersionDownloadView(generics.RetrieveAPIView):
|
|||||||
'GET': (permission_document_download,)
|
'GET': (permission_document_download,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentVersion.objects.all()
|
queryset = DocumentVersion.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
return None
|
return None
|
||||||
@@ -189,7 +189,7 @@ class APIDocumentView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
'DELETE': (permission_document_trash,)
|
'DELETE': (permission_document_trash,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.objects.all()
|
queryset = Document.on_organization.all()
|
||||||
serializer_class = DocumentSerializer
|
serializer_class = DocumentSerializer
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
@@ -233,7 +233,7 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
|
|||||||
}
|
}
|
||||||
mayan_permission_attribute_check = 'document'
|
mayan_permission_attribute_check = 'document'
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentPage.objects.all()
|
queryset = DocumentPage.on_organization.all()
|
||||||
serializer_class = DocumentPageImageSerializer
|
serializer_class = DocumentPageImageSerializer
|
||||||
|
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView):
|
|||||||
}
|
}
|
||||||
mayan_permission_attribute_check = 'document'
|
mayan_permission_attribute_check = 'document'
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentPage.objects.all()
|
queryset = DocumentPage.on_organization.all()
|
||||||
serializer_class = DocumentPageSerializer
|
serializer_class = DocumentPageSerializer
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
@@ -279,7 +279,7 @@ class APIDocumentTypeListView(generics.ListCreateAPIView):
|
|||||||
mayan_object_permissions = {'GET': (permission_document_type_view,)}
|
mayan_object_permissions = {'GET': (permission_document_type_view,)}
|
||||||
mayan_view_permissions = {'POST': (permission_document_type_create,)}
|
mayan_view_permissions = {'POST': (permission_document_type_create,)}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentType.objects.all()
|
queryset = DocumentType.on_organization.all()
|
||||||
serializer_class = DocumentTypeSerializer
|
serializer_class = DocumentTypeSerializer
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
@@ -309,7 +309,7 @@ class APIDocumentTypeView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
'DELETE': (permission_document_type_delete,)
|
'DELETE': (permission_document_type_delete,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentType.objects.all()
|
queryset = DocumentType.on_organization.all()
|
||||||
serializer_class = DocumentTypeSerializer
|
serializer_class = DocumentTypeSerializer
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
@@ -435,7 +435,7 @@ class APIDocumentVersionView(generics.RetrieveUpdateAPIView):
|
|||||||
}
|
}
|
||||||
mayan_permission_attribute_check = 'document'
|
mayan_permission_attribute_check = 'document'
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentVersion.objects.all()
|
queryset = DocumentVersion.on_organization.all()
|
||||||
serializer_class = DocumentVersionSerializer
|
serializer_class = DocumentVersionSerializer
|
||||||
|
|
||||||
def patch(self, *args, **kwargs):
|
def patch(self, *args, **kwargs):
|
||||||
@@ -463,7 +463,7 @@ class APIDocumentVersionRevertView(generics.GenericAPIView):
|
|||||||
}
|
}
|
||||||
mayan_permission_attribute_check = 'document'
|
mayan_permission_attribute_check = 'document'
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentVersion.objects.all()
|
queryset = DocumentVersion.on_organization.all()
|
||||||
serializer_class = DocumentVersionRevertSerializer
|
serializer_class = DocumentVersionRevertSerializer
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from .links import (
|
|||||||
link_clear_image_cache, link_document_clear_transformations,
|
link_clear_image_cache, link_document_clear_transformations,
|
||||||
link_document_delete, link_document_document_type_edit,
|
link_document_delete, link_document_document_type_edit,
|
||||||
link_document_multiple_document_type_edit, link_document_download,
|
link_document_multiple_document_type_edit, link_document_download,
|
||||||
link_document_edit, link_document_list, link_document_list_deleted,
|
link_document_edit, link_document_list, link_document_list_trashed,
|
||||||
link_document_list_recent, link_document_multiple_delete,
|
link_document_list_recent, link_document_multiple_delete,
|
||||||
link_document_multiple_trash, link_document_multiple_clear_transformations,
|
link_document_multiple_trash, link_document_multiple_clear_transformations,
|
||||||
link_document_multiple_download, link_document_multiple_restore,
|
link_document_multiple_download, link_document_multiple_restore,
|
||||||
@@ -87,19 +87,19 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
|
|
||||||
APIEndPoint(app=self, version_string='1')
|
APIEndPoint(app=self, version_string='1')
|
||||||
|
|
||||||
DeletedDocument = self.get_model('DeletedDocument')
|
|
||||||
Document = self.get_model('Document')
|
Document = self.get_model('Document')
|
||||||
DocumentPage = self.get_model('DocumentPage')
|
DocumentPage = self.get_model('DocumentPage')
|
||||||
DocumentType = self.get_model('DocumentType')
|
DocumentType = self.get_model('DocumentType')
|
||||||
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
|
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
|
||||||
DocumentVersion = self.get_model('DocumentVersion')
|
DocumentVersion = self.get_model('DocumentVersion')
|
||||||
|
TrashedDocument = self.get_model('TrashedDocument')
|
||||||
|
|
||||||
MissingItem(
|
MissingItem(
|
||||||
label=_('Create a document type'),
|
label=_('Create a document type'),
|
||||||
description=_(
|
description=_(
|
||||||
'Every uploaded document must be assigned a document type, '
|
'Every uploaded document must be assigned a document type, '
|
||||||
'it is the basic way Mayan EDMS categorizes documents.'
|
'it is the basic way Mayan EDMS categorizes documents.'
|
||||||
), condition=lambda: not DocumentType.objects.exists(),
|
), condition=lambda: not DocumentType.on_organization.exists(),
|
||||||
view='documents:document_type_list'
|
view='documents:document_type_list'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -172,20 +172,20 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DeletedDocument, label=_('Thumbnail'),
|
source=TrashedDocument, label=_('Thumbnail'),
|
||||||
func=lambda context: document_thumbnail(
|
func=lambda context: document_thumbnail(
|
||||||
context['object'],
|
context['object'],
|
||||||
gallery_name='documents:delete_document_list',
|
gallery_name='documents:trashed_document_list',
|
||||||
size=setting_thumbnail_size.value,
|
size=setting_thumbnail_size.value,
|
||||||
title=getattr(context['object'], 'label', None),
|
title=getattr(context['object'], 'label', None),
|
||||||
disable_title_link=True
|
disable_title_link=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DeletedDocument, label=_('Type'), attribute='document_type'
|
source=TrashedDocument, label=_('Type'), attribute='document_type'
|
||||||
)
|
)
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
source=DeletedDocument, label=_('Date time trashed'),
|
source=TrashedDocument, label=_('Date time trashed'),
|
||||||
attribute='deleted_date_time'
|
attribute='deleted_date_time'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -268,7 +268,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
menu_front_page.bind_links(
|
menu_front_page.bind_links(
|
||||||
links=(
|
links=(
|
||||||
link_document_list_recent, link_document_list,
|
link_document_list_recent, link_document_list,
|
||||||
link_document_list_deleted
|
link_document_list_trashed
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
menu_setup.bind_links(links=(link_document_type_setup,))
|
menu_setup.bind_links(links=(link_document_type_setup,))
|
||||||
@@ -304,7 +304,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
menu_sidebar.bind_links(
|
menu_sidebar.bind_links(
|
||||||
links=(link_trash_can_empty,),
|
links=(link_trash_can_empty,),
|
||||||
sources=(
|
sources=(
|
||||||
'documents:document_list_deleted', 'documents:trash_can_empty'
|
'documents:document_list_trashed', 'documents:trash_can_empty'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
)
|
)
|
||||||
menu_object.bind_links(
|
menu_object.bind_links(
|
||||||
links=(link_document_restore, link_document_delete),
|
links=(link_document_restore, link_document_delete),
|
||||||
sources=(DeletedDocument,)
|
sources=(TrashedDocument,)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Document facet links
|
# Document facet links
|
||||||
@@ -354,7 +354,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
menu_multi_item.bind_links(
|
menu_multi_item.bind_links(
|
||||||
links=(
|
links=(
|
||||||
link_document_multiple_restore, link_document_multiple_delete
|
link_document_multiple_restore, link_document_multiple_delete
|
||||||
), sources=(DeletedDocument,)
|
), sources=(TrashedDocument,)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Document pages
|
# Document pages
|
||||||
@@ -430,5 +430,5 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
dispatch_uid='create_default_document_type'
|
dispatch_uid='create_default_document_type'
|
||||||
)
|
)
|
||||||
|
|
||||||
registry.register(DeletedDocument)
|
registry.register(TrashedDocument)
|
||||||
registry.register(Document)
|
registry.register(Document)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class DocumentTypeSelectForm(forms.Form):
|
|||||||
logger.debug('user: %s', user)
|
logger.debug('user: %s', user)
|
||||||
super(DocumentTypeSelectForm, self).__init__(*args, **kwargs)
|
super(DocumentTypeSelectForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
queryset = DocumentType.objects.all()
|
queryset = DocumentType.on_organization.all()
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(user, (permission_document_create,))
|
Permission.check_permissions(user, (permission_document_create,))
|
||||||
except PermissionDenied:
|
except PermissionDenied:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ def create_default_document_type(sender, **kwargs):
|
|||||||
app_label='documents', model_name='DocumentType'
|
app_label='documents', model_name='DocumentType'
|
||||||
)
|
)
|
||||||
|
|
||||||
if not DocumentType.objects.count():
|
if not DocumentType.on_organization.count():
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.objects.create(
|
||||||
label=DEFAULT_DOCUMENT_TYPE_LABEL
|
label=DEFAULT_DOCUMENT_TYPE_LABEL
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -140,9 +140,9 @@ link_document_list_recent = Link(
|
|||||||
icon='fa fa-clock-o', text=_('Recent documents'),
|
icon='fa fa-clock-o', text=_('Recent documents'),
|
||||||
view='documents:document_list_recent'
|
view='documents:document_list_recent'
|
||||||
)
|
)
|
||||||
link_document_list_deleted = Link(
|
link_document_list_trashed = Link(
|
||||||
icon='fa fa-trash', text=_('Trash'),
|
icon='fa fa-trash', text=_('Trash'),
|
||||||
view='documents:document_list_deleted'
|
view='documents:document_list_trashed'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
@@ -22,9 +23,9 @@ class DocumentManager(models.Manager):
|
|||||||
return self.get(uuid=uuid)
|
return self.get(uuid=uuid)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return TrashCanQuerySet(
|
return TrashedDocumentQuerySet(
|
||||||
self.model, using=self._db
|
self.model, using=self._db
|
||||||
).filter(in_trash=False)
|
)
|
||||||
|
|
||||||
def invalidate_cache(self):
|
def invalidate_cache(self):
|
||||||
for document in self.model.objects.all():
|
for document in self.model.objects.all():
|
||||||
@@ -49,7 +50,7 @@ class DocumentTypeManager(models.Manager):
|
|||||||
'Document type: %s, has a deletion period delta of: %s',
|
'Document type: %s, has a deletion period delta of: %s',
|
||||||
document_type, delta
|
document_type, delta
|
||||||
)
|
)
|
||||||
for document in document_type.deleted_documents.filter(deleted_date_time__lt=now() - delta):
|
for document in document_type.trashed_documents.filter(deleted_date_time__lt=now() - delta):
|
||||||
logger.info(
|
logger.info(
|
||||||
'Document "%s" with id: %d, trashed on: %s, exceded '
|
'Document "%s" with id: %d, trashed on: %s, exceded '
|
||||||
'delete period', document, document.pk,
|
'delete period', document, document.pk,
|
||||||
@@ -109,8 +110,48 @@ class NewVersionBlockManager(models.Manager):
|
|||||||
return self.filter(document=document).exists()
|
return self.filter(document=document).exists()
|
||||||
|
|
||||||
|
|
||||||
class PassthroughManager(models.Manager):
|
class OrganizationDocumentManager(models.Manager):
|
||||||
pass
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return TrashedDocumentQuerySet(
|
||||||
|
self.model, using=self._db
|
||||||
|
).filter(in_trash=False).filter(
|
||||||
|
document_type__in=DocumentType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentVersionManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentVersionManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document__document_type__in=DocumentType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentTypeFilenameManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentTypeFilenameManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_type__in=DocumentType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentPage(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentPage, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_version__document__document_type__in=DocumentType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RecentDocumentManager(models.Manager):
|
class RecentDocumentManager(models.Manager):
|
||||||
@@ -129,24 +170,37 @@ class RecentDocumentManager(models.Manager):
|
|||||||
return new_recent
|
return new_recent
|
||||||
|
|
||||||
def get_for_user(self, user):
|
def get_for_user(self, user):
|
||||||
document_model = apps.get_model('documents', 'document')
|
Document = apps.get_model('documents', 'Document')
|
||||||
|
|
||||||
if user.is_authenticated():
|
if user.is_authenticated():
|
||||||
return document_model.objects.filter(
|
return Document.objects.filter(
|
||||||
recentdocument__user=user
|
recentdocument__user=user,
|
||||||
).order_by('-recentdocument__datetime_accessed')
|
).order_by('-recentdocument__datetime_accessed')
|
||||||
else:
|
else:
|
||||||
return document_model.objects.none()
|
return Document.objects.none()
|
||||||
|
|
||||||
|
|
||||||
class TrashCanManager(models.Manager):
|
class TrashedDocumentManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
return super(
|
return super(
|
||||||
TrashCanManager, self
|
TrashedDocumentManager, self
|
||||||
).get_queryset().filter(in_trash=True)
|
).get_queryset().filter(in_trash=True)
|
||||||
|
|
||||||
|
|
||||||
class TrashCanQuerySet(models.QuerySet):
|
class OrganizationTrashedDocumentManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationTrashedDocumentManager, self
|
||||||
|
).get_queryset().filter(in_trash=True).filter(
|
||||||
|
document_type__in=DocumentType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TrashedDocumentQuerySet(models.QuerySet):
|
||||||
def delete(self, to_trash=True):
|
def delete(self, to_trash=True):
|
||||||
for instance in self:
|
for instance in self:
|
||||||
instance.delete(to_trash=to_trash)
|
instance.delete(to_trash=to_trash)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- 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'),
|
||||||
|
('documents', '0034_auto_20160509_2321'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='documenttype',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -23,6 +23,9 @@ from converter.exceptions import InvalidOfficeFormat, PageCountError
|
|||||||
from converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION
|
from converter.literals import DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION
|
||||||
from converter.models import Transformation
|
from converter.models import Transformation
|
||||||
from mimetype.api import get_mimetype
|
from mimetype.api import get_mimetype
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
from permissions import Permission
|
from permissions import Permission
|
||||||
|
|
||||||
from .events import (
|
from .events import (
|
||||||
@@ -34,7 +37,10 @@ from .exceptions import NewDocumentVersionNotAllowed
|
|||||||
from .literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
|
from .literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
|
||||||
from .managers import (
|
from .managers import (
|
||||||
DocumentManager, DocumentTypeManager, NewVersionBlockManager,
|
DocumentManager, DocumentTypeManager, NewVersionBlockManager,
|
||||||
PassthroughManager, RecentDocumentManager, TrashCanManager
|
OrganizationDocumentManager, OrganizationDocumentVersionManager,
|
||||||
|
OrganizationTrashedDocumentManager,
|
||||||
|
OrganizationDocumentTypeFilenameManager, OrganizationDocumentPage,
|
||||||
|
RecentDocumentManager, TrashedDocumentManager
|
||||||
)
|
)
|
||||||
from .permissions import permission_document_view
|
from .permissions import permission_document_view
|
||||||
from .runtime import cache_storage_backend, storage_backend
|
from .runtime import cache_storage_backend, storage_backend
|
||||||
@@ -61,6 +67,9 @@ class DocumentType(models.Model):
|
|||||||
Define document types or classes to which a specific set of
|
Define document types or classes to which a specific set of
|
||||||
properties can be attached
|
properties can be attached
|
||||||
"""
|
"""
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=32, unique=True, verbose_name=_('Label')
|
max_length=32, unique=True, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
@@ -87,12 +96,13 @@ class DocumentType(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = DocumentTypeManager()
|
objects = DocumentTypeManager()
|
||||||
|
on_organization = CurrentOrganizationManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
for document in Document.passthrough.filter(document_type=self):
|
for document in Document.objects.filter(document_type=self):
|
||||||
document.delete(to_trash=False)
|
document.delete(to_trash=False)
|
||||||
|
|
||||||
return super(DocumentType, self).delete(*args, **kwargs)
|
return super(DocumentType, self).delete(*args, **kwargs)
|
||||||
@@ -106,8 +116,8 @@ class DocumentType(models.Model):
|
|||||||
verbose_name_plural = _('Documents types')
|
verbose_name_plural = _('Documents types')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def deleted_documents(self):
|
def trashed_documents(self):
|
||||||
return DeletedDocument.objects.filter(document_type=self)
|
return TrashedDocument.objects.filter(document_type=self)
|
||||||
|
|
||||||
def get_document_count(self, user):
|
def get_document_count(self, user):
|
||||||
queryset = self.documents
|
queryset = self.documents
|
||||||
@@ -185,9 +195,14 @@ class Document(models.Model):
|
|||||||
), verbose_name=_('Is stub?')
|
), verbose_name=_('Is stub?')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# objects manager needs to go first otherwise
|
||||||
|
# objects becomes equal to ActiveDocumentManager(), which it shouldn't.
|
||||||
|
# The first manager becomes the default manager.
|
||||||
|
# https://docs.djangoproject.com/en/1.9/topics/db/managers/#default-managers
|
||||||
|
|
||||||
objects = DocumentManager()
|
objects = DocumentManager()
|
||||||
passthrough = PassthroughManager()
|
on_organization = OrganizationDocumentManager()
|
||||||
trash = TrashCanManager()
|
trash = TrashedDocumentManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label or ugettext('Document stub, id: %d') % self.pk
|
return self.label or ugettext('Document stub, id: %d') % self.pk
|
||||||
@@ -324,8 +339,9 @@ class Document(models.Model):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class DeletedDocument(Document):
|
class TrashedDocument(Document):
|
||||||
objects = TrashCanManager()
|
objects = TrashedDocumentManager()
|
||||||
|
on_organization = OrganizationTrashedDocumentManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
@@ -372,6 +388,9 @@ class DocumentVersion(models.Model):
|
|||||||
blank=True, editable=False, null=True, verbose_name=_('Checksum')
|
blank=True, editable=False, null=True, verbose_name=_('Checksum')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationDocumentVersionManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{0} - {1}'.format(self.document, self.timestamp)
|
return '{0} - {1}'.format(self.document, self.timestamp)
|
||||||
|
|
||||||
@@ -627,6 +646,9 @@ class DocumentTypeFilename(models.Model):
|
|||||||
)
|
)
|
||||||
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
|
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationDocumentTypeFilenameManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('filename',)
|
ordering = ('filename',)
|
||||||
unique_together = ('document_type', 'filename')
|
unique_together = ('document_type', 'filename')
|
||||||
@@ -651,6 +673,9 @@ class DocumentPage(models.Model):
|
|||||||
verbose_name=_('Page number')
|
verbose_name=_('Page number')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationDocumentPage()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _(
|
return _(
|
||||||
'Page %(page_num)d out of %(total_pages)d of %(document)s'
|
'Page %(page_num)d out of %(total_pages)d of %(document)s'
|
||||||
|
|||||||
@@ -103,32 +103,6 @@ class NewDocumentVersionSerializer(serializers.Serializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeletedDocumentSerializer(serializers.HyperlinkedModelSerializer):
|
|
||||||
document_type_label = serializers.SerializerMethodField()
|
|
||||||
restore = serializers.HyperlinkedIdentityField(
|
|
||||||
view_name='rest_api:deleteddocument-restore'
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_document_type_label(self, instance):
|
|
||||||
return instance.document_type.label
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
extra_kwargs = {
|
|
||||||
'document_type': {'view_name': 'rest_api:documenttype-detail'},
|
|
||||||
'url': {'view_name': 'rest_api:deleteddocument-detail'}
|
|
||||||
}
|
|
||||||
fields = (
|
|
||||||
'date_added', 'deleted_date_time', 'description', 'document_type',
|
|
||||||
'document_type_label', 'id', 'label', 'language', 'restore',
|
|
||||||
'url', 'uuid',
|
|
||||||
)
|
|
||||||
model = Document
|
|
||||||
read_only_fields = (
|
|
||||||
'deleted_date_time', 'description', 'document_type', 'label',
|
|
||||||
'language'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
|
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
document_type_label = serializers.SerializerMethodField()
|
document_type_label = serializers.SerializerMethodField()
|
||||||
latest_version = DocumentVersionSerializer(many=False, read_only=True)
|
latest_version = DocumentVersionSerializer(many=False, read_only=True)
|
||||||
@@ -156,7 +130,7 @@ class NewDocumentSerializer(serializers.ModelSerializer):
|
|||||||
file = serializers.FileField(write_only=True)
|
file = serializers.FileField(write_only=True)
|
||||||
|
|
||||||
def save(self, _user):
|
def save(self, _user):
|
||||||
document = Document.objects.create(
|
document = Document.on_organization.create(
|
||||||
description=self.validated_data.get('description', ''),
|
description=self.validated_data.get('description', ''),
|
||||||
document_type=self.validated_data['document_type'],
|
document_type=self.validated_data['document_type'],
|
||||||
label=self.validated_data.get(
|
label=self.validated_data.get(
|
||||||
@@ -191,3 +165,29 @@ class RecentDocumentSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
fields = ('document', 'datetime_accessed')
|
fields = ('document', 'datetime_accessed')
|
||||||
model = RecentDocument
|
model = RecentDocument
|
||||||
|
|
||||||
|
|
||||||
|
class TrashedDocumentSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
document_type_label = serializers.SerializerMethodField()
|
||||||
|
restore = serializers.HyperlinkedIdentityField(
|
||||||
|
view_name='rest_api:deleteddocument-restore'
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_document_type_label(self, instance):
|
||||||
|
return instance.document_type.label
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
extra_kwargs = {
|
||||||
|
'document_type': {'view_name': 'rest_api:documenttype-detail'},
|
||||||
|
'url': {'view_name': 'rest_api:deleteddocument-detail'}
|
||||||
|
}
|
||||||
|
fields = (
|
||||||
|
'date_added', 'deleted_date_time', 'description', 'document_type',
|
||||||
|
'document_type_label', 'id', 'label', 'language', 'restore',
|
||||||
|
'url', 'uuid',
|
||||||
|
)
|
||||||
|
model = Document
|
||||||
|
read_only_fields = (
|
||||||
|
'deleted_date_time', 'description', 'document_type', 'label',
|
||||||
|
'language'
|
||||||
|
)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def task_get_document_page_image(document_page_id, *args, **kwargs):
|
|||||||
app_label='documents', model_name='DocumentPage'
|
app_label='documents', model_name='DocumentPage'
|
||||||
)
|
)
|
||||||
|
|
||||||
document_page = DocumentPage.objects.get(pk=document_page_id)
|
document_page = DocumentPage.on_organization.get(pk=document_page_id)
|
||||||
return document_page.get_image(*args, **kwargs)
|
return document_page.get_image(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ def task_update_page_count(self, version_id):
|
|||||||
app_label='documents', model_name='DocumentVersion'
|
app_label='documents', model_name='DocumentVersion'
|
||||||
)
|
)
|
||||||
|
|
||||||
document_version = DocumentVersion.objects.get(pk=version_id)
|
document_version = DocumentVersion.on_organization.get(pk=version_id)
|
||||||
try:
|
try:
|
||||||
document_version.update_page_count()
|
document_version.update_page_count()
|
||||||
except OperationalError as exception:
|
except OperationalError as exception:
|
||||||
@@ -95,12 +95,12 @@ def task_upload_new_document(self, document_type_id, shared_uploaded_file_id, de
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
document_type = DocumentType.objects.get(pk=document_type_id)
|
document_type = DocumentType.on_organization.get(pk=document_type_id)
|
||||||
shared_file = SharedUploadedFile.objects.get(
|
shared_file = SharedUploadedFile.objects.get(
|
||||||
pk=shared_uploaded_file_id
|
pk=shared_uploaded_file_id
|
||||||
)
|
)
|
||||||
if user_id:
|
if user_id:
|
||||||
user = get_user_model().objects.get(pk=user_id)
|
user = get_user_model().on_organization.get(pk=user_id)
|
||||||
else:
|
else:
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
@@ -149,12 +149,12 @@ def task_upload_new_version(self, document_id, shared_uploaded_file_id, user_id,
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
document = Document.objects.get(pk=document_id)
|
document = Document.on_organization.get(pk=document_id)
|
||||||
shared_file = SharedUploadedFile.objects.get(
|
shared_file = SharedUploadedFile.objects.get(
|
||||||
pk=shared_uploaded_file_id
|
pk=shared_uploaded_file_id
|
||||||
)
|
)
|
||||||
if user_id:
|
if user_id:
|
||||||
user = get_user_model().objects.get(pk=user_id)
|
user = get_user_model().on_organization.get(pk=user_id)
|
||||||
else:
|
else:
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,13 @@ __all__ = (
|
|||||||
# Filenames
|
# Filenames
|
||||||
TEST_COMPRESSED_DOCUMENTS_FILENAME = 'compressed_documents.zip'
|
TEST_COMPRESSED_DOCUMENTS_FILENAME = 'compressed_documents.zip'
|
||||||
TEST_DEU_DOCUMENT_FILENAME = 'deu_website.png'
|
TEST_DEU_DOCUMENT_FILENAME = 'deu_website.png'
|
||||||
|
|
||||||
TEST_DOCUMENT_DESCRIPTION = 'test description'
|
TEST_DOCUMENT_DESCRIPTION = 'test description'
|
||||||
TEST_DOCUMENT_FILENAME = 'mayan_11_1.pdf'
|
TEST_DOCUMENT_FILENAME = 'mayan_11_1.pdf'
|
||||||
TEST_DOCUMENT_TYPE = 'test_document_type'
|
TEST_DOCUMENT_TYPE = 'test_document_type'
|
||||||
TEST_DOCUMENT_TYPE_2 = 'test document type 2'
|
TEST_DOCUMENT_TYPE_2 = 'test document type 2'
|
||||||
|
TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label'
|
||||||
|
TEST_DOCUMENT_TYPE_2_LABEL = 'test document type 2 label'
|
||||||
TEST_HYBRID_DOCUMENT = 'hybrid_text_and_image.pdf'
|
TEST_HYBRID_DOCUMENT = 'hybrid_text_and_image.pdf'
|
||||||
TEST_MULTI_PAGE_TIFF = 'multi_page.tiff'
|
TEST_MULTI_PAGE_TIFF = 'multi_page.tiff'
|
||||||
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png.zip'
|
TEST_NON_ASCII_COMPRESSED_DOCUMENT_FILENAME = 'I18N_title_áéíóúüñÑ.png.zip'
|
||||||
@@ -70,3 +73,5 @@ TEST_SMALL_DOCUMENT_PATH = os.path.join(
|
|||||||
settings.BASE_DIR, 'contrib', 'sample_documents',
|
settings.BASE_DIR, 'contrib', 'sample_documents',
|
||||||
TEST_SMALL_DOCUMENT_FILENAME
|
TEST_SMALL_DOCUMENT_FILENAME
|
||||||
)
|
)
|
||||||
|
TEST_TRANSFORMATION_NAME = 'rotate'
|
||||||
|
TEST_TRANSFORMATION_ARGUMENT = 'degrees: 180'
|
||||||
|
|||||||
@@ -4,48 +4,32 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from json import loads
|
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
from django.utils.six import BytesIO
|
from django.utils.six import BytesIO
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
|
from rest_api.tests import GenericAPITestCase
|
||||||
from user_management.tests.literals import (
|
from user_management.tests.literals import (
|
||||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||||
)
|
)
|
||||||
|
|
||||||
from .literals import (
|
from .literals import (
|
||||||
TEST_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE,
|
TEST_DOCUMENT_FILENAME, TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE,
|
||||||
TEST_SMALL_DOCUMENT_CHECKSUM, TEST_SMALL_DOCUMENT_PATH,
|
TEST_DOCUMENT_TYPE_EDITED_LABEL, TEST_SMALL_DOCUMENT_CHECKSUM,
|
||||||
|
TEST_SMALL_DOCUMENT_PATH,
|
||||||
)
|
)
|
||||||
from ..models import Document, DocumentType, HASH_FUNCTION
|
from ..models import Document, DocumentType, HASH_FUNCTION
|
||||||
|
|
||||||
|
|
||||||
class DocumentTypeAPITestCase(APITestCase):
|
class DocumentTypeAPITestCase(GenericAPITestCase):
|
||||||
"""
|
"""
|
||||||
Test the document type API endpoints
|
Test the document type API endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.admin_user.delete()
|
|
||||||
|
|
||||||
def test_document_type_create(self):
|
def test_document_type_create(self):
|
||||||
self.assertEqual(DocumentType.objects.all().count(), 0)
|
self.assertEqual(DocumentType.on_organization.all().count(), 0)
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse('rest_api:documenttype-list'), data={
|
reverse('rest_api:documenttype-list'), data={
|
||||||
@@ -53,66 +37,69 @@ class DocumentTypeAPITestCase(APITestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(DocumentType.objects.all().count(), 1)
|
self.assertEqual(DocumentType.on_organization.all().count(), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
DocumentType.objects.all().first().label, TEST_DOCUMENT_TYPE
|
DocumentType.on_organization.all().first().label,
|
||||||
|
TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_type_edit_via_put(self):
|
def test_document_type_edit_via_put(self):
|
||||||
document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)
|
document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
self.client.put(
|
|
||||||
reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
|
|
||||||
{'label': TEST_DOCUMENT_TYPE + 'edited'}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.get(pk=document_type.pk)
|
response = self.client.put(
|
||||||
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')
|
reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
|
||||||
|
{'label': TEST_DOCUMENT_TYPE_EDITED_LABEL}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
document_type = DocumentType.on_organization.get(pk=document_type.pk)
|
||||||
|
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE_EDITED_LABEL)
|
||||||
|
|
||||||
def test_document_type_edit_via_patch(self):
|
def test_document_type_edit_via_patch(self):
|
||||||
document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)
|
document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
self.client.patch(
|
|
||||||
reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
|
|
||||||
{'label': TEST_DOCUMENT_TYPE + 'edited'}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.get(pk=document_type.pk)
|
response = self.client.patch(
|
||||||
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')
|
reverse('rest_api:documenttype-detail', args=(document_type.pk,)),
|
||||||
|
{'label': TEST_DOCUMENT_TYPE_EDITED_LABEL}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
document_type = DocumentType.on_organization.get(pk=document_type.pk)
|
||||||
|
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE_EDITED_LABEL)
|
||||||
|
document_type.delete()
|
||||||
|
|
||||||
def test_document_type_delete(self):
|
def test_document_type_delete(self):
|
||||||
document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)
|
document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
self.client.delete(
|
self.client.delete(
|
||||||
reverse('rest_api:documenttype-detail', args=(document_type.pk,))
|
reverse('rest_api:documenttype-detail', args=(document_type.pk,))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(DocumentType.objects.all().count(), 0)
|
self.assertEqual(DocumentType.on_organization.all().count(), 0)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class DocumentAPITestCase(APITestCase):
|
class DocumentAPITestCase(GenericAPITestCase):
|
||||||
"""
|
"""
|
||||||
Test document API endpoints
|
Test document API endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(DocumentAPITestCase, self).setUp()
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
self.document_type = DocumentType.on_organization.create(
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.admin_user.delete()
|
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentAPITestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_upload(self):
|
def test_document_upload(self):
|
||||||
with open(TEST_DOCUMENT_PATH) as file_descriptor:
|
with open(TEST_DOCUMENT_PATH) as file_descriptor:
|
||||||
@@ -123,14 +110,14 @@ class DocumentAPITestCase(APITestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
document_data = loads(response.content)
|
document_data = response.data
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.status_code, status.HTTP_201_CREATED
|
response.status_code, status.HTTP_201_CREATED
|
||||||
)
|
)
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
document = Document.objects.first()
|
document = Document.on_organization.first()
|
||||||
|
|
||||||
self.assertEqual(document.pk, document_data['id'])
|
self.assertEqual(document.pk, document_data['id'])
|
||||||
|
|
||||||
@@ -158,41 +145,9 @@ class DocumentAPITestCase(APITestCase):
|
|||||||
reverse('rest_api:document-detail', args=(document.pk,))
|
reverse('rest_api:document-detail', args=(document.pk,))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.trash.count(), 1)
|
self.assertEqual(Document.trash.count(), 1)
|
||||||
|
|
||||||
def test_deleted_document_delete_from_trash(self):
|
|
||||||
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
|
||||||
document = self.document_type.new_document(
|
|
||||||
file_object=file_object,
|
|
||||||
)
|
|
||||||
|
|
||||||
document.delete()
|
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
|
||||||
self.assertEqual(Document.trash.count(), 1)
|
|
||||||
|
|
||||||
self.client.delete(
|
|
||||||
reverse('rest_api:trasheddocument-detail', args=(document.pk,))
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(Document.trash.count(), 0)
|
|
||||||
|
|
||||||
def test_deleted_document_restore(self):
|
|
||||||
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
|
||||||
document = self.document_type.new_document(
|
|
||||||
file_object=file_object,
|
|
||||||
)
|
|
||||||
|
|
||||||
document.delete()
|
|
||||||
|
|
||||||
self.client.post(
|
|
||||||
reverse('rest_api:trasheddocument-restore', args=(document.pk,))
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(Document.trash.count(), 0)
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
|
||||||
|
|
||||||
def test_document_new_version_upload(self):
|
def test_document_new_version_upload(self):
|
||||||
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
document = self.document_type.new_document(
|
document = self.document_type.new_document(
|
||||||
@@ -293,5 +248,34 @@ class DocumentAPITestCase(APITestCase):
|
|||||||
|
|
||||||
del(buf)
|
del(buf)
|
||||||
|
|
||||||
# TODO: def test_document_set_document_type(self):
|
def test_trashed_document_delete_from_trash(self):
|
||||||
# pass
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
|
document = self.document_type.new_document(
|
||||||
|
file_object=file_object,
|
||||||
|
)
|
||||||
|
|
||||||
|
document.delete()
|
||||||
|
|
||||||
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
self.assertEqual(Document.trash.count(), 1)
|
||||||
|
|
||||||
|
self.client.delete(
|
||||||
|
reverse('rest_api:trasheddocument-detail', args=(document.pk,))
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(Document.trash.count(), 0)
|
||||||
|
|
||||||
|
def test_trashed_document_restore(self):
|
||||||
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
|
document = self.document_type.new_document(
|
||||||
|
file_object=file_object,
|
||||||
|
)
|
||||||
|
|
||||||
|
document.delete()
|
||||||
|
|
||||||
|
self.client.post(
|
||||||
|
reverse('rest_api:trasheddocument-restore', args=(document.pk,))
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(Document.trash.count(), 0)
|
||||||
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document, role=self.role
|
content_object=self.document, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(
|
acl.permissions.add(
|
||||||
@@ -81,7 +81,7 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase):
|
|||||||
def test_document_version_download_link_with_permission(self):
|
def test_document_version_download_link_with_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
acl = AccessControlList.objects.create(
|
acl = AccessControlList.on_organization.create(
|
||||||
content_object=self.document, role=self.role
|
content_object=self.document, role=self.role
|
||||||
)
|
)
|
||||||
acl.permissions.add(permission_document_download.stored_permission)
|
acl.permissions.add(permission_document_download.stored_permission)
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import time
|
|||||||
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
|
|
||||||
|
from organizations.tests.base import OrganizationTestCase
|
||||||
|
|
||||||
from ..exceptions import NewDocumentVersionNotAllowed
|
from ..exceptions import NewDocumentVersionNotAllowed
|
||||||
from ..literals import STUB_EXPIRATION_INTERVAL
|
from ..literals import STUB_EXPIRATION_INTERVAL
|
||||||
from ..models import DeletedDocument, Document, DocumentType, NewVersionBlock
|
from ..models import Document, DocumentType, NewVersionBlock, TrashedDocument
|
||||||
|
|
||||||
from .literals import (
|
from .literals import (
|
||||||
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_PATH, TEST_MULTI_PAGE_TIFF_PATH,
|
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_PATH, TEST_MULTI_PAGE_TIFF_PATH,
|
||||||
@@ -16,9 +18,10 @@ from .literals import (
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class DocumentTestCase(TestCase):
|
class DocumentTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(DocumentTestCase, self).setUp()
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +32,7 @@ class DocumentTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_creation(self):
|
def test_document_creation(self):
|
||||||
self.assertEqual(self.document_type.label, TEST_DOCUMENT_TYPE)
|
self.assertEqual(self.document_type.label, TEST_DOCUMENT_TYPE)
|
||||||
@@ -57,30 +61,30 @@ class DocumentTestCase(TestCase):
|
|||||||
self.assertEqual(self.document.versions.count(), 3)
|
self.assertEqual(self.document.versions.count(), 3)
|
||||||
|
|
||||||
def test_restoring_documents(self):
|
def test_restoring_documents(self):
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
# Trash the document
|
# Trash the document
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
# Restore the document
|
# Restore the document
|
||||||
self.document.restore()
|
self.document.restore()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_trashing_documents(self):
|
def test_trashing_documents(self):
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
# Trash the document
|
# Trash the document
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
# Delete the document
|
# Delete the document
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_auto_trashing(self):
|
def test_auto_trashing(self):
|
||||||
"""
|
"""
|
||||||
@@ -97,13 +101,13 @@ class DocumentTestCase(TestCase):
|
|||||||
# field
|
# field
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
|
|
||||||
DocumentType.objects.check_trash_periods()
|
DocumentType.objects.check_trash_periods()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_auto_delete(self):
|
def test_auto_delete(self):
|
||||||
"""
|
"""
|
||||||
@@ -116,13 +120,13 @@ class DocumentTestCase(TestCase):
|
|||||||
self.document_type.delete_time_unit = 'seconds'
|
self.document_type.delete_time_unit = 'seconds'
|
||||||
self.document_type.save()
|
self.document_type.save()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
|
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
# Needed by MySQL as milliseconds value is not store in timestamp
|
# Needed by MySQL as milliseconds value is not store in timestamp
|
||||||
# field
|
# field
|
||||||
@@ -130,14 +134,16 @@ class DocumentTestCase(TestCase):
|
|||||||
|
|
||||||
DocumentType.objects.check_delete_periods()
|
DocumentType.objects.check_delete_periods()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class OfficeDocumentTestCase(TestCase):
|
class OfficeDocumentTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(OfficeDocumentTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -148,6 +154,7 @@ class OfficeDocumentTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(OfficeDocumentTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_creation(self):
|
def test_document_creation(self):
|
||||||
self.assertEqual(self.document.file_mimetype, 'application/msword')
|
self.assertEqual(self.document.file_mimetype, 'application/msword')
|
||||||
@@ -162,9 +169,11 @@ class OfficeDocumentTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class MultiPageTiffTestCase(TestCase):
|
class MultiPageTiffTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(MultiPageTiffTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -175,6 +184,7 @@ class MultiPageTiffTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(MultiPageTiffTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_creation(self):
|
def test_document_creation(self):
|
||||||
self.assertEqual(self.document.file_mimetype, 'image/tiff')
|
self.assertEqual(self.document.file_mimetype, 'image/tiff')
|
||||||
@@ -187,9 +197,11 @@ class MultiPageTiffTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class DocumentVersionTestCase(TestCase):
|
class DocumentVersionTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(DocumentVersionTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -200,6 +212,7 @@ class DocumentVersionTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentVersionTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_add_new_version(self):
|
def test_add_new_version(self):
|
||||||
self.assertEqual(self.document.versions.count(), 1)
|
self.assertEqual(self.document.versions.count(), 1)
|
||||||
@@ -236,23 +249,25 @@ class DocumentVersionTestCase(TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class DocumentManagerTestCase(TestCase):
|
class DocumentManagerTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(DocumentManagerTestCase, self).setUp()
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentManagerTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_stubs_deletion(self):
|
def test_document_stubs_deletion(self):
|
||||||
document_stub = Document.objects.create(
|
document_stub = Document.on_organization.create(
|
||||||
document_type=self.document_type
|
document_type=self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
Document.objects.delete_stubs()
|
Document.objects.delete_stubs()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
document_stub.date_added = document_stub.date_added - timedelta(
|
document_stub.date_added = document_stub.date_added - timedelta(
|
||||||
seconds=STUB_EXPIRATION_INTERVAL + 1
|
seconds=STUB_EXPIRATION_INTERVAL + 1
|
||||||
@@ -261,13 +276,14 @@ class DocumentManagerTestCase(TestCase):
|
|||||||
|
|
||||||
Document.objects.delete_stubs()
|
Document.objects.delete_stubs()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class NewVersionBlockTestCase(TestCase):
|
class NewVersionBlockTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(NewVersionBlockTestCase, self).setUp()
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -279,6 +295,7 @@ class NewVersionBlockTestCase(TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(NewVersionBlockTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_blocking(self):
|
def test_blocking(self):
|
||||||
NewVersionBlock.objects.block(document=self.document)
|
NewVersionBlock.objects.block(document=self.document)
|
||||||
|
|||||||
62
mayan/apps/documents/tests/test_organization_views.py
Normal file
62
mayan/apps/documents/tests/test_organization_views.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import Document,DocumentType
|
||||||
|
|
||||||
|
from .literals import (
|
||||||
|
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_2_LABEL,
|
||||||
|
TEST_DOCUMENT_TYPE_QUICK_LABEL, TEST_SMALL_DOCUMENT_CHECKSUM,
|
||||||
|
TEST_SMALL_DOCUMENT_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentOrganizationViewTestCase(OrganizationViewTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(DocumentOrganizationViewTestCase, self).setUp()
|
||||||
|
|
||||||
|
# Create a document for organization A
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
|
self.document = self.document_type.new_document(
|
||||||
|
file_object=file_object
|
||||||
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(DocumentOrganizationViewTestCase, self).tearDown()
|
||||||
|
if self.document_type.pk:
|
||||||
|
self.document_type.delete()
|
||||||
|
|
||||||
|
def test_document_to_trash_view(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'documents:document_trash', args=(self.document.pk,)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_document_view_view(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
# Make sure admin for organization B cannot find the document for
|
||||||
|
# organization A
|
||||||
|
response = self.get(
|
||||||
|
'documents:document_properties', args=(self.document.pk,),
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_document_document_type_change_view(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
document_type = DocumentType.on_organization.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE_2_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.post(
|
||||||
|
'documents:document_document_type_edit',
|
||||||
|
args=(self.document.pk,),
|
||||||
|
data={'document_type': document_type.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
@@ -15,7 +15,7 @@ from user_management.tests.literals import (
|
|||||||
|
|
||||||
from ..literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
|
from ..literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
|
||||||
from ..models import (
|
from ..models import (
|
||||||
DeletedDocument, Document, DocumentType, HASH_FUNCTION
|
Document, DocumentType, TrashedDocument, HASH_FUNCTION
|
||||||
)
|
)
|
||||||
from ..permissions import (
|
from ..permissions import (
|
||||||
permission_document_create, permission_document_delete,
|
permission_document_create, permission_document_delete,
|
||||||
@@ -28,22 +28,18 @@ from ..permissions import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .literals import (
|
from .literals import (
|
||||||
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_QUICK_LABEL,
|
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_EDITED_LABEL,
|
||||||
TEST_SMALL_DOCUMENT_CHECKSUM, TEST_SMALL_DOCUMENT_PATH
|
TEST_DOCUMENT_TYPE_2_LABEL, TEST_DOCUMENT_TYPE_QUICK_LABEL,
|
||||||
|
TEST_SMALL_DOCUMENT_CHECKSUM, TEST_SMALL_DOCUMENT_PATH,
|
||||||
|
TEST_TRANSFORMATION_ARGUMENT, TEST_TRANSFORMATION_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
TEST_DOCUMENT_TYPE_EDITED_LABEL = 'test document type edited label'
|
|
||||||
TEST_DOCUMENT_TYPE_2_LABEL = 'test document type 2 label'
|
|
||||||
TEST_TRANSFORMATION_NAME = 'rotate'
|
|
||||||
TEST_TRANSFORMATION_ARGUMENT = 'degrees: 180'
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class GenericDocumentViewTestCase(GenericViewTestCase):
|
class GenericDocumentViewTestCase(GenericViewTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(GenericDocumentViewTestCase, self).setUp()
|
super(GenericDocumentViewTestCase, self).setUp()
|
||||||
self.document_type = DocumentType.objects.create(
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,7 +111,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.document.document_type, self.document_type
|
self.document.document_type, self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2_LABEL
|
label=TEST_DOCUMENT_TYPE_2_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -128,7 +124,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.get(pk=self.document.pk).document_type,
|
Document.on_organization.get(pk=self.document.pk).document_type,
|
||||||
self.document_type
|
self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -141,7 +137,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.document.document_type, self.document_type
|
self.document.document_type, self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2_LABEL
|
label=TEST_DOCUMENT_TYPE_2_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,7 +156,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertContains(response, text='success', status_code=200)
|
self.assertContains(response, text='success', status_code=200)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.get(pk=self.document.pk).document_type,
|
Document.on_organization.get(pk=self.document.pk).document_type,
|
||||||
document_type
|
document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -170,10 +166,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.first().document_type, self.document_type
|
Document.on_organization.first().document_type, self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2_LABEL
|
label=TEST_DOCUMENT_TYPE_2_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -188,7 +184,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.first().document_type, self.document_type
|
Document.on_organization.first().document_type, self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_multiple_document_type_change_view_with_permission(self):
|
def test_document_multiple_document_type_change_view_with_permission(self):
|
||||||
@@ -197,10 +193,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.first().document_type, self.document_type
|
Document.on_organization.first().document_type, self.document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2_LABEL
|
label=TEST_DOCUMENT_TYPE_2_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -222,7 +218,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Document.objects.first().document_type, document_type
|
Document.on_organization.first().document_type, document_type
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_download_user_view(self):
|
def test_document_download_user_view(self):
|
||||||
@@ -230,7 +226,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_download', args=(self.document.pk,)
|
'documents:document_download', args=(self.document.pk,)
|
||||||
@@ -262,7 +258,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_multiple_download',
|
'documents:document_multiple_download',
|
||||||
@@ -296,7 +292,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_version_download', args=(
|
'documents:document_version_download', args=(
|
||||||
@@ -486,20 +482,20 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
def test_trash_can_empty_view_no_permissions(self):
|
def test_trash_can_empty_view_no_permissions(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.post('documents:trash_can_empty')
|
response = self.post('documents:trash_can_empty')
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_trash_can_empty_view_with_permission(self):
|
def test_trash_can_empty_view_with_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
self.role.permissions.add(
|
self.role.permissions.add(
|
||||||
permission_empty_trash.stored_permission
|
permission_empty_trash.stored_permission
|
||||||
@@ -509,8 +505,8 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertContains(
|
self.assertContains(
|
||||||
response, text='emptied successfully', status_code=200
|
response, text='emptied successfully', status_code=200
|
||||||
)
|
)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_document_version_revert_no_permission(self):
|
def test_document_version_revert_no_permission(self):
|
||||||
first_version = self.document.latest_version
|
first_version = self.document.latest_version
|
||||||
@@ -560,7 +556,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_type_create',
|
'documents:document_type_create',
|
||||||
@@ -573,7 +569,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
self.assertEqual(DocumentType.objects.count(), 0)
|
self.assertEqual(DocumentType.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_document_type_create_view_with_permission(self):
|
def test_document_type_create_view_with_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -582,7 +578,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
self.role.permissions.add(
|
self.role.permissions.add(
|
||||||
permission_document_type_create.stored_permission
|
permission_document_type_create.stored_permission
|
||||||
@@ -602,9 +598,9 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.assertContains(response, text='successfully', status_code=200)
|
self.assertContains(response, text='successfully', status_code=200)
|
||||||
|
|
||||||
self.assertEqual(DocumentType.objects.count(), 1)
|
self.assertEqual(DocumentType.on_organization.count(), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
DocumentType.objects.first().label, TEST_DOCUMENT_TYPE
|
DocumentType.on_organization.first().label, TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_document_type_delete_view_no_permission(self):
|
def test_document_type_delete_view_no_permission(self):
|
||||||
@@ -618,7 +614,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(DocumentType.objects.count(), 1)
|
self.assertEqual(DocumentType.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_document_type_delete_view_with_permission(self):
|
def test_document_type_delete_view_with_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -638,7 +634,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, 'successfully', status_code=200)
|
self.assertContains(response, 'successfully', status_code=200)
|
||||||
self.assertEqual(DocumentType.objects.count(), 0)
|
self.assertEqual(DocumentType.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_document_type_edit_view_no_permission(self):
|
def test_document_type_edit_view_no_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -658,7 +654,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
DocumentType.objects.get(pk=self.document_type.pk).label,
|
DocumentType.on_organization.get(pk=self.document_type.pk).label,
|
||||||
TEST_DOCUMENT_TYPE
|
TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -687,7 +683,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertContains(response, 'successfully', status_code=200)
|
self.assertContains(response, 'successfully', status_code=200)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
DocumentType.objects.get(pk=self.document_type.pk).label,
|
DocumentType.on_organization.get(pk=self.document_type.pk).label,
|
||||||
TEST_DOCUMENT_TYPE_EDITED_LABEL
|
TEST_DOCUMENT_TYPE_EDITED_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -728,27 +724,27 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
|
|||||||
self.assertEqual(self.document_type.filenames.count(), 1)
|
self.assertEqual(self.document_type.filenames.count(), 1)
|
||||||
|
|
||||||
|
|
||||||
class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
class TrashedDocumentTestCase(GenericDocumentViewTestCase):
|
||||||
def test_document_restore_view_no_permission(self):
|
def test_document_restore_view_no_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_restore', args=(self.document.pk,)
|
'documents:document_restore', args=(self.document.pk,)
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_document_restore_view_with_permission(self):
|
def test_document_restore_view_with_permission(self):
|
||||||
self.login(
|
self.login(
|
||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.role.permissions.add(
|
self.role.permissions.add(
|
||||||
permission_document_restore.stored_permission
|
permission_document_restore.stored_permission
|
||||||
)
|
)
|
||||||
@@ -757,8 +753,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
follow=True
|
follow=True
|
||||||
)
|
)
|
||||||
self.assertContains(response, text='restored', status_code=200)
|
self.assertContains(response, text='restored', status_code=200)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_document_trash_no_permissions(self):
|
def test_document_trash_no_permissions(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -770,8 +766,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self.assertEqual(Document.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_document_trash_with_permissions(self):
|
def test_document_trash_with_permissions(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -788,8 +784,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, text='success', status_code=200)
|
self.assertContains(response, text='success', status_code=200)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_document_delete_no_permissions(self):
|
def test_document_delete_no_permissions(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -797,15 +793,15 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'documents:document_delete', args=(self.document.pk,),
|
'documents:document_delete', args=(self.document.pk,),
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_document_delete_with_permissions(self):
|
def test_document_delete_with_permissions(self):
|
||||||
self.login(
|
self.login(
|
||||||
@@ -813,8 +809,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 1)
|
self.assertEqual(TrashedDocument.on_organization.count(), 1)
|
||||||
|
|
||||||
self.role.permissions.add(
|
self.role.permissions.add(
|
||||||
permission_document_delete.stored_permission
|
permission_document_delete.stored_permission
|
||||||
@@ -826,8 +822,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, text='success', status_code=200)
|
self.assertContains(response, text='success', status_code=200)
|
||||||
self.assertEqual(DeletedDocument.objects.count(), 0)
|
self.assertEqual(TrashedDocument.on_organization.count(), 0)
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
self.assertEqual(Document.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_deleted_document_list_view_no_permissions(self):
|
def test_deleted_document_list_view_no_permissions(self):
|
||||||
self.document.delete()
|
self.document.delete()
|
||||||
@@ -836,7 +832,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.get('documents:document_list_deleted')
|
response = self.get('documents:document_list_trashed')
|
||||||
|
|
||||||
self.assertNotContains(response, self.document.label, status_code=200)
|
self.assertNotContains(response, self.document.label, status_code=200)
|
||||||
|
|
||||||
@@ -850,6 +846,6 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
|
|||||||
self.role.permissions.add(
|
self.role.permissions.add(
|
||||||
permission_document_view.stored_permission
|
permission_document_view.stored_permission
|
||||||
)
|
)
|
||||||
response = self.get('documents:document_list_deleted')
|
response = self.get('documents:document_list_trashed')
|
||||||
|
|
||||||
self.assertContains(response, self.document.label, status_code=200)
|
self.assertContains(response, self.document.label, status_code=200)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ from __future__ import unicode_literals
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
from .api_views import (
|
from .api_views import (
|
||||||
APIDeletedDocumentListView, APIDeletedDocumentRestoreView,
|
APITrashedDocumentListView, APITrashedDocumentRestoreView,
|
||||||
APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentView,
|
APITrashedDocumentView, APIDocumentDownloadView, APIDocumentView,
|
||||||
APIDocumentListView, APIDocumentVersionDownloadView,
|
APIDocumentListView, APIDocumentVersionDownloadView,
|
||||||
APIDocumentPageImageView, APIDocumentPageView,
|
APIDocumentPageImageView, APIDocumentPageView,
|
||||||
APIDocumentTypeDocumentListView, APIDocumentTypeListView,
|
APIDocumentTypeDocumentListView, APIDocumentTypeListView,
|
||||||
@@ -14,17 +14,16 @@ from .api_views import (
|
|||||||
)
|
)
|
||||||
from .settings import setting_print_size, setting_display_size
|
from .settings import setting_print_size, setting_display_size
|
||||||
from .views import (
|
from .views import (
|
||||||
ClearImageCacheView, DeletedDocumentDeleteView,
|
ClearImageCacheView, DocumentEditView, DocumentListView, DocumentPageView,
|
||||||
DeletedDocumentDeleteManyView, DeletedDocumentListView, DocumentEditView,
|
DocumentPageListView, DocumentPageViewResetView, DocumentPreviewView,
|
||||||
DocumentListView, DocumentPageView, DocumentPageListView,
|
DocumentRestoreView, DocumentRestoreManyView, DocumentTrashView,
|
||||||
DocumentPageViewResetView, DocumentPreviewView, DocumentRestoreView,
|
DocumentTrashManyView, DocumentTypeCreateView, DocumentTypeDeleteView,
|
||||||
DocumentRestoreManyView, DocumentTrashView, DocumentTrashManyView,
|
|
||||||
DocumentTypeCreateView, DocumentTypeDeleteView,
|
|
||||||
DocumentTypeDocumentListView, DocumentTypeFilenameCreateView,
|
DocumentTypeDocumentListView, DocumentTypeFilenameCreateView,
|
||||||
DocumentTypeFilenameDeleteView, DocumentTypeFilenameEditView,
|
DocumentTypeFilenameDeleteView, DocumentTypeFilenameEditView,
|
||||||
DocumentTypeFilenameListView, DocumentTypeListView, DocumentTypeEditView,
|
DocumentTypeFilenameListView, DocumentTypeListView, DocumentTypeEditView,
|
||||||
DocumentVersionListView, DocumentVersionRevertView, DocumentView,
|
DocumentVersionListView, DocumentVersionRevertView, DocumentView,
|
||||||
EmptyTrashCanView, RecentDocumentListView
|
EmptyTrashCanView, RecentDocumentListView, TrashedDocumentDeleteView,
|
||||||
|
TrashedDocumentDeleteManyView, TrashedDocumentListView
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
@@ -35,8 +34,8 @@ urlpatterns = patterns(
|
|||||||
name='document_list_recent'
|
name='document_list_recent'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^list/deleted/$', DeletedDocumentListView.as_view(),
|
r'^list/deleted/$', TrashedDocumentListView.as_view(),
|
||||||
name='document_list_deleted'
|
name='document_list_trashed'
|
||||||
),
|
),
|
||||||
|
|
||||||
url(
|
url(
|
||||||
@@ -56,11 +55,11 @@ urlpatterns = patterns(
|
|||||||
name='document_multiple_restore'
|
name='document_multiple_restore'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^(?P<pk>\d+)/delete/$', DeletedDocumentDeleteView.as_view(),
|
r'^(?P<pk>\d+)/delete/$', TrashedDocumentDeleteView.as_view(),
|
||||||
name='document_delete'
|
name='document_delete'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^multiple/delete/$', DeletedDocumentDeleteManyView.as_view(),
|
r'^multiple/delete/$', TrashedDocumentDeleteManyView.as_view(),
|
||||||
name='document_multiple_delete'
|
name='document_multiple_delete'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
@@ -242,18 +241,6 @@ urlpatterns = patterns(
|
|||||||
|
|
||||||
api_urls = patterns(
|
api_urls = patterns(
|
||||||
'',
|
'',
|
||||||
url(
|
|
||||||
r'^trashed_documents/$', APIDeletedDocumentListView.as_view(),
|
|
||||||
name='trasheddocument-list'
|
|
||||||
),
|
|
||||||
url(
|
|
||||||
r'^trashed_documents/(?P<pk>[0-9]+)/$',
|
|
||||||
APIDeletedDocumentView.as_view(), name='trasheddocument-detail'
|
|
||||||
),
|
|
||||||
url(
|
|
||||||
r'^trashed_documents/(?P<pk>[0-9]+)/restore/$',
|
|
||||||
APIDeletedDocumentRestoreView.as_view(), name='trasheddocument-restore'
|
|
||||||
),
|
|
||||||
url(r'^documents/$', APIDocumentListView.as_view(), name='document-list'),
|
url(r'^documents/$', APIDocumentListView.as_view(), name='document-list'),
|
||||||
url(
|
url(
|
||||||
r'^documents/recent/$', APIRecentDocumentListView.as_view(),
|
r'^documents/recent/$', APIRecentDocumentListView.as_view(),
|
||||||
@@ -304,4 +291,16 @@ api_urls = patterns(
|
|||||||
r'^document_types/$', APIDocumentTypeListView.as_view(),
|
r'^document_types/$', APIDocumentTypeListView.as_view(),
|
||||||
name='documenttype-list'
|
name='documenttype-list'
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^trashed_documents/$', APITrashedDocumentListView.as_view(),
|
||||||
|
name='trasheddocument-list'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^trashed_documents/(?P<pk>[0-9]+)/$',
|
||||||
|
APITrashedDocumentView.as_view(), name='trasheddocument-detail'
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r'^trashed_documents/(?P<pk>[0-9]+)/restore/$',
|
||||||
|
APITrashedDocumentRestoreView.as_view(), name='trasheddocument-restore'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,13 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from organizations.admin import OrganizationAdminMixin
|
||||||
|
|
||||||
from .models import Folder
|
from .models import Folder
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Folder)
|
@admin.register(Folder)
|
||||||
class FolderAdmin(admin.ModelAdmin):
|
class FolderAdmin(OrganizationAdminMixin, admin.ModelAdmin):
|
||||||
filter_horizontal = ('documents',)
|
filter_horizontal = ('documents',)
|
||||||
list_display = ('label', 'datetime_created')
|
list_display = ('label', 'datetime_created')
|
||||||
|
list_display_links = ('label',)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class APIFolderListView(generics.ListCreateAPIView):
|
|||||||
mayan_object_permissions = {'GET': (permission_folder_view,)}
|
mayan_object_permissions = {'GET': (permission_folder_view,)}
|
||||||
mayan_view_permissions = {'POST': (permission_folder_create,)}
|
mayan_view_permissions = {'POST': (permission_folder_create,)}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Folder.objects.all()
|
queryset = Folder.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.request.method == 'GET':
|
if self.request.method == 'GET':
|
||||||
@@ -85,7 +85,7 @@ class APIFolderView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
'DELETE': (permission_folder_delete,)
|
'DELETE': (permission_folder_delete,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Folder.objects.all()
|
queryset = Folder.on_organization.all()
|
||||||
serializer_class = FolderSerializer
|
serializer_class = FolderSerializer
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class FolderListForm(forms.Form):
|
|||||||
logger.debug('user: %s', user)
|
logger.debug('user: %s', user)
|
||||||
super(FolderListForm, self).__init__(*args, **kwargs)
|
super(FolderListForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
queryset = Folder.objects.all()
|
queryset = Folder.on_organization.all()
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(user, (permission_folder_view,))
|
Permission.check_permissions(user, (permission_folder_view,))
|
||||||
except PermissionDenied:
|
except PermissionDenied:
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class FolderManager(models.Manager):
|
class FolderManager(models.Manager):
|
||||||
def get_by_natural_key(self, label, *user_key):
|
def get_by_natural_key(self, label, *user_key):
|
||||||
user = get_user_model().objects.get_by_natural_key(*user_key)
|
return self.get(label=label)
|
||||||
return self.get(label=label, user=user)
|
|
||||||
|
|||||||
21
mayan/apps/folders/migrations/0007_folder_organization.py
Normal file
21
mayan/apps/folders/migrations/0007_folder_organization.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import organizations.shortcuts
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('organizations', '0001_initial'),
|
||||||
|
('folders', '0006_auto_20160308_0445'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='folder',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,6 +9,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from acls.models import AccessControlList
|
from acls.models import AccessControlList
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.permissions import permission_document_view
|
from documents.permissions import permission_document_view
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
from permissions import Permission
|
from permissions import Permission
|
||||||
|
|
||||||
from .managers import FolderManager
|
from .managers import FolderManager
|
||||||
@@ -16,6 +19,9 @@ from .managers import FolderManager
|
|||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Folder(models.Model):
|
class Folder(models.Model):
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
db_index=True, max_length=128, verbose_name=_('Label')
|
db_index=True, max_length=128, verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
@@ -27,6 +33,7 @@ class Folder(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = FolderManager()
|
objects = FolderManager()
|
||||||
|
on_organization = CurrentOrganizationManager()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
|
|||||||
@@ -60,7 +60,9 @@ class NewFolderDocumentSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
try:
|
try:
|
||||||
document = Document.objects.get(pk=validated_data['document'])
|
document = Document.on_organization.get(
|
||||||
|
pk=validated_data['document']
|
||||||
|
)
|
||||||
validated_data['folder'].documents.add(document)
|
validated_data['folder'].documents.add(document)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
raise ValidationError(exception)
|
raise ValidationError(exception)
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
|
from rest_api.tests import GenericAPITestCase
|
||||||
from user_management.tests.literals import (
|
from user_management.tests.literals import (
|
||||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||||
)
|
)
|
||||||
@@ -17,60 +16,50 @@ from ..models import Folder
|
|||||||
from .literals import TEST_FOLDER_EDITED_LABEL, TEST_FOLDER_LABEL
|
from .literals import TEST_FOLDER_EDITED_LABEL, TEST_FOLDER_LABEL
|
||||||
|
|
||||||
|
|
||||||
class FolderAPITestCase(APITestCase):
|
class FolderAPITestCase(GenericAPITestCase):
|
||||||
"""
|
"""
|
||||||
Test the folder API endpoints
|
Test the folder API endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_folder_create(self):
|
def test_folder_create(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse('rest_api:folder-list'), {'label': TEST_FOLDER_LABEL}
|
reverse('rest_api:folder-list'), {'label': TEST_FOLDER_LABEL}
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.first()
|
folder = Folder.on_organization.first()
|
||||||
|
|
||||||
self.assertEqual(response.data['id'], folder.pk)
|
self.assertEqual(response.data['id'], folder.pk)
|
||||||
self.assertEqual(response.data['label'], TEST_FOLDER_LABEL)
|
self.assertEqual(response.data['label'], TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
self.assertEqual(Folder.objects.count(), 1)
|
self.assertEqual(Folder.on_organization.count(), 1)
|
||||||
self.assertEqual(folder.label, TEST_FOLDER_LABEL)
|
self.assertEqual(folder.label, TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
def test_folder_delete(self):
|
def test_folder_delete(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
self.client.delete(
|
self.client.delete(
|
||||||
reverse('rest_api:folder-detail', args=(folder.pk,))
|
reverse('rest_api:folder-detail', args=(folder.pk,))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(Folder.objects.count(), 0)
|
self.assertEqual(Folder.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_folder_edit(self):
|
def test_folder_edit(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
self.client.put(
|
self.client.put(
|
||||||
reverse('rest_api:folder-detail', args=(folder.pk,)),
|
reverse('rest_api:folder-detail', args=(folder.pk,)),
|
||||||
{'label': TEST_FOLDER_EDITED_LABEL}
|
{'label': TEST_FOLDER_EDITED_LABEL}
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.first()
|
folder = Folder.on_organization.first()
|
||||||
|
|
||||||
self.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
|
self.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
def test_folder_add_document(self):
|
def test_folder_add_document(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,9 +77,9 @@ class FolderAPITestCase(APITestCase):
|
|||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
def test_folder_remove_document(self):
|
def test_folder_remove_document(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
document_type = DocumentType.objects.create(
|
document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from django.test import TestCase, override_settings
|
|||||||
|
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.tests import TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
from documents.tests import TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||||
|
from organizations.utils import create_default_organization
|
||||||
|
|
||||||
from ..models import Folder
|
from ..models import Folder
|
||||||
|
|
||||||
@@ -13,7 +14,9 @@ from .literals import TEST_FOLDER_LABEL
|
|||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class FolderTestCase(TestCase):
|
class FolderTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
create_default_organization()
|
||||||
|
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,20 +29,20 @@ class FolderTestCase(TestCase):
|
|||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
|
||||||
def test_folder_creation(self):
|
def test_folder_creation(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
self.assertEqual(Folder.objects.all().count(), 1)
|
self.assertEqual(Folder.on_organization.all().count(), 1)
|
||||||
self.assertEqual(list(Folder.objects.all()), [folder])
|
self.assertEqual(list(Folder.on_organization.all()), [folder])
|
||||||
|
|
||||||
def test_addition_of_documents(self):
|
def test_addition_of_documents(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
folder.documents.add(self.document)
|
folder.documents.add(self.document)
|
||||||
|
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
self.assertEqual(list(folder.documents.all()), [self.document])
|
self.assertEqual(list(folder.documents.all()), [self.document])
|
||||||
|
|
||||||
def test_addition_and_deletion_of_documents(self):
|
def test_addition_and_deletion_of_documents(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
folder.documents.add(self.document)
|
folder.documents.add(self.document)
|
||||||
|
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
|
|||||||
62
mayan/apps/folders/tests/test_organization_views.py
Normal file
62
mayan/apps/folders/tests/test_organization_views.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import Folder
|
||||||
|
|
||||||
|
from .literals import TEST_FOLDER_LABEL, TEST_FOLDER_EDITED_LABEL
|
||||||
|
|
||||||
|
|
||||||
|
class FolderOrganizationViewTestCase(OrganizationViewTestCase):
|
||||||
|
def test_folder_create_view(self):
|
||||||
|
# Create a folder for organization A
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
response = self.post(
|
||||||
|
'folders:folder_create', data={
|
||||||
|
'label': TEST_FOLDER_LABEL
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(Folder.on_organization.count(), 1)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
self.assertEqual(Folder.on_organization.count(), 0)
|
||||||
|
|
||||||
|
def test_folder_delete_view(self):
|
||||||
|
# Create a folder for organization A
|
||||||
|
folder = Folder.objects.create(
|
||||||
|
organization=self.organization_a, label=TEST_FOLDER_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post('folders:folder_delete', args=(folder.pk,))
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_folder_edit_view(self):
|
||||||
|
# Create a folder for organization A
|
||||||
|
folder = Folder.objects.create(
|
||||||
|
organization=self.organization_a, label=TEST_FOLDER_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
# Make sure admin for organization B cannot edit the folder
|
||||||
|
response = self.post(
|
||||||
|
'folders:folder_edit', args=(folder.pk,), data={
|
||||||
|
'label': TEST_FOLDER_EDITED_LABEL
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_folder_view_view(self):
|
||||||
|
# Create a folder for organization A
|
||||||
|
folder = Folder.objects.create(
|
||||||
|
organization=self.organization_a, label=TEST_FOLDER_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
# Make sure admin for organization B cannot find the folder for
|
||||||
|
# organization A
|
||||||
|
response = self.get(
|
||||||
|
'folders:folder_view', args=(folder.pk,),
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
@@ -26,7 +26,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 403)
|
self.assertEquals(response.status_code, 403)
|
||||||
self.assertEqual(Folder.objects.count(), 0)
|
self.assertEqual(Folder.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_folder_create_view_with_permission(self):
|
def test_folder_create_view_with_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
@@ -41,11 +41,13 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
self.assertContains(response, text='created', status_code=200)
|
self.assertContains(response, text='created', status_code=200)
|
||||||
self.assertEqual(Folder.objects.count(), 1)
|
self.assertEqual(Folder.on_organization.count(), 1)
|
||||||
self.assertEqual(Folder.objects.first().label, TEST_FOLDER_LABEL)
|
self.assertEqual(
|
||||||
|
Folder.on_organization.first().label, TEST_FOLDER_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
def test_folder_create_duplicate_view_with_permission(self):
|
def test_folder_create_duplicate_view_with_permission(self):
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
@@ -60,17 +62,17 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, text='exists', status_code=200)
|
self.assertContains(response, text='exists', status_code=200)
|
||||||
self.assertEqual(Folder.objects.count(), 1)
|
self.assertEqual(Folder.on_organization.count(), 1)
|
||||||
self.assertEqual(Folder.objects.first().pk, folder.pk)
|
self.assertEqual(Folder.on_organization.first().pk, folder.pk)
|
||||||
|
|
||||||
def test_folder_delete_view_no_permission(self):
|
def test_folder_delete_view_no_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post('folders:folder_delete', args=(folder.pk,))
|
response = self.post('folders:folder_delete', args=(folder.pk,))
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
self.assertEqual(Folder.objects.count(), 1)
|
self.assertEqual(Folder.on_organization.count(), 1)
|
||||||
|
|
||||||
def test_folder_delete_view_with_permission(self):
|
def test_folder_delete_view_with_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
@@ -79,19 +81,19 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_folder_delete.stored_permission
|
permission_folder_delete.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_delete', args=(folder.pk,), follow=True
|
'folders:folder_delete', args=(folder.pk,), follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertContains(response, text='deleted', status_code=200)
|
self.assertContains(response, text='deleted', status_code=200)
|
||||||
self.assertEqual(Folder.objects.count(), 0)
|
self.assertEqual(Folder.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_folder_edit_view_no_permission(self):
|
def test_folder_edit_view_no_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_edit', args=(folder.pk,), data={
|
'folders:folder_edit', args=(folder.pk,), data={
|
||||||
@@ -99,7 +101,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertEqual(folder.label, TEST_FOLDER_LABEL)
|
self.assertEqual(folder.label, TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
def test_folder_edit_view_with_permission(self):
|
def test_folder_edit_view_with_permission(self):
|
||||||
@@ -109,7 +111,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_folder_edit.stored_permission
|
permission_folder_edit.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_edit', args=(folder.pk,), data={
|
'folders:folder_edit', args=(folder.pk,), data={
|
||||||
@@ -117,7 +119,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertContains(response, text='update', status_code=200)
|
self.assertContains(response, text='update', status_code=200)
|
||||||
self.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
|
self.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
|
||||||
|
|
||||||
@@ -126,7 +128,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.role.permissions.add(permission_folder_view.stored_permission)
|
self.role.permissions.add(permission_folder_view.stored_permission)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_add_document', args=(self.document.pk,), data={
|
'folders:folder_add_document', args=(self.document.pk,), data={
|
||||||
@@ -150,7 +152,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_document_view.stored_permission
|
permission_document_view.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_add_document', args=(self.document.pk,), data={
|
'folders:folder_add_document', args=(self.document.pk,), data={
|
||||||
@@ -158,7 +160,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertContains(response, text='added', status_code=200)
|
self.assertContains(response, text='added', status_code=200)
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
@@ -170,7 +172,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.role.permissions.add(permission_folder_view.stored_permission)
|
self.role.permissions.add(permission_folder_view.stored_permission)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_add_multiple_documents', data={
|
'folders:folder_add_multiple_documents', data={
|
||||||
@@ -190,7 +192,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_folder_add_document.stored_permission
|
permission_folder_add_document.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
response = self.post(
|
response = self.post(
|
||||||
'folders:folder_add_multiple_documents', data={
|
'folders:folder_add_multiple_documents', data={
|
||||||
@@ -198,7 +200,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertContains(response, text='added', status_code=200)
|
self.assertContains(response, text='added', status_code=200)
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
@@ -208,7 +210,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
def test_folder_remove_document_view_no_permission(self):
|
def test_folder_remove_document_view_no_permission(self):
|
||||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
folder.documents.add(self.document)
|
folder.documents.add(self.document)
|
||||||
|
|
||||||
@@ -223,7 +225,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
|
|
||||||
def test_folder_remove_document_view_with_permission(self):
|
def test_folder_remove_document_view_with_permission(self):
|
||||||
@@ -233,7 +235,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_folder_remove_document.stored_permission
|
permission_folder_remove_document.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
|
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
|
||||||
|
|
||||||
folder.documents.add(self.document)
|
folder.documents.add(self.document)
|
||||||
self.assertEqual(folder.documents.count(), 1)
|
self.assertEqual(folder.documents.count(), 1)
|
||||||
@@ -245,6 +247,6 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
|
|||||||
}, follow=True
|
}, follow=True
|
||||||
)
|
)
|
||||||
|
|
||||||
folder = Folder.objects.get(pk=folder.pk)
|
folder = Folder.on_organization.get(pk=folder.pk)
|
||||||
self.assertContains(response, text='removed', status_code=200)
|
self.assertContains(response, text='removed', status_code=200)
|
||||||
self.assertEqual(folder.documents.count(), 0)
|
self.assertEqual(folder.documents.count(), 0)
|
||||||
|
|||||||
@@ -34,14 +34,34 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class FolderCreateView(SingleObjectCreateView):
|
class FolderCreateView(SingleObjectCreateView):
|
||||||
fields = ('label',)
|
fields = ('label',)
|
||||||
model = Folder
|
post_action_redirect = reverse_lazy('folders:folder_list')
|
||||||
view_permission = permission_folder_create
|
view_permission = permission_folder_create
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
try:
|
||||||
|
Folder.on_organization.get(label=form.cleaned_data['label'])
|
||||||
|
except Folder.DoesNotExist:
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.user = self.request.user
|
||||||
|
instance.save()
|
||||||
|
return super(FolderCreateView, self).form_valid(form)
|
||||||
|
else:
|
||||||
|
messages.error(
|
||||||
|
self.request,
|
||||||
|
_(
|
||||||
|
'A folder named: %s, already exists.'
|
||||||
|
) % form.cleaned_data['label']
|
||||||
|
)
|
||||||
|
return super(FolderCreateView, self).form_invalid(form)
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'title': _('Create folder'),
|
'title': _('Create folder'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Folder.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class FolderDeleteView(SingleObjectDeleteView):
|
class FolderDeleteView(SingleObjectDeleteView):
|
||||||
model = Folder
|
model = Folder
|
||||||
@@ -54,6 +74,9 @@ class FolderDeleteView(SingleObjectDeleteView):
|
|||||||
'title': _('Delete the folder: %s?') % self.get_object(),
|
'title': _('Delete the folder: %s?') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Folder.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class FolderDetailView(DocumentListView):
|
class FolderDetailView(DocumentListView):
|
||||||
def get_document_queryset(self):
|
def get_document_queryset(self):
|
||||||
@@ -67,23 +90,11 @@ class FolderDetailView(DocumentListView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_folder(self):
|
def get_folder(self):
|
||||||
folder = get_object_or_404(Folder, pk=self.kwargs['pk'])
|
return get_object_or_404(Folder.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
try:
|
|
||||||
Permission.check_permissions(
|
|
||||||
self.request.user, (permission_folder_view,)
|
|
||||||
)
|
|
||||||
except PermissionDenied:
|
|
||||||
AccessControlList.objects.check_access(
|
|
||||||
permission_folder_view, self.request.user, folder
|
|
||||||
)
|
|
||||||
|
|
||||||
return folder
|
|
||||||
|
|
||||||
|
|
||||||
class FolderEditView(SingleObjectEditView):
|
class FolderEditView(SingleObjectEditView):
|
||||||
fields = ('label',)
|
fields = ('label',)
|
||||||
model = Folder
|
|
||||||
object_permission = permission_folder_edit
|
object_permission = permission_folder_edit
|
||||||
post_action_redirect = reverse_lazy('folders:folder_list')
|
post_action_redirect = reverse_lazy('folders:folder_list')
|
||||||
|
|
||||||
@@ -93,9 +104,11 @@ class FolderEditView(SingleObjectEditView):
|
|||||||
'title': _('Edit folder: %s') % self.get_object(),
|
'title': _('Edit folder: %s') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Folder.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class FolderListView(SingleObjectListView):
|
class FolderListView(SingleObjectListView):
|
||||||
model = Folder
|
|
||||||
object_permission = permission_folder_view
|
object_permission = permission_folder_view
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
@@ -104,10 +117,19 @@ class FolderListView(SingleObjectListView):
|
|||||||
'title': _('Folders'),
|
'title': _('Folders'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_folder_queryset(self):
|
||||||
|
return Folder.on_organization.all()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
self.queryset = self.get_folder_queryset()
|
||||||
|
return super(FolderListView, self).get_queryset()
|
||||||
|
|
||||||
|
|
||||||
class DocumentFolderListView(FolderListView):
|
class DocumentFolderListView(FolderListView):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
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:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -132,10 +154,12 @@ class DocumentFolderListView(FolderListView):
|
|||||||
|
|
||||||
|
|
||||||
def folder_add_document(request, document_id=None, document_id_list=None):
|
def folder_add_document(request, document_id=None, document_id_list=None):
|
||||||
|
queryset = Document.on_organization.all()
|
||||||
|
|
||||||
if document_id:
|
if document_id:
|
||||||
queryset = Document.objects.filter(pk=document_id)
|
queryset = queryset.filter(pk=document_id)
|
||||||
elif document_id_list:
|
elif document_id_list:
|
||||||
queryset = Document.objects.filter(pk__in=document_id_list)
|
queryset = queryset.filter(pk__in=document_id_list)
|
||||||
|
|
||||||
if not queryset:
|
if not queryset:
|
||||||
messages.error(request, _('Must provide at least one document.'))
|
messages.error(request, _('Must provide at least one document.'))
|
||||||
@@ -216,12 +240,12 @@ def folder_add_document(request, document_id=None, document_id_list=None):
|
|||||||
def folder_document_remove(request, folder_id, document_id=None, document_id_list=None):
|
def folder_document_remove(request, folder_id, document_id=None, document_id_list=None):
|
||||||
post_action_redirect = None
|
post_action_redirect = None
|
||||||
|
|
||||||
folder = get_object_or_404(Folder, pk=folder_id)
|
folder = get_object_or_404(Folder.on_organization, pk=folder_id)
|
||||||
|
|
||||||
if document_id:
|
if document_id:
|
||||||
queryset = Document.objects.filter(pk=document_id)
|
queryset = Document.on_organization.filter(pk=document_id)
|
||||||
elif document_id_list:
|
elif document_id_list:
|
||||||
queryset = Document.objects.filter(pk__in=document_id_list)
|
queryset = Document.on_organization.filter(pk__in=document_id_list)
|
||||||
|
|
||||||
if not queryset:
|
if not queryset:
|
||||||
messages.error(request, _('Must provide at least one folder document.'))
|
messages.error(request, _('Must provide at least one folder document.'))
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from organizations.admin import OrganizationAdminMixin
|
||||||
|
|
||||||
from .models import MetadataType
|
from .models import MetadataType
|
||||||
|
|
||||||
|
|
||||||
@admin.register(MetadataType)
|
@admin.register(MetadataType)
|
||||||
class MetadataTypeAdmin(admin.ModelAdmin):
|
class MetadataTypeAdmin(OrganizationAdminMixin, admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'name', 'label', 'default', 'lookup', 'validation', 'parser'
|
'name', 'label', 'default', 'lookup', 'validation', 'parser'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ def save_metadata(metadata_dict, document, create=False):
|
|||||||
"""
|
"""
|
||||||
if create:
|
if create:
|
||||||
# Use matched metadata now to create document metadata
|
# Use matched metadata now to create document metadata
|
||||||
document_metadata, created = DocumentMetadata.objects.get_or_create(
|
document_metadata, created = DocumentMetadata.on_organization.get_or_create(
|
||||||
document=document,
|
document=document,
|
||||||
metadata_type=get_object_or_404(
|
metadata_type=get_object_or_404(
|
||||||
MetadataType,
|
MetadataType,
|
||||||
@@ -57,7 +57,7 @@ def save_metadata(metadata_dict, document, create=False):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
document_metadata = DocumentMetadata.objects.get(
|
document_metadata = DocumentMetadata.on_organization.get(
|
||||||
document=document,
|
document=document,
|
||||||
metadata_type=get_object_or_404(
|
metadata_type=get_object_or_404(
|
||||||
MetadataType,
|
MetadataType,
|
||||||
@@ -92,7 +92,7 @@ def metadata_repr_as_list(metadata_list):
|
|||||||
output = []
|
output = []
|
||||||
for metadata_dict in metadata_list:
|
for metadata_dict in metadata_list:
|
||||||
try:
|
try:
|
||||||
output.append('%s - %s' % (MetadataType.objects.get(
|
output.append('%s - %s' % (MetadataType.on_organization.get(
|
||||||
pk=metadata_dict['id']), metadata_dict.get('value', '')))
|
pk=metadata_dict['id']), metadata_dict.get('value', '')))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -107,9 +107,9 @@ def set_bulk_metadata(document, metadata_dictionary):
|
|||||||
]
|
]
|
||||||
|
|
||||||
for metadata_type_name, value in metadata_dictionary.items():
|
for metadata_type_name, value in metadata_dictionary.items():
|
||||||
metadata_type = MetadataType.objects.get(name=metadata_type_name)
|
metadata_type = MetadataType.on_organization.get(name=metadata_type_name)
|
||||||
|
|
||||||
if metadata_type in document_type_metadata_types:
|
if metadata_type in document_type_metadata_types:
|
||||||
DocumentMetadata.objects.get_or_create(
|
DocumentMetadata.on_organization.get_or_create(
|
||||||
document=document, metadata_type=metadata_type, value=value
|
document=document, metadata_type=metadata_type, value=value
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from .serializers import (
|
|||||||
|
|
||||||
class APIMetadataTypeListView(generics.ListCreateAPIView):
|
class APIMetadataTypeListView(generics.ListCreateAPIView):
|
||||||
serializer_class = MetadataTypeSerializer
|
serializer_class = MetadataTypeSerializer
|
||||||
queryset = MetadataType.objects.all()
|
queryset = MetadataType.on_organization.all()
|
||||||
|
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
filter_backends = (MayanObjectPermissionsFilter,)
|
filter_backends = (MayanObjectPermissionsFilter,)
|
||||||
@@ -53,7 +53,7 @@ class APIMetadataTypeListView(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
class APIMetadataTypeView(generics.RetrieveUpdateDestroyAPIView):
|
class APIMetadataTypeView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
serializer_class = MetadataTypeSerializer
|
serializer_class = MetadataTypeSerializer
|
||||||
queryset = MetadataType.objects.all()
|
queryset = MetadataType.on_organization.all()
|
||||||
|
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
mayan_object_permissions = {
|
mayan_object_permissions = {
|
||||||
@@ -92,7 +92,7 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
|
|||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
|
|
||||||
def get_document(self):
|
def get_document(self):
|
||||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
document = self.get_document()
|
document = self.get_document()
|
||||||
@@ -151,7 +151,7 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView):
|
class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
serializer_class = DocumentMetadataSerializer
|
serializer_class = DocumentMetadataSerializer
|
||||||
queryset = DocumentMetadata.objects.all()
|
queryset = DocumentMetadata.on_organization.all()
|
||||||
|
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
mayan_object_permissions = {
|
mayan_object_permissions = {
|
||||||
@@ -220,7 +220,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
document_type = get_object_or_404(
|
document_type = get_object_or_404(
|
||||||
DocumentType, pk=self.kwargs['document_type_pk']
|
DocumentType.on_organization, pk=self.kwargs['document_type_pk']
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -253,7 +253,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
|
|||||||
Add an optional metadata type to a document type.
|
Add an optional metadata type to a document type.
|
||||||
"""
|
"""
|
||||||
document_type = get_object_or_404(
|
document_type = get_object_or_404(
|
||||||
DocumentType, pk=self.kwargs['document_type_pk']
|
DocumentType.on_organization, pk=self.kwargs['document_type_pk']
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -270,7 +270,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
metadata_type = get_object_or_404(
|
metadata_type = get_object_or_404(
|
||||||
MetadataType, pk=serializer.data['metadata_type_pk']
|
MetadataType.on_organization, pk=serializer.data['metadata_type_pk']
|
||||||
)
|
)
|
||||||
document_type_metadata_type = document_type.metadata.create(
|
document_type_metadata_type = document_type.metadata.create(
|
||||||
metadata_type=metadata_type, required=self.required_metadata
|
metadata_type=metadata_type, required=self.required_metadata
|
||||||
@@ -313,7 +313,7 @@ class APIDocumentTypeMetadataTypeView(views.APIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
document_type_metadata_type = get_object_or_404(
|
document_type_metadata_type = get_object_or_404(
|
||||||
DocumentTypeMetadataType, pk=self.kwargs['pk']
|
DocumentTypeMetadataType.on_organization, pk=self.kwargs['pk']
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -109,13 +109,13 @@ MetadataFormSet = formset_factory(MetadataForm, extra=0)
|
|||||||
|
|
||||||
class AddMetadataForm(forms.Form):
|
class AddMetadataForm(forms.Form):
|
||||||
metadata_type = forms.ModelChoiceField(
|
metadata_type = forms.ModelChoiceField(
|
||||||
queryset=MetadataType.objects.all(), label=_('Metadata type')
|
queryset=MetadataType.on_organization.all(), label=_('Metadata type')
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
document_type = kwargs.pop('document_type')
|
document_type = kwargs.pop('document_type')
|
||||||
super(AddMetadataForm, self).__init__(*args, **kwargs)
|
super(AddMetadataForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['metadata_type'].queryset = MetadataType.objects.filter(
|
self.fields['metadata_type'].queryset = MetadataType.on_organization.filter(
|
||||||
pk__in=document_type.metadata.values_list(
|
pk__in=document_type.metadata.values_list(
|
||||||
'metadata_type', flat=True
|
'metadata_type', flat=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def post_post_document_type_change_metadata(sender, instance, **kwargs):
|
|||||||
|
|
||||||
# Add new document type metadata types to document
|
# Add new document type metadata types to document
|
||||||
for document_type_metadata_type in instance.document_type.metadata.filter(required=True):
|
for document_type_metadata_type in instance.document_type.metadata.filter(required=True):
|
||||||
DocumentMetadata.objects.create(
|
DocumentMetadata.on_organization.create(
|
||||||
document=instance,
|
document=instance,
|
||||||
metadata_type=document_type_metadata_type.metadata_type,
|
metadata_type=document_type_metadata_type.metadata_type,
|
||||||
value=None
|
value=None
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class MetadataTypeManager(models.Manager):
|
class MetadataTypeManager(models.Manager):
|
||||||
def get_by_natural_key(self, name):
|
def get_by_natural_key(self, name):
|
||||||
return self.get(name=name)
|
return self.get(name=name)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentMetadataManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
Document = apps.get_model('documents', 'Document')
|
||||||
|
MetadataType = apps.get_model('metadata', 'MetadataType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentMetadataManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document__in=Document.on_organization.all(),
|
||||||
|
metadata_type__in=MetadataType.on_organization.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentTypeMetadataTypeManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
MetadataType = apps.get_model('metadata', 'MetadataType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentTypeMetadataTypeManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_type__in=DocumentType.on_organization.all(),
|
||||||
|
metadata_type__in=MetadataType.on_organization.all()
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# -*- 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'),
|
||||||
|
('metadata', '0007_auto_20150918_0800'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='metadatatype',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(default=organizations.shortcuts.get_current_organization, to='organizations.Organization'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,9 +10,15 @@ from django.utils.module_loading import import_string
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from documents.models import Document, DocumentType
|
from documents.models import Document, DocumentType
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
|
|
||||||
from .classes import MetadataLookup
|
from .classes import MetadataLookup
|
||||||
from .managers import MetadataTypeManager
|
from .managers import (
|
||||||
|
MetadataTypeManager, OrganizationDocumentMetadataManager,
|
||||||
|
OrganizationDocumentTypeMetadataTypeManager
|
||||||
|
)
|
||||||
from .settings import setting_available_parsers, setting_available_validators
|
from .settings import setting_available_parsers, setting_available_validators
|
||||||
|
|
||||||
|
|
||||||
@@ -35,7 +41,9 @@ class MetadataType(models.Model):
|
|||||||
"""
|
"""
|
||||||
Define a type of metadata
|
Define a type of metadata
|
||||||
"""
|
"""
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=48,
|
max_length=48,
|
||||||
help_text=_(
|
help_text=_(
|
||||||
@@ -79,6 +87,12 @@ class MetadataType(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = MetadataTypeManager()
|
objects = MetadataTypeManager()
|
||||||
|
on_organization = CurrentOrganizationManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('label',)
|
||||||
|
verbose_name = _('Metadata type')
|
||||||
|
verbose_name_plural = _('Metadata types')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
@@ -86,11 +100,6 @@ class MetadataType(models.Model):
|
|||||||
def natural_key(self):
|
def natural_key(self):
|
||||||
return (self.name,)
|
return (self.name,)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ('label',)
|
|
||||||
verbose_name = _('Metadata type')
|
|
||||||
verbose_name_plural = _('Metadata types')
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def comma_splitter(string):
|
def comma_splitter(string):
|
||||||
splitter = shlex.shlex(string.encode('utf-8'), posix=True)
|
splitter = shlex.shlex(string.encode('utf-8'), posix=True)
|
||||||
@@ -158,6 +167,14 @@ class DocumentMetadata(models.Model):
|
|||||||
verbose_name=_('Value')
|
verbose_name=_('Value')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationDocumentMetadataManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('document', 'metadata_type')
|
||||||
|
verbose_name = _('Document metadata')
|
||||||
|
verbose_name_plural = _('Document metadata')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return unicode(self.metadata_type)
|
return unicode(self.metadata_type)
|
||||||
|
|
||||||
@@ -184,11 +201,6 @@ class DocumentMetadata(models.Model):
|
|||||||
document_type=self.document.document_type, value=self.value
|
document_type=self.document.document_type, value=self.value
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('document', 'metadata_type')
|
|
||||||
verbose_name = _('Document metadata')
|
|
||||||
verbose_name_plural = _('Document metadata')
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_required(self):
|
def is_required(self):
|
||||||
return self.metadata_type.get_required_for(
|
return self.metadata_type.get_required_for(
|
||||||
@@ -207,10 +219,13 @@ class DocumentTypeMetadataType(models.Model):
|
|||||||
)
|
)
|
||||||
required = models.BooleanField(default=False, verbose_name=_('Required'))
|
required = models.BooleanField(default=False, verbose_name=_('Required'))
|
||||||
|
|
||||||
def __str__(self):
|
objects = models.Manager()
|
||||||
return unicode(self.metadata_type)
|
on_organization = OrganizationDocumentTypeMetadataTypeManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('document_type', 'metadata_type')
|
unique_together = ('document_type', 'metadata_type')
|
||||||
verbose_name = _('Document type metadata type options')
|
verbose_name = _('Document type metadata type options')
|
||||||
verbose_name_plural = _('Document type metadata types options')
|
verbose_name_plural = _('Document type metadata types options')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return unicode(self.metadata_type)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class DocumentNewMetadataSerializer(serializers.Serializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
metadata_type = MetadataType.objects.get(
|
metadata_type = MetadataType.on_organization.get(
|
||||||
pk=validated_data['metadata_type_pk']
|
pk=validated_data['metadata_type_pk']
|
||||||
)
|
)
|
||||||
instance = self.document.metadata.create(
|
instance = self.document.metadata.create(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def task_remove_metadata_type(document_type_id, metadata_type_id):
|
|||||||
app_label='metadata', model_name='DocumentMetadata'
|
app_label='metadata', model_name='DocumentMetadata'
|
||||||
)
|
)
|
||||||
|
|
||||||
DocumentMetadata.objects.filter(
|
DocumentMetadata.on_organization.filter(
|
||||||
document__document_type__id=document_type_id,
|
document__document_type__id=document_type_id,
|
||||||
metadata_type__id=metadata_type_id
|
metadata_type__id=metadata_type_id
|
||||||
).delete()
|
).delete()
|
||||||
@@ -31,7 +31,7 @@ def task_add_required_metadata_type(document_type_id, metadata_type_id):
|
|||||||
app_label='metadata', model_name='MetadataType'
|
app_label='metadata', model_name='MetadataType'
|
||||||
)
|
)
|
||||||
|
|
||||||
metadata_type = MetadataType.objects.get(pk=metadata_type_id)
|
metadata_type = MetadataType.on_organization.get(pk=metadata_type_id)
|
||||||
|
|
||||||
for document in DocumentType.objects.get(pk=document_type_id).documents.all():
|
for document in DocumentType.on_organization.get(pk=document_type_id).documents.all():
|
||||||
document.metadata.create(metadata_type=metadata_type)
|
document.metadata.create(metadata_type=metadata_type)
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
|
from rest_api.tests import GenericAPITestCase
|
||||||
from user_management.tests.literals import (
|
from user_management.tests.literals import (
|
||||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||||
)
|
)
|
||||||
@@ -23,19 +22,12 @@ TEST_METADATA_VALUE = 'test value'
|
|||||||
TEST_METADATA_VALUE_EDITED = 'test value edited'
|
TEST_METADATA_VALUE_EDITED = 'test value edited'
|
||||||
|
|
||||||
|
|
||||||
class MetadataTypeAPITestCase(APITestCase):
|
class MetadataTypeAPITestCase(GenericAPITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(MetadataTypeAPITestCase, self).setUp()
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.admin_user.delete()
|
super(MetadataTypeAPITestCase, self).tearDown()
|
||||||
|
|
||||||
def test_metadata_type_create(self):
|
def test_metadata_type_create(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@@ -45,7 +37,7 @@ class MetadataTypeAPITestCase(APITestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
metadata_type = MetadataType.objects.first()
|
metadata_type = MetadataType.on_organization.first()
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
self.assertEqual(response.data['id'], metadata_type.pk)
|
self.assertEqual(response.data['id'], metadata_type.pk)
|
||||||
@@ -56,7 +48,7 @@ class MetadataTypeAPITestCase(APITestCase):
|
|||||||
self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME)
|
self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME)
|
||||||
|
|
||||||
def test_metadata_type_delete(self):
|
def test_metadata_type_delete(self):
|
||||||
metadata_type = MetadataType.objects.create(
|
metadata_type = MetadataType.on_organization.create(
|
||||||
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,10 +58,10 @@ class MetadataTypeAPITestCase(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 204)
|
self.assertEqual(response.status_code, 204)
|
||||||
|
|
||||||
self.assertEqual(MetadataType.objects.count(), 0)
|
self.assertEqual(MetadataType.on_organization.count(), 0)
|
||||||
|
|
||||||
def test_metadata_type_edit(self):
|
def test_metadata_type_edit(self):
|
||||||
metadata_type = MetadataType.objects.create(
|
metadata_type = MetadataType.on_organization.create(
|
||||||
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -89,28 +81,21 @@ class MetadataTypeAPITestCase(APITestCase):
|
|||||||
self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME_2)
|
self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME_2)
|
||||||
|
|
||||||
|
|
||||||
class DocumentTypeMetadataTypeAPITestCase(APITestCase):
|
class DocumentTypeMetadataTypeAPITestCase(GenericAPITestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(DocumentTypeMetadataTypeAPITestCase, self).setUp()
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
self.document_type = DocumentType.on_organization.create(
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
self.metadata_type = MetadataType.objects.create(
|
self.metadata_type = MetadataType.on_organization.create(
|
||||||
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.admin_user.delete()
|
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentTypeMetadataTypeAPITestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_type_metadata_type_optional_create(self):
|
def test_document_type_metadata_type_optional_create(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@@ -122,7 +107,7 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
|
|
||||||
document_type_metadata_type = DocumentTypeMetadataType.objects.filter(
|
document_type_metadata_type = DocumentTypeMetadataType.on_organization.filter(
|
||||||
document_type=self.document_type, required=False
|
document_type=self.document_type, required=False
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
@@ -142,7 +127,7 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 201)
|
self.assertEqual(response.status_code, 201)
|
||||||
|
|
||||||
document_type_metadata_type = DocumentTypeMetadataType.objects.filter(
|
document_type_metadata_type = DocumentTypeMetadataType.on_organization.filter(
|
||||||
document_type=self.document_type, required=True
|
document_type=self.document_type, required=True
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
@@ -169,23 +154,16 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
|
|||||||
self.assertEqual(self.document_type.metadata.all().count(), 0)
|
self.assertEqual(self.document_type.metadata.all().count(), 0)
|
||||||
|
|
||||||
|
|
||||||
class DocumentMetadataAPITestCase(APITestCase):
|
class DocumentMetadataAPITestCase(GenericAPITestCase):
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(DocumentMetadataAPITestCase, self).setUp()
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
self.document_type = DocumentType.on_organization.create(
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
self.metadata_type = MetadataType.objects.create(
|
self.metadata_type = MetadataType.on_organization.create(
|
||||||
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
label=TEST_METADATA_TYPE_LABEL, name=TEST_METADATA_TYPE_NAME
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -199,8 +177,8 @@ class DocumentMetadataAPITestCase(APITestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.admin_user.delete()
|
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(DocumentMetadataAPITestCase, self).tearDown()
|
||||||
|
|
||||||
def test_document_metadata_create(self):
|
def test_document_metadata_create(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@@ -213,7 +191,7 @@ class DocumentMetadataAPITestCase(APITestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
document_metadata = DocumentMetadata.objects.get(
|
document_metadata = DocumentMetadata.on_organization.get(
|
||||||
document=self.document
|
document=self.document
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.core.files.base import File
|
from django.core.files.base import File
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.test import TestCase, override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
|
||||||
|
from organizations.tests import OrganizationTestCase
|
||||||
|
|
||||||
from ..models import MetadataType, DocumentMetadata
|
from ..models import MetadataType, DocumentMetadata
|
||||||
|
|
||||||
@@ -18,13 +19,14 @@ from .literals import (
|
|||||||
|
|
||||||
|
|
||||||
@override_settings(OCR_AUTO_OCR=False)
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
class MetadataTestCase(TestCase):
|
class MetadataTestCase(OrganizationTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.document_type = DocumentType.objects.create(
|
super(MetadataTestCase, self).setUp()
|
||||||
|
self.document_type = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
self.metadata_type = MetadataType.objects.create(
|
self.metadata_type = MetadataType.on_organization.create(
|
||||||
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ class MetadataTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(MetadataTestCase, self).tearDown()
|
||||||
|
|
||||||
def test_no_default(self):
|
def test_no_default(self):
|
||||||
document_metadata = DocumentMetadata(
|
document_metadata = DocumentMetadata(
|
||||||
|
|||||||
65
mayan/apps/metadata/tests/test_organization_views.py
Normal file
65
mayan/apps/metadata/tests/test_organization_views.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import MetadataType
|
||||||
|
|
||||||
|
from .literals import (
|
||||||
|
TEST_DOCUMENT_METADATA_VALUE_2, TEST_METADATA_TYPE_LABEL,
|
||||||
|
TEST_METADATA_TYPE_LABEL_2, TEST_METADATA_TYPE_NAME,
|
||||||
|
TEST_METADATA_TYPE_NAME_2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataOrganizationViewTestCase(OrganizationViewTestCase):
|
||||||
|
def create_metadata_type(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.metadata_type = MetadataType.on_organization.create(
|
||||||
|
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_metadata_type_creation(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
response = self.post(
|
||||||
|
'metadata:setup_metadata_type_create', data={
|
||||||
|
'label': TEST_METADATA_TYPE_LABEL,
|
||||||
|
'name': TEST_METADATA_TYPE_NAME
|
||||||
|
}, follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertContains(response, text='created', status_code=200)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
self.assertEqual(MetadataType.on_organization.count(), 0)
|
||||||
|
|
||||||
|
def test_metadata_type_delete(self):
|
||||||
|
self.create_metadata_type()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'metadata:setup_metadata_type_delete',
|
||||||
|
args=(self.metadata_type.pk,), follow=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.assertEqual(MetadataType.on_organization.count(), 1)
|
||||||
|
|
||||||
|
def test_metadata_type_edit(self):
|
||||||
|
self.create_metadata_type()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'metadata:setup_metadata_type_edit',
|
||||||
|
args=(self.metadata_type.pk,), data={
|
||||||
|
'label': TEST_METADATA_TYPE_LABEL_2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.metadata_type.refresh_from_db()
|
||||||
|
self.assertEqual(
|
||||||
|
self.metadata_type.label, TEST_METADATA_TYPE_LABEL
|
||||||
|
)
|
||||||
@@ -30,7 +30,7 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(DocumentMetadataTestCase, self).setUp()
|
super(DocumentMetadataTestCase, self).setUp()
|
||||||
|
|
||||||
self.metadata_type = MetadataType.objects.create(
|
self.metadata_type = MetadataType.on_organization.create(
|
||||||
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
name=TEST_METADATA_TYPE_NAME, label=TEST_METADATA_TYPE_LABEL
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -94,11 +94,11 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase):
|
|||||||
permission_metadata_document_edit.stored_permission
|
permission_metadata_document_edit.stored_permission
|
||||||
)
|
)
|
||||||
|
|
||||||
document_type_2 = DocumentType.objects.create(
|
document_type_2 = DocumentType.on_organization.create(
|
||||||
label=TEST_DOCUMENT_TYPE_2
|
label=TEST_DOCUMENT_TYPE_2
|
||||||
)
|
)
|
||||||
|
|
||||||
metadata_type_2 = MetadataType.objects.create(
|
metadata_type_2 = MetadataType.on_organization.create(
|
||||||
name=TEST_METADATA_TYPE_NAME_2, label=TEST_METADATA_TYPE_LABEL_2
|
name=TEST_METADATA_TYPE_NAME_2, label=TEST_METADATA_TYPE_LABEL_2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ from .permissions import (
|
|||||||
|
|
||||||
def metadata_edit(request, document_id=None, document_id_list=None):
|
def metadata_edit(request, document_id=None, document_id_list=None):
|
||||||
if document_id:
|
if document_id:
|
||||||
documents = Document.objects.filter(pk=document_id)
|
documents = Document.on_organization.filter(pk=document_id)
|
||||||
if not documents:
|
if not documents:
|
||||||
raise Document.DoesNotExist
|
raise Document.DoesNotExist
|
||||||
elif document_id_list:
|
elif document_id_list:
|
||||||
documents = Document.objects.filter(pk__in=document_id_list)
|
documents = Document.on_organization.filter(pk__in=document_id_list)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -186,11 +186,11 @@ def metadata_multiple_edit(request):
|
|||||||
|
|
||||||
def metadata_add(request, document_id=None, document_id_list=None):
|
def metadata_add(request, document_id=None, document_id_list=None):
|
||||||
if document_id:
|
if document_id:
|
||||||
documents = Document.objects.filter(pk=document_id)
|
documents = Document.on_organization.filter(pk=document_id)
|
||||||
if not documents:
|
if not documents:
|
||||||
raise Document.DoesNotExist
|
raise Document.DoesNotExist
|
||||||
elif document_id_list:
|
elif document_id_list:
|
||||||
documents = Document.objects.select_related('document_type').filter(pk__in=document_id_list)
|
documents = Document.on_organization.select_related('document_type').filter(pk__in=document_id_list)
|
||||||
if len(set([document.document_type.pk for document in documents])) > 1:
|
if len(set([document.document_type.pk for document in documents])) > 1:
|
||||||
messages.error(
|
messages.error(
|
||||||
request, _('Only select documents of the same type.')
|
request, _('Only select documents of the same type.')
|
||||||
@@ -241,7 +241,7 @@ def metadata_add(request, document_id=None, document_id_list=None):
|
|||||||
metadata_type = form.cleaned_data['metadata_type']
|
metadata_type = form.cleaned_data['metadata_type']
|
||||||
for document in documents:
|
for document in documents:
|
||||||
try:
|
try:
|
||||||
document_metadata, created = DocumentMetadata.objects.get_or_create(
|
document_metadata, created = DocumentMetadata.on_organization.get_or_create(
|
||||||
document=document,
|
document=document,
|
||||||
metadata_type=metadata_type,
|
metadata_type=metadata_type,
|
||||||
defaults={'value': ''}
|
defaults={'value': ''}
|
||||||
@@ -331,11 +331,11 @@ def metadata_multiple_add(request):
|
|||||||
|
|
||||||
def metadata_remove(request, document_id=None, document_id_list=None):
|
def metadata_remove(request, document_id=None, document_id_list=None):
|
||||||
if document_id:
|
if document_id:
|
||||||
documents = Document.objects.filter(pk=document_id)
|
documents = Document.on_organization.filter(pk=document_id)
|
||||||
if not documents:
|
if not documents:
|
||||||
raise Document.DoesNotExist
|
raise Document.DoesNotExist
|
||||||
elif document_id_list:
|
elif document_id_list:
|
||||||
documents = Document.objects.filter(pk__in=document_id_list)
|
documents = Document.on_organization.filter(pk__in=document_id_list)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Permission.check_permissions(
|
Permission.check_permissions(
|
||||||
@@ -416,10 +416,10 @@ def metadata_remove(request, document_id=None, document_id_list=None):
|
|||||||
for form in formset.forms:
|
for form in formset.forms:
|
||||||
if form.cleaned_data['update']:
|
if form.cleaned_data['update']:
|
||||||
metadata_type = get_object_or_404(
|
metadata_type = get_object_or_404(
|
||||||
MetadataType, pk=form.cleaned_data['id']
|
MetadataType.on_organization, pk=form.cleaned_data['id']
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
document_metadata = DocumentMetadata.objects.get(
|
document_metadata = DocumentMetadata.on_organization.get(
|
||||||
document=document, metadata_type=metadata_type
|
document=document, metadata_type=metadata_type
|
||||||
)
|
)
|
||||||
document_metadata.delete()
|
document_metadata.delete()
|
||||||
@@ -494,7 +494,7 @@ class DocumentMetadataListView(SingleObjectListView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_document(self):
|
def get_document(self):
|
||||||
return get_object_or_404(Document, pk=self.kwargs['pk'])
|
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
document = self.get_document()
|
document = self.get_document()
|
||||||
@@ -512,13 +512,14 @@ class DocumentMetadataListView(SingleObjectListView):
|
|||||||
class MetadataTypeCreateView(SingleObjectCreateView):
|
class MetadataTypeCreateView(SingleObjectCreateView):
|
||||||
extra_context = {'title': _('Create metadata type')}
|
extra_context = {'title': _('Create metadata type')}
|
||||||
form_class = MetadataTypeForm
|
form_class = MetadataTypeForm
|
||||||
model = MetadataType
|
|
||||||
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
||||||
view_permission = permission_metadata_type_create
|
view_permission = permission_metadata_type_create
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return MetadataType.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MetadataTypeDeleteView(SingleObjectDeleteView):
|
class MetadataTypeDeleteView(SingleObjectDeleteView):
|
||||||
model = MetadataType
|
|
||||||
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
||||||
view_permission = permission_metadata_type_delete
|
view_permission = permission_metadata_type_delete
|
||||||
|
|
||||||
@@ -529,10 +530,12 @@ class MetadataTypeDeleteView(SingleObjectDeleteView):
|
|||||||
'title': _('Delete the metadata type: %s?') % self.get_object(),
|
'title': _('Delete the metadata type: %s?') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return MetadataType.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MetadataTypeEditView(SingleObjectEditView):
|
class MetadataTypeEditView(SingleObjectEditView):
|
||||||
form_class = MetadataTypeForm
|
form_class = MetadataTypeForm
|
||||||
model = MetadataType
|
|
||||||
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
|
||||||
view_permission = permission_metadata_type_edit
|
view_permission = permission_metadata_type_edit
|
||||||
|
|
||||||
@@ -542,13 +545,13 @@ class MetadataTypeEditView(SingleObjectEditView):
|
|||||||
'title': _('Edit metadata type: %s') % self.get_object(),
|
'title': _('Edit metadata type: %s') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return MetadataType.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MetadataTypeListView(SingleObjectListView):
|
class MetadataTypeListView(SingleObjectListView):
|
||||||
view_permission = permission_metadata_type_view
|
view_permission = permission_metadata_type_view
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return MetadataType.objects.all()
|
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
return {
|
return {
|
||||||
'extra_columns': (
|
'extra_columns': (
|
||||||
@@ -561,6 +564,9 @@ class MetadataTypeListView(SingleObjectListView):
|
|||||||
'title': _('Metadata types'),
|
'title': _('Metadata types'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return MetadataType.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
|
class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
|
||||||
decode_content_type = True
|
decode_content_type = True
|
||||||
@@ -572,12 +578,12 @@ class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
|
|||||||
self.get_object().metadata.create(metadata_type=item, required=False)
|
self.get_object().metadata.create(metadata_type=item, required=False)
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return get_object_or_404(DocumentType, pk=self.kwargs['pk'])
|
return get_object_or_404(DocumentType.on_organization, pk=self.kwargs['pk'])
|
||||||
|
|
||||||
def left_list(self):
|
def left_list(self):
|
||||||
return AssignRemoveView.generate_choices(
|
return AssignRemoveView.generate_choices(
|
||||||
set(MetadataType.objects.all()) - set(
|
set(MetadataType.on_organization.all()) - set(
|
||||||
MetadataType.objects.filter(
|
MetadataType.on_organization.filter(
|
||||||
id__in=self.get_object().metadata.values_list(
|
id__in=self.get_object().metadata.values_list(
|
||||||
'metadata_type', flat=True
|
'metadata_type', flat=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from organizations.managers import CurrentOrganizationManager
|
||||||
|
|
||||||
|
|
||||||
class MessageManager(models.Manager):
|
class MessageManager(models.Manager):
|
||||||
def get_for_now(self):
|
def get_for_now(self):
|
||||||
@@ -9,3 +12,10 @@ class MessageManager(models.Manager):
|
|||||||
return self.filter(enabled=True).filter(
|
return self.filter(enabled=True).filter(
|
||||||
Q(start_datetime__isnull=True) | Q(start_datetime__lte=now)
|
Q(start_datetime__isnull=True) | Q(start_datetime__lte=now)
|
||||||
).filter(Q(end_datetime__isnull=True) | Q(end_datetime__gte=now))
|
).filter(Q(end_datetime__isnull=True) | Q(end_datetime__gte=now))
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationMessageManager(MessageManager, CurrentOrganizationManager):
|
||||||
|
def get_queryset(self):
|
||||||
|
return super(OrganizationMessageManager, self).get_queryset().filter(
|
||||||
|
**{self._get_field_name() + '__id': settings.ORGANIZATION_ID}
|
||||||
|
)
|
||||||
|
|||||||
@@ -13,12 +13,36 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='MessageOfTheDay',
|
name='MessageOfTheDay',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
(
|
||||||
('label', models.CharField(max_length=32, verbose_name='Label')),
|
'id', models.AutoField(
|
||||||
('message', models.TextField(verbose_name='Message', blank=True)),
|
verbose_name='ID', serialize=False, auto_created=True,
|
||||||
('enabled', models.BooleanField(default=True, verbose_name='Enabled')),
|
primary_key=True
|
||||||
('start_datetime', models.DateTimeField(verbose_name='Start date time', blank=True)),
|
)
|
||||||
('end_datetime', models.DateTimeField(verbose_name='End date time', blank=True)),
|
),
|
||||||
|
(
|
||||||
|
'label',
|
||||||
|
models.CharField(max_length=32, verbose_name='Label')
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'message',
|
||||||
|
models.TextField(verbose_name='Message', blank=True)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'enabled',
|
||||||
|
models.BooleanField(default=True, verbose_name='Enabled')
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'start_datetime',
|
||||||
|
models.DateTimeField(
|
||||||
|
verbose_name='Start date time', blank=True
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'end_datetime',
|
||||||
|
models.DateTimeField(
|
||||||
|
verbose_name='End date time', blank=True
|
||||||
|
)
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Message of the day',
|
'verbose_name': 'Message of the day',
|
||||||
|
|||||||
@@ -14,11 +14,15 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='messageoftheday',
|
model_name='messageoftheday',
|
||||||
name='end_datetime',
|
name='end_datetime',
|
||||||
field=models.DateTimeField(null=True, verbose_name='End date time', blank=True),
|
field=models.DateTimeField(
|
||||||
|
null=True, verbose_name='End date time', blank=True
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='messageoftheday',
|
model_name='messageoftheday',
|
||||||
name='start_datetime',
|
name='start_datetime',
|
||||||
field=models.DateTimeField(null=True, verbose_name='Start date time', blank=True),
|
field=models.DateTimeField(
|
||||||
|
null=True, verbose_name='Start date time', blank=True
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,21 +14,35 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name='message',
|
||||||
name='end_datetime',
|
name='end_datetime',
|
||||||
field=models.DateTimeField(help_text='Date and time until when this message is to be displayed.', null=True, verbose_name='End date time', blank=True),
|
field=models.DateTimeField(
|
||||||
|
help_text='Date and time until when this message is to be '
|
||||||
|
'displayed.', null=True, verbose_name='End date time',
|
||||||
|
blank=True
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name='message',
|
||||||
name='label',
|
name='label',
|
||||||
field=models.CharField(help_text='Short description of this message.', max_length=32, verbose_name='Label'),
|
field=models.CharField(
|
||||||
|
help_text='Short description of this message.', max_length=32,
|
||||||
|
verbose_name='Label'
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name='message',
|
||||||
name='message',
|
name='message',
|
||||||
field=models.TextField(help_text='The actual message to be displayed.', verbose_name='Message'),
|
field=models.TextField(
|
||||||
|
help_text='The actual message to be displayed.',
|
||||||
|
verbose_name='Message'
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='message',
|
model_name='message',
|
||||||
name='start_datetime',
|
name='start_datetime',
|
||||||
field=models.DateTimeField(help_text='Date and time after which this message will be displayed.', null=True, verbose_name='Start date time', blank=True),
|
field=models.DateTimeField(
|
||||||
|
help_text='Date and time after which this message will be '
|
||||||
|
'displayed.', null=True, verbose_name='Start date time',
|
||||||
|
blank=True
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
24
mayan/apps/motd/migrations/0006_message_organization.py
Normal file
24
mayan/apps/motd/migrations/0006_message_organization.py
Normal file
@@ -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'),
|
||||||
|
('motd', '0005_auto_20160510_0025'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='message',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
default=organizations.shortcuts.get_current_organization,
|
||||||
|
to='organizations.Organization'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -4,11 +4,17 @@ from django.db import models
|
|||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .managers import MessageManager
|
from organizations.models import Organization
|
||||||
|
from organizations.shortcuts import get_current_organization
|
||||||
|
|
||||||
|
from .managers import MessageManager, OrganizationMessageManager
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Message(models.Model):
|
class Message(models.Model):
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
Organization, default=get_current_organization
|
||||||
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=32, help_text=_('Short description of this message.'),
|
max_length=32, help_text=_('Short description of this message.'),
|
||||||
verbose_name=_('Label')
|
verbose_name=_('Label')
|
||||||
@@ -30,6 +36,7 @@ class Message(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
objects = MessageManager()
|
objects = MessageManager()
|
||||||
|
on_organization = OrganizationMessageManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Message')
|
verbose_name = _('Message')
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ register = Library()
|
|||||||
|
|
||||||
@register.inclusion_tag('motd/messages.html')
|
@register.inclusion_tag('motd/messages.html')
|
||||||
def motd():
|
def motd():
|
||||||
return {'messages': Message.objects.get_for_now()}
|
return {'messages': Message.on_organization.get_for_now()}
|
||||||
|
|||||||
5
mayan/apps/motd/tests/literals.py
Normal file
5
mayan/apps/motd/tests/literals.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
TEST_MESSAGE_LABEL = 'test message label'
|
||||||
|
TEST_MESSAGE_LABEL_EDITED = 'test message label edited'
|
||||||
|
TEST_MESSAGE_TEXT = 'test message text'
|
||||||
@@ -5,20 +5,27 @@ from datetime import timedelta
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from organizations.models import Organization
|
||||||
|
from organizations.utils import create_default_organization
|
||||||
|
|
||||||
from ..models import Message
|
from ..models import Message
|
||||||
|
|
||||||
TEST_LABEL = 'test label'
|
from .literals import TEST_MESSAGE_LABEL, TEST_MESSAGE_TEXT
|
||||||
TEST_MESSAGE = 'test message'
|
|
||||||
|
|
||||||
|
|
||||||
class MOTDTestCase(TestCase):
|
class MOTDTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.motd = Message.objects.create(
|
create_default_organization()
|
||||||
label=TEST_LABEL, message=TEST_MESSAGE
|
self.motd = Message.on_organization.create(
|
||||||
|
label=TEST_MESSAGE_LABEL, message=TEST_MESSAGE_TEXT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
Organization.objects.all().delete()
|
||||||
|
Organization.objects.clear_cache()
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
queryset = Message.objects.get_for_now()
|
queryset = Message.on_organization.get_for_now()
|
||||||
|
|
||||||
self.assertEqual(queryset.exists(), True)
|
self.assertEqual(queryset.exists(), True)
|
||||||
|
|
||||||
@@ -26,7 +33,7 @@ class MOTDTestCase(TestCase):
|
|||||||
self.motd.start_datetime = timezone.now() - timedelta(days=1)
|
self.motd.start_datetime = timezone.now() - timedelta(days=1)
|
||||||
self.motd.save()
|
self.motd.save()
|
||||||
|
|
||||||
queryset = Message.objects.get_for_now()
|
queryset = Message.on_organization.get_for_now()
|
||||||
|
|
||||||
self.assertEqual(queryset.first(), self.motd)
|
self.assertEqual(queryset.first(), self.motd)
|
||||||
|
|
||||||
@@ -35,7 +42,7 @@ class MOTDTestCase(TestCase):
|
|||||||
self.motd.end_datetime = timezone.now() - timedelta(days=1)
|
self.motd.end_datetime = timezone.now() - timedelta(days=1)
|
||||||
self.motd.save()
|
self.motd.save()
|
||||||
|
|
||||||
queryset = Message.objects.get_for_now()
|
queryset = Message.on_organization.get_for_now()
|
||||||
|
|
||||||
self.assertEqual(queryset.exists(), False)
|
self.assertEqual(queryset.exists(), False)
|
||||||
|
|
||||||
@@ -43,6 +50,6 @@ class MOTDTestCase(TestCase):
|
|||||||
self.motd.enabled = False
|
self.motd.enabled = False
|
||||||
self.motd.save()
|
self.motd.save()
|
||||||
|
|
||||||
queryset = Message.objects.get_for_now()
|
queryset = Message.on_organization.get_for_now()
|
||||||
|
|
||||||
self.assertEqual(queryset.exists(), False)
|
self.assertEqual(queryset.exists(), False)
|
||||||
|
|||||||
67
mayan/apps/motd/tests/test_organization_views.py
Normal file
67
mayan/apps/motd/tests/test_organization_views.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from organizations.tests.test_organization_views import OrganizationViewTestCase
|
||||||
|
|
||||||
|
from ..models import Message
|
||||||
|
|
||||||
|
from .literals import (
|
||||||
|
TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageOrganizationViewTestCase(OrganizationViewTestCase):
|
||||||
|
def create_message(self):
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
self.message = Message.on_organization.create(
|
||||||
|
label=TEST_MESSAGE_LABEL, message=TEST_MESSAGE_TEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_message_create_view(self):
|
||||||
|
# Create a message for organization A
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
|
||||||
|
response = self.post(
|
||||||
|
'motd:message_create', data={
|
||||||
|
'label': TEST_MESSAGE_LABEL, 'message': TEST_MESSAGE_TEXT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertNotContains(response, text='danger', status_code=302)
|
||||||
|
self.assertEqual(Message.on_organization.count(), 1)
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
self.assertEqual(Message.on_organization.count(), 0)
|
||||||
|
|
||||||
|
def test_message_delete_view(self):
|
||||||
|
# Create a message for organization A
|
||||||
|
self.create_message()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
response = self.post(
|
||||||
|
'motd:message_delete', args=(self.message.pk,)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_message_edit_view(self):
|
||||||
|
# Create a message for organization A
|
||||||
|
self.create_message()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
# Make sure admin for organization B cannot edit the message
|
||||||
|
response = self.post(
|
||||||
|
'motd:message_edit', args=(self.message.pk,), data={
|
||||||
|
'label': TEST_MESSAGE_LABEL_EDITED
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_message_messageged_item_list_view(self):
|
||||||
|
# Create a message for organization A
|
||||||
|
self.create_message()
|
||||||
|
|
||||||
|
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
|
||||||
|
# Make sure admin for organization B cannot find the message for
|
||||||
|
# organization A
|
||||||
|
response = self.get('motd:message_list')
|
||||||
|
self.assertNotContains(
|
||||||
|
response, text=self.message.label, status_code=200
|
||||||
|
)
|
||||||
@@ -10,7 +10,9 @@ urlpatterns = patterns(
|
|||||||
'',
|
'',
|
||||||
url(r'^list/$', MessageListView.as_view(), name='message_list'),
|
url(r'^list/$', MessageListView.as_view(), name='message_list'),
|
||||||
url(r'^create/$', MessageCreateView.as_view(), name='message_create'),
|
url(r'^create/$', MessageCreateView.as_view(), name='message_create'),
|
||||||
url(r'^(?P<pk>\d+)/edit/$', MessageEditView.as_view(), name='message_edit'),
|
url(
|
||||||
|
r'^(?P<pk>\d+)/edit/$', MessageEditView.as_view(), name='message_edit'
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'^(?P<pk>\d+)/delete/$', MessageDeleteView.as_view(),
|
r'^(?P<pk>\d+)/delete/$', MessageDeleteView.as_view(),
|
||||||
name='message_delete'
|
name='message_delete'
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class MessageCreateView(SingleObjectCreateView):
|
class MessageCreateView(SingleObjectCreateView):
|
||||||
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
|
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
|
||||||
model = Message
|
|
||||||
view_permission = permission_message_create
|
view_permission = permission_message_create
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
@@ -29,9 +28,11 @@ class MessageCreateView(SingleObjectCreateView):
|
|||||||
'title': _('Create message'),
|
'title': _('Create message'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Message.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MessageDeleteView(SingleObjectDeleteView):
|
class MessageDeleteView(SingleObjectDeleteView):
|
||||||
model = Message
|
|
||||||
object_permission = permission_message_delete
|
object_permission = permission_message_delete
|
||||||
post_action_redirect = reverse_lazy('motd:message_list')
|
post_action_redirect = reverse_lazy('motd:message_list')
|
||||||
|
|
||||||
@@ -42,10 +43,12 @@ class MessageDeleteView(SingleObjectDeleteView):
|
|||||||
'title': _('Delete the message: %s?') % self.get_object(),
|
'title': _('Delete the message: %s?') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Message.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MessageEditView(SingleObjectEditView):
|
class MessageEditView(SingleObjectEditView):
|
||||||
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
|
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
|
||||||
model = Message
|
|
||||||
object_permission = permission_message_edit
|
object_permission = permission_message_edit
|
||||||
post_action_redirect = reverse_lazy('motd:message_list')
|
post_action_redirect = reverse_lazy('motd:message_list')
|
||||||
|
|
||||||
@@ -55,9 +58,11 @@ class MessageEditView(SingleObjectEditView):
|
|||||||
'title': _('Edit message: %s') % self.get_object(),
|
'title': _('Edit message: %s') % self.get_object(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Message.on_organization.all()
|
||||||
|
|
||||||
|
|
||||||
class MessageListView(SingleObjectListView):
|
class MessageListView(SingleObjectListView):
|
||||||
model = Message
|
|
||||||
object_permission = permission_message_view
|
object_permission = permission_message_view
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
@@ -65,3 +70,6 @@ class MessageListView(SingleObjectListView):
|
|||||||
'hide_link': True,
|
'hide_link': True,
|
||||||
'title': _('Messages'),
|
'title': _('Messages'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Message.on_organization.all()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class APIDocumentOCRView(generics.GenericAPIView):
|
|||||||
'POST': (permission_ocr_document,)
|
'POST': (permission_ocr_document,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = Document.objects.all()
|
queryset = Document.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
return None
|
return None
|
||||||
@@ -44,7 +44,7 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
|
|||||||
'POST': (permission_ocr_document,)
|
'POST': (permission_ocr_document,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentVersion.objects.all()
|
queryset = DocumentVersion.on_organization.all()
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
return None
|
return None
|
||||||
@@ -83,7 +83,7 @@ class APIDocumentPageContentView(generics.RetrieveAPIView):
|
|||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
serializer_class = DocumentPageContentSerializer
|
serializer_class = DocumentPageContentSerializer
|
||||||
queryset = DocumentPage.objects.all()
|
queryset = DocumentPage.on_organization.all()
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class OCRBackendBase(object):
|
|||||||
image = document_page.get_image()
|
image = document_page.get_image()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
document_page_content, created = DocumentPageContent.objects.get_or_create(
|
document_page_content, created = DocumentPageContent.on_organization.get_or_create(
|
||||||
document_page=document_page
|
document_page=document_page
|
||||||
)
|
)
|
||||||
document_page_content.content = self.execute(
|
document_page_content.content = self.execute(
|
||||||
|
|||||||
@@ -57,5 +57,5 @@ class DocumentContentForm(forms.Form):
|
|||||||
|
|
||||||
class DocumentTypeSelectForm(forms.Form):
|
class DocumentTypeSelectForm(forms.Form):
|
||||||
document_type = forms.ModelChoiceField(
|
document_type = forms.ModelChoiceField(
|
||||||
queryset=DocumentType.objects.all(), label=('Document type')
|
queryset=DocumentType.on_organization.all(), label=('Document type')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ def initialize_new_ocr_settings(sender, instance, **kwargs):
|
|||||||
DocumentTypeSettings = get_model('ocr', 'DocumentTypeSettings')
|
DocumentTypeSettings = get_model('ocr', 'DocumentTypeSettings')
|
||||||
|
|
||||||
if kwargs['created']:
|
if kwargs['created']:
|
||||||
DocumentTypeSettings.objects.create(
|
DocumentTypeSettings.on_organization.create(
|
||||||
document_type=instance, auto_ocr=setting_auto_ocr.value
|
document_type=instance, auto_ocr=setting_auto_ocr.value
|
||||||
)
|
)
|
||||||
|
|||||||
37
mayan/apps/ocr/managers.py
Normal file
37
mayan/apps/ocr/managers.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentTypeSettingsManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentType = apps.get_model('documents', 'DocumentType')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentTypeSettingsManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_type__in=DocumentType.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentVersionOCRErrorManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentVersion = apps.get_model('documents', 'DocumentVersion')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentVersionOCRErrorManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_version__in=DocumentVersion.on_organization.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizationDocumentPageContentManager(models.Manager):
|
||||||
|
def get_queryset(self):
|
||||||
|
DocumentPage = apps.get_model('documents', 'DocumentPage')
|
||||||
|
|
||||||
|
return super(
|
||||||
|
OrganizationDocumentPageContentManager, self
|
||||||
|
).get_queryset().filter(
|
||||||
|
document_page__in=DocumentPage.on_organization.all(),
|
||||||
|
)
|
||||||
@@ -6,6 +6,12 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from documents.models import DocumentPage, DocumentType, DocumentVersion
|
from documents.models import DocumentPage, DocumentType, DocumentVersion
|
||||||
|
|
||||||
|
from .managers import (
|
||||||
|
OrganizationDocumentTypeSettingsManager,
|
||||||
|
OrganizationDocumentVersionOCRErrorManager,
|
||||||
|
OrganizationDocumentPageContentManager
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DocumentTypeSettings(models.Model):
|
class DocumentTypeSettings(models.Model):
|
||||||
"""
|
"""
|
||||||
@@ -20,6 +26,9 @@ class DocumentTypeSettings(models.Model):
|
|||||||
verbose_name=_('Automatically queue newly created documents for OCR.')
|
verbose_name=_('Automatically queue newly created documents for OCR.')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
on_organization = OrganizationDocumentTypeSettingsManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Document type settings')
|
verbose_name = _('Document type settings')
|
||||||
verbose_name_plural = _('Document types settings')
|
verbose_name_plural = _('Document types settings')
|
||||||
@@ -35,14 +44,17 @@ class DocumentVersionOCRError(models.Model):
|
|||||||
)
|
)
|
||||||
result = models.TextField(blank=True, null=True, verbose_name=_('Result'))
|
result = models.TextField(blank=True, null=True, verbose_name=_('Result'))
|
||||||
|
|
||||||
def __str__(self):
|
objects = models.Manager()
|
||||||
return unicode(self.document_version)
|
on_organization = OrganizationDocumentVersionOCRErrorManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('datetime_submitted',)
|
ordering = ('datetime_submitted',)
|
||||||
verbose_name = _('Document Version OCR Error')
|
verbose_name = _('Document Version OCR Error')
|
||||||
verbose_name_plural = _('Document Version OCR Errors')
|
verbose_name_plural = _('Document Version OCR Errors')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return unicode(self.document_version)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class DocumentPageContent(models.Model):
|
class DocumentPageContent(models.Model):
|
||||||
@@ -55,9 +67,12 @@ class DocumentPageContent(models.Model):
|
|||||||
)
|
)
|
||||||
content = models.TextField(blank=True, verbose_name=_('Content'))
|
content = models.TextField(blank=True, verbose_name=_('Content'))
|
||||||
|
|
||||||
def __str__(self):
|
objects = models.Manager()
|
||||||
return unicode(self.document_page)
|
on_organization = OrganizationDocumentPageContentManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Document page content')
|
verbose_name = _('Document page content')
|
||||||
verbose_name_plural = _('Document pages contents')
|
verbose_name_plural = _('Document pages contents')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return unicode(self.document_page)
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class Parser(object):
|
|||||||
file_object = document_page.document_version.get_intermidiate_file()
|
file_object = document_page.document_version.get_intermidiate_file()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
document_page_content, created = DocumentPageContent.objects.get_or_create(
|
document_page_content, created = DocumentPageContent.on_organization.get_or_create(
|
||||||
document_page=document_page
|
document_page=document_page
|
||||||
)
|
)
|
||||||
document_page_content.content = self.execute(
|
document_page_content.content = self.execute(
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ def task_do_ocr(self, document_version_pk):
|
|||||||
logger.debug('acquired lock: %s', lock_id)
|
logger.debug('acquired lock: %s', lock_id)
|
||||||
document_version = None
|
document_version = None
|
||||||
try:
|
try:
|
||||||
document_version = DocumentVersion.objects.get(pk=document_version_pk)
|
document_version = DocumentVersion.on_organization.get(
|
||||||
|
pk=document_version_pk
|
||||||
|
)
|
||||||
logger.info(
|
logger.info(
|
||||||
'Starting document OCR for document version: %s',
|
'Starting document OCR for document version: %s',
|
||||||
document_version
|
document_version
|
||||||
@@ -53,7 +55,7 @@ def task_do_ocr(self, document_version_pk):
|
|||||||
exception
|
exception
|
||||||
)
|
)
|
||||||
if document_version:
|
if document_version:
|
||||||
entry, created = DocumentVersionOCRError.objects.get_or_create(
|
entry, created = DocumentVersionOCRError.on_organization.get_or_create(
|
||||||
document_version=document_version
|
document_version=document_version
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,7 +74,7 @@ def task_do_ocr(self, document_version_pk):
|
|||||||
'OCR complete for document version: %s', document_version
|
'OCR complete for document version: %s', document_version
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
entry = DocumentVersionOCRError.objects.get(
|
entry = DocumentVersionOCRError.on_organization.get(
|
||||||
document_version=document_version
|
document_version=document_version
|
||||||
)
|
)
|
||||||
except DocumentVersionOCRError.DoesNotExist:
|
except DocumentVersionOCRError.DoesNotExist:
|
||||||
|
|||||||
@@ -1,36 +1,23 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
from user_management.tests import (
|
from rest_api.tests import GenericAPITestCase
|
||||||
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OCRAPITestCase(APITestCase):
|
class OCRAPITestCase(GenericAPITestCase):
|
||||||
"""
|
"""
|
||||||
Test the OCR app API endpoints
|
Test the OCR app API endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin_user = get_user_model().objects.create_superuser(
|
super(OCRAPITestCase, self).setUp()
|
||||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
|
||||||
password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.login(
|
self.document_type = DocumentType.on_organization.create(
|
||||||
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
|
||||||
)
|
|
||||||
|
|
||||||
self.document_type = DocumentType.objects.create(
|
|
||||||
label=TEST_DOCUMENT_TYPE
|
label=TEST_DOCUMENT_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,6 +28,7 @@ class OCRAPITestCase(APITestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.document_type.delete()
|
self.document_type.delete()
|
||||||
|
super(OCRAPITestCase, self).tearDown()
|
||||||
|
|
||||||
def test_submit_document(self):
|
def test_submit_document(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
@@ -81,5 +69,5 @@ class OCRAPITestCase(APITestCase):
|
|||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
'Mayan EDMS Documentation' in json.loads(response.content)['content']
|
'Mayan EDMS Documentation' in response.data['content']
|
||||||
)
|
)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user