Merge branch 'feature/multi-tenant' of gitlab.com:mayan-edms/mayan-edms into feature/multi-tenant

This commit is contained in:
Roberto Rosario
2016-06-13 03:14:24 -04:00
130 changed files with 2189 additions and 1041 deletions

View File

@@ -52,10 +52,10 @@ clean-pyc:
# Testing
test:
./manage.py test $(MODULE) --settings=mayan.settings.testing --nomigrations
python -Wall ./manage.py test $(MODULE) --settings=mayan.settings.testing --nomigrations
test-all:
./manage.py runtests --settings=mayan.settings.testing --nomigrations
python -Wall ./manage.py runtests --settings=mayan.settings.testing --nomigrations
# Documentation

View File

@@ -7,7 +7,7 @@ from django.test import TestCase, override_settings
from documents.models import Document, DocumentType
from documents.permissions import permission_document_view
from documents.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
from organizations.utils import create_default_organization
from organizations.tests.base import OrganizationTestCase
from permissions.classes import Permission
from permissions.models import Role
from permissions.tests.literals import TEST_ROLE_LABEL
@@ -20,9 +20,9 @@ TEST_DOCUMENT_TYPE_2 = 'test document type 2'
@override_settings(OCR_AUTO_OCR=False)
class PermissionTestCase(TestCase):
class PermissionTestCase(OrganizationTestCase):
def setUp(self):
create_default_organization()
super(PermissionTestCase, self).setUp()
self.document_type_1 = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
@@ -62,6 +62,8 @@ class PermissionTestCase(TestCase):
for document_type in DocumentType.on_organization.all():
document_type.delete()
super(PermissionTestCase, self).tearDown()
def test_check_access_without_permissions(self):
with self.assertRaises(PermissionDenied):
AccessControlList.objects.check_access(

View File

@@ -68,10 +68,10 @@
{% endfor %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% if not user.is_authenticated %}
{% if not request.user.is_authenticated %}
{% trans 'Anonymous' %}
{% else %}
<li><a href="{% url 'common:current_user_details' %}" title="{% trans 'User details' %}">{{ request.organization }}: {{ user.get_full_name|default: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 %}
</ul>
</div>

View File

@@ -6,6 +6,7 @@ from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings
from django.test.client import Client
from organizations.tests.base import OrganizationTestCase
from user_management.tests.literals import (
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
class UserLoginTestCase(TestCase):
class UserLoginTestCase(OrganizationTestCase):
"""
Test that users can login via the supported authentication methods
"""
def setUp(self):
super(UserLoginTestCase, self).setUp()
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
self.client = Client()
def tearDown(self):
super(UserLoginTestCase, self).tearDown()
@override_settings(AUTHENTICATION_LOGIN_METHOD='username')
def test_normal_behaviour(self):
response = self.client.get(reverse('documents:document_list'))

View File

@@ -31,7 +31,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
return DocumentCheckoutSerializer
def get_queryset(self):
documents = DocumentCheckout.objects.checked_out_documents()
documents = DocumentCheckout.on_organization.checked_out_documents()
try:
Permission.check_permissions(
@@ -44,7 +44,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
else:
filtered_documents = documents
return DocumentCheckout.objects.filter(
return DocumentCheckout.on_organization.filter(
document__pk__in=filtered_documents.values_list('pk', flat=True)
)
@@ -66,7 +66,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
if serializer.is_valid():
document = get_object_or_404(
Document, pk=serializer.data['document']
Document.on_organization, pk=serializer.data['document']
)
try:
Permission.check_permissions(
@@ -80,7 +80,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
timezone = pytz.utc
try:
DocumentCheckout.objects.create(
DocumentCheckout.on_organization.create(
document=document,
expiration_datetime=timezone.localize(
serializer.data['expiration_datetime']
@@ -104,7 +104,7 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
def get_queryset(self):
if self.request.method == 'GET':
documents = DocumentCheckout.objects.checked_out_documents()
documents = DocumentCheckout.on_organization.checked_out_documents()
try:
Permission.check_permissions(
@@ -117,13 +117,13 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
else:
filtered_documents = documents
return DocumentCheckout.objects.filter(
return DocumentCheckout.on_organization.filter(
document__pk__in=filtered_documents.values_list(
'pk', flat=True
)
)
elif self.request.method == 'DELETE':
return DocumentCheckout.objects.all()
return DocumentCheckout.on_organization.all()
def get(self, request, *args, **kwargs):
"""

View File

@@ -43,23 +43,23 @@ class CheckoutsApp(MayanAppConfig):
Document.add_to_class(
'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(
'checkout_info',
lambda document: DocumentCheckout.objects.document_checkout_info(
lambda document: DocumentCheckout.on_organization.document_checkout_info(
document
)
)
Document.add_to_class(
'checkout_state',
lambda document: DocumentCheckout.objects.document_checkout_state(
lambda document: DocumentCheckout.on_organization.document_checkout_state(
document
)
)
Document.add_to_class(
'is_checked_out',
lambda document: DocumentCheckout.objects.is_document_checked_out(
lambda document: DocumentCheckout.on_organization.is_document_checked_out(
document
)
)

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
import logging
from django.apps import apps
from django.db import models
from django.utils.timezone import now
@@ -25,15 +26,15 @@ class DocumentCheckoutManager(models.Manager):
)
def checked_out_documents(self):
return Document.objects.filter(
pk__in=self.model.objects.all().values_list(
return Document.on_organization.filter(
pk__in=self.all().values_list(
'document__pk', flat=True
)
)
def expired_check_outs(self):
expired_list = Document.objects.filter(
pk__in=self.model.objects.filter(
expired_list = Document.on_organization.filter(
pk__in=self.filter(
expiration_datetime__lte=now()
).values_list('document__pk', flat=True)
)
@@ -45,14 +46,14 @@ class DocumentCheckoutManager(models.Manager):
document.check_in()
def is_document_checked_out(self, document):
if self.model.objects.filter(document=document):
if self.model.on_organization.filter(document=document):
return True
else:
return False
def check_in_document(self, document, user=None):
try:
document_checkout = self.model.objects.get(document=document)
document_checkout = self.get(document=document)
except self.model.DoesNotExist:
raise DocumentNotCheckedOut
else:
@@ -70,7 +71,7 @@ class DocumentCheckoutManager(models.Manager):
def document_checkout_info(self, document):
try:
return self.model.objects.get(document=document)
return self.get(document=document)
except self.model.DoesNotExist:
raise DocumentNotCheckedOut
@@ -87,3 +88,14 @@ class DocumentCheckoutManager(models.Manager):
return True
else:
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()
)

View File

@@ -14,7 +14,9 @@ from documents.models import Document, NewVersionBlock
from .events import event_document_check_out
from .exceptions import DocumentAlreadyCheckedOut
from .managers import DocumentCheckoutManager
from .managers import (
DocumentCheckoutManager, OrganizationDocumentCheckoutManager
)
logger = logging.getLogger(__name__)
@@ -44,6 +46,11 @@ class DocumentCheckout(models.Model):
)
objects = DocumentCheckoutManager()
on_organization = OrganizationDocumentCheckoutManager()
class Meta:
verbose_name = _('Document checkout')
verbose_name_plural = _('Document checkouts')
def __str__(self):
return unicode(self.document)
@@ -82,7 +89,3 @@ class DocumentCheckout(models.Model):
)
return result
class Meta:
verbose_name = _('Document checkout')
verbose_name_plural = _('Document checkouts')

View File

@@ -2,14 +2,13 @@ from __future__ import unicode_literals
from rest_framework import serializers
from documents.serializers import DocumentSerializer
from .models import DocumentCheckout
class DocumentCheckoutSerializer(serializers.ModelSerializer):
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)
self.fields['document'] = DocumentSerializer()

View File

@@ -29,7 +29,7 @@ def task_check_expired_check_outs():
name=lock_id, timeout=CHECKOUT_EXPIRATION_LOCK_EXPIRE
)
logger.debug('acquired lock: %s', lock_id)
DocumentCheckout.objects.check_in_expired_check_outs()
DocumentCheckout.on_organization.check_in_expired_check_outs()
lock.release()
except LockError:
logger.debug('unable to obtain lock')

View File

@@ -12,6 +12,7 @@ from documents.models import DocumentType
from documents.tests.literals import (
TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
)
from organizations.tests.base import OrganizationTestCase
from user_management.tests.literals import (
TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD
)
@@ -21,14 +22,15 @@ from ..models import DocumentCheckout
@override_settings(OCR_AUTO_OCR=False)
class DocumentCheckoutTestCase(TestCase):
class DocumentCheckoutTestCase(OrganizationTestCase):
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,
password=TEST_ADMIN_PASSWORD
)
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -39,18 +41,19 @@ class DocumentCheckoutTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(DocumentCheckoutTestCase, self).tearDown()
def test_document_checkout(self):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
self.assertTrue(self.document.is_checked_out())
self.assertTrue(
DocumentCheckout.objects.is_document_checked_out(
DocumentCheckout.on_organization.is_document_checked_out(
document=self.document
)
)
@@ -58,7 +61,7 @@ class DocumentCheckoutTestCase(TestCase):
def test_version_creation_blocking(self):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
@@ -70,7 +73,7 @@ class DocumentCheckoutTestCase(TestCase):
def test_checkin_in(self):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
@@ -79,7 +82,7 @@ class DocumentCheckoutTestCase(TestCase):
self.assertFalse(self.document.is_checked_out())
self.assertFalse(
DocumentCheckout.objects.is_document_checked_out(
DocumentCheckout.on_organization.is_document_checked_out(
document=self.document
)
)
@@ -87,13 +90,13 @@ class DocumentCheckoutTestCase(TestCase):
def test_double_checkout(self):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
with self.assertRaises(DocumentAlreadyCheckedOut):
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document,
expiration_datetime=expiration_datetime, user=self.admin_user,
block_new_version=True
@@ -106,13 +109,13 @@ class DocumentCheckoutTestCase(TestCase):
def test_auto_checkin(self):
expiration_datetime = now() + datetime.timedelta(seconds=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
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())

View 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
)

View File

@@ -27,7 +27,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.user, block_new_version=True
)
@@ -49,7 +49,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.user, block_new_version=True
)
@@ -72,7 +72,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
self.assertFalse(self.document.is_checked_out())
self.assertFalse(
DocumentCheckout.objects.is_document_checked_out(
DocumentCheckout.on_organization.is_document_checked_out(
document=self.document
)
)
@@ -134,7 +134,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)
@@ -169,7 +169,7 @@ class DocumentCheckoutViewTestCase(GenericDocumentViewTestCase):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
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):
expiration_datetime = now() + datetime.timedelta(days=1)
DocumentCheckout.objects.checkout_document(
DocumentCheckout.on_organization.checkout_document(
document=self.document, expiration_datetime=expiration_datetime,
user=self.admin_user, block_new_version=True
)

View File

@@ -30,7 +30,7 @@ class CheckoutDocumentView(SingleObjectCreateView):
form_class = DocumentCheckoutForm
def dispatch(self, request, *args, **kwargs):
self.document = get_object_or_404(Document, pk=self.kwargs['pk'])
self.document = get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
try:
Permission.check_permissions(
@@ -103,12 +103,11 @@ class CheckoutListView(DocumentListView):
}
def get_document_queryset(self):
return DocumentCheckout.objects.checked_out_documents()
return DocumentCheckout.on_organization.checked_out_documents()
class CheckoutDetailView(SingleObjectDetailView):
form_class = DocumentCheckoutDefailForm
model = Document
object_permission = permission_document_checkout_detail_view
def get_extra_context(self):
@@ -120,7 +119,7 @@ class CheckoutDetailView(SingleObjectDetailView):
}
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):
@@ -142,7 +141,7 @@ class DocumentCheckinView(ConfirmView):
return context
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):
return reverse('checkouts:checkout_info', args=(self.get_object().pk,))

View File

@@ -18,7 +18,7 @@ class Command(management.BaseCommand):
def handle(self, *args, **options):
path = os.path.join(settings.BASE_DIR, 'mayan', 'settings', 'local.py')
if os.path.exists(path):
print 'Existing file at: {0}. Backup, remove this file and try again.'.format(path)
self.stdout.write(self.style.NOTICE('Existing settings file at: {0}. Backup, remove this file, and try again.'.format(path)))
else:
with open(path, 'w+') as file_object:
file_object.write('\n'.join([

View File

@@ -1,16 +1,21 @@
from __future__ import unicode_literals
from django.core import management
from django.db.utils import OperationalError
from ...signals import post_initial_setup
class Command(management.BaseCommand):
help = 'Gets Mayan EDMS ready to be used.'
help = 'Initializes an install and gets it ready to be used.'
def handle(self, *args, **options):
management.call_command('createsettings', interactive=False)
management.call_command('migrate', interactive=False)
try:
result = management.call_command('migrate', interactive=False)
except OperationalError as exception:
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
management.call_command('createautoadmin', interactive=False)
management.call_command('createorganizationadmin', interactive=False)
post_initial_setup.send(sender=self)

View File

@@ -7,7 +7,7 @@ from django.http import HttpResponse
from django.template import Context, Template
from django.test import TestCase
from organizations.utils import create_default_organization
from organizations.tests.base import OrganizationTestCase
from permissions import Permission
from permissions.models import Role
from permissions.tests.literals import TEST_ROLE_LABEL
@@ -20,9 +20,9 @@ from user_management.tests import (
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
class GenericViewTestCase(TestCase):
class GenericViewTestCase(OrganizationTestCase):
def setUp(self):
create_default_organization()
super(GenericViewTestCase, self).setUp()
self.has_test_view = False
self.admin_user = get_user_model().on_organization.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
@@ -47,6 +47,8 @@ class GenericViewTestCase(TestCase):
if self.has_test_view:
urlpatterns.pop(0)
super(GenericViewTestCase, self).tearDown()
def add_test_view(self, test_object):
from mayan.urls import urlpatterns

View File

@@ -2,6 +2,8 @@ from __future__ import unicode_literals
from django.contrib import admin
from organizations.admin import OrganizationAdminMixin
from .models import (
Workflow, WorkflowInstance, WorkflowInstanceLogEntry, WorkflowState,
WorkflowTransition
@@ -22,7 +24,7 @@ class WorkflowTransitionInline(admin.TabularInline):
@admin.register(Workflow)
class WorkflowAdmin(admin.ModelAdmin):
class WorkflowAdmin(OrganizationAdminMixin, admin.ModelAdmin):
def document_types_list(self, instance):
return ','.join(
instance.document_types.values_list('label', flat=True)

View File

@@ -7,4 +7,4 @@ def launch_workflow(sender, instance, created, **kwargs):
Workflow = get_model('document_states', 'Workflow')
if created:
Workflow.objects.launch_for(instance)
Workflow.on_organization.launch_for(instance)

View File

@@ -1,7 +1,62 @@
from __future__ import unicode_literals
from django.apps import apps
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):
def launch_for(self, document):
for workflow in document.document_type.workflows.all():
workflow.launch_for(document)
class OrganizationWorkflowManager(WorkflowManager, CurrentOrganizationManager):
pass

View 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', '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'),
),
]

View File

@@ -9,14 +9,23 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from documents.models import Document, DocumentType
from organizations.models import Organization
from organizations.shortcuts import get_current_organization
from .managers import WorkflowManager
from .managers import (
OrganizationWorkflowManager, OrganizationWorkflowStateManager,
OrganizationWorkflowTransitionManager, OrganizationWorkflowInstanceManager,
OrganizationWorkflowInstanceLogEntryManager, WorkflowManager
)
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class Workflow(models.Model):
organization = models.ForeignKey(
Organization, default=get_current_organization
)
label = models.CharField(
max_length=255, unique=True, verbose_name=_('Label')
)
@@ -26,12 +35,19 @@ class Workflow(models.Model):
)
objects = WorkflowManager()
on_organization = OrganizationWorkflowManager()
class Meta:
verbose_name = _('Workflow')
verbose_name_plural = _('Workflows')
def __str__(self):
return self.label
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):
try:
@@ -54,10 +70,6 @@ class Workflow(models.Model):
'Workflow %s launched for document %s', self, document
)
class Meta:
verbose_name = _('Workflow')
verbose_name_plural = _('Workflows')
@python_2_unicode_compatible
class WorkflowState(models.Model):
@@ -79,6 +91,14 @@ class WorkflowState(models.Model):
), 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):
return self.label
@@ -87,11 +107,6 @@ class WorkflowState(models.Model):
self.workflow.states.all().update(initial=False)
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
class WorkflowTransition(models.Model):
@@ -109,8 +124,8 @@ class WorkflowTransition(models.Model):
verbose_name=_('Destination state')
)
def __str__(self):
return self.label
objects = models.Manager()
on_organization = OrganizationWorkflowTransitionManager()
class Meta:
unique_together = (
@@ -119,6 +134,9 @@ class WorkflowTransition(models.Model):
verbose_name = _('Workflow transition')
verbose_name_plural = _('Workflow transitions')
def __str__(self):
return self.label
@python_2_unicode_compatible
class WorkflowInstance(models.Model):
@@ -129,6 +147,14 @@ class WorkflowInstance(models.Model):
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):
return unicode(self.workflow)
@@ -168,11 +194,6 @@ class WorkflowInstance(models.Model):
def get_transition_choices(self):
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
class WorkflowInstanceLogEntry(models.Model):
@@ -189,9 +210,12 @@ class WorkflowInstanceLogEntry(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('User'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __str__(self):
return unicode(self.transition)
objects = models.Manager()
on_organization = OrganizationWorkflowInstanceLogEntryManager()
class Meta:
verbose_name = _('Workflow instance log entry')
verbose_name_plural = _('Workflow instance log entries')
def __str__(self):
return unicode(self.transition)

View File

@@ -1,6 +1,7 @@
from __future__ import unicode_literals
TEST_WORKFLOW_LABEL = 'test workflow'
TEST_WORKFLOW_LABEL_EDITED = 'test workflow edited'
TEST_WORKFLOW_INITIAL_STATE_LABEL = 'test initial state'
TEST_WORKFLOW_INITIAL_STATE_COMPLETION = 33
TEST_WORKFLOW_STATE_LABEL = 'test state'

View 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
)

View File

@@ -1,19 +1,15 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test.client import Client
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 documents.tests.test_views import GenericDocumentViewTestCase
from user_management.tests.literals import (
TEST_USER_PASSWORD, TEST_USER_USERNAME
)
from ..models import Workflow, WorkflowState, WorkflowTransition
from ..permissions import (
permission_workflow_create, permission_workflow_delete,
permission_workflow_edit
)
from .literals import (
TEST_WORKFLOW_LABEL, TEST_WORKFLOW_INITIAL_STATE_LABEL,
@@ -22,70 +18,67 @@ from .literals import (
)
class DocumentStateViewTestCase(TestCase):
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 = 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
class DocumentStateViewTestCase(GenericDocumentViewTestCase):
def create_workflow(self):
self.workflow = Workflow.on_organization.create(
label=TEST_WORKFLOW_LABEL
)
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
self.document = self.document_type.new_document(
file_object=file_object
)
def test_workflow_create_view_with_permission(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
def tearDown(self):
self.document_type.delete()
self.role.permissions.add(
permission_workflow_create.stored_permission
)
def test_creating_workflow(self):
response = self.client.post(
reverse(
'document_states:setup_workflow_create'
), data={
response = self.post(
'document_states:setup_workflow_create', data={
'label': TEST_WORKFLOW_LABEL,
}, follow=True
)
self.assertEquals(response.status_code, 200)
self.assertEquals(Workflow.objects.count(), 1)
self.assertEquals(Workflow.objects.all()[0].label, TEST_WORKFLOW_LABEL)
self.assertEquals(Workflow.on_organization.count(), 1)
self.assertEquals(
Workflow.on_organization.all()[0].label, TEST_WORKFLOW_LABEL
)
def test_delete_workflow(self):
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
def test_workflow_delete_view_with_permission(self):
self.create_workflow()
self.assertEquals(Workflow.objects.count(), 1)
self.assertEquals(Workflow.objects.all()[0].label, TEST_WORKFLOW_LABEL)
self.assertEquals(Workflow.on_organization.count(), 1)
self.assertEquals(
Workflow.on_organization.all()[0].label, TEST_WORKFLOW_LABEL
)
response = self.client.post(
reverse(
'document_states:setup_workflow_delete', args=(workflow.pk,)
), follow=True
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.role.permissions.add(
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(Workflow.objects.count(), 0)
self.assertEquals(Workflow.on_organization.count(), 0)
def test_create_workflow_state(self):
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
def test_workflow_state_create_view_with_permission(self):
self.create_workflow()
response = self.client.post(
reverse(
'document_states:setup_workflow_state_create',
args=(workflow.pk,)
), data={
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.role.permissions.add(
permission_workflow_edit.stored_permission
)
response = self.post(
'document_states:setup_workflow_state_create',
args=(self.workflow.pk,), data={
'label': TEST_WORKFLOW_STATE_LABEL,
'completion': TEST_WORKFLOW_STATE_COMPLETION,
}, follow=True
@@ -93,50 +86,61 @@ class DocumentStateViewTestCase(TestCase):
self.assertEquals(response.status_code, 200)
self.assertEquals(WorkflowState.objects.count(), 1)
self.assertEquals(WorkflowState.on_organization.count(), 1)
self.assertEquals(
WorkflowState.objects.all()[0].label, TEST_WORKFLOW_STATE_LABEL
WorkflowState.on_organization.all()[0].label,
TEST_WORKFLOW_STATE_LABEL
)
self.assertEquals(
WorkflowState.objects.all()[0].completion,
WorkflowState.on_organization.all()[0].completion,
TEST_WORKFLOW_STATE_COMPLETION
)
def test_delete_workflow_state(self):
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
workflow_state = WorkflowState.objects.create(
workflow=workflow, label=TEST_WORKFLOW_STATE_LABEL,
def test_workflow_state_delete_view_with_permission(self):
self.create_workflow()
workflow_state = WorkflowState.on_organization.create(
workflow=self.workflow, label=TEST_WORKFLOW_STATE_LABEL,
completion=TEST_WORKFLOW_STATE_COMPLETION
)
response = self.client.post(
reverse(
'document_states:setup_workflow_state_delete',
args=(workflow_state.pk,)
), follow=True
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.role.permissions.add(
permission_workflow_edit.stored_permission
)
response = self.post(
'document_states:setup_workflow_state_delete',
args=(workflow_state.pk,), follow=True
)
self.assertEquals(response.status_code, 200)
self.assertEquals(WorkflowState.objects.count(), 0)
self.assertEquals(Workflow.objects.count(), 1)
self.assertEquals(WorkflowState.on_organization.count(), 0)
self.assertEquals(Workflow.on_organization.count(), 1)
def test_create_workflow_transition(self):
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
workflow_initial_state = WorkflowState.objects.create(
workflow=workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
def test_workflow_transition_create_view_with_permission(self):
self.create_workflow()
workflow_initial_state = WorkflowState.on_organization.create(
workflow=self.workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
)
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
)
response = self.client.post(
reverse(
'document_states:setup_workflow_transition_create',
args=(workflow.pk,)
), data={
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.role.permissions.add(
permission_workflow_edit.stored_permission
)
response = self.post(
'document_states:setup_workflow_transition_create',
args=(self.workflow.pk,), data={
'label': TEST_WORKFLOW_TRANSITION_LABEL,
'origin_state': workflow_initial_state.pk,
'destination_state': workflow_state.pk,
@@ -145,47 +149,52 @@ class DocumentStateViewTestCase(TestCase):
self.assertEquals(response.status_code, 200)
self.assertEquals(WorkflowTransition.objects.count(), 1)
self.assertEquals(WorkflowTransition.on_organization.count(), 1)
self.assertEquals(
WorkflowTransition.objects.all()[0].label,
WorkflowTransition.on_organization.all()[0].label,
TEST_WORKFLOW_TRANSITION_LABEL
)
self.assertEquals(
WorkflowTransition.objects.all()[0].origin_state,
WorkflowTransition.on_organization.all()[0].origin_state,
workflow_initial_state
)
self.assertEquals(
WorkflowTransition.objects.all()[0].destination_state,
WorkflowTransition.on_organization.all()[0].destination_state,
workflow_state
)
def test_delete_workflow_transition(self):
workflow = Workflow.objects.create(label=TEST_WORKFLOW_LABEL)
workflow_initial_state = WorkflowState.objects.create(
workflow=workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
def test_workflow_transition_delete_view_with_permission(self):
self.create_workflow()
workflow_initial_state = WorkflowState.on_organization.create(
workflow=self.workflow, label=TEST_WORKFLOW_INITIAL_STATE_LABEL,
completion=TEST_WORKFLOW_INITIAL_STATE_COMPLETION, initial=True
)
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
)
workflow_transition = WorkflowTransition.objects.create(
workflow=workflow, label=TEST_WORKFLOW_TRANSITION_LABEL,
workflow_transition = WorkflowTransition.on_organization.create(
workflow=self.workflow, label=TEST_WORKFLOW_TRANSITION_LABEL,
origin_state=workflow_initial_state,
destination_state=workflow_state
)
self.assertEquals(WorkflowTransition.objects.count(), 1)
self.assertEquals(WorkflowTransition.on_organization.count(), 1)
response = self.client.post(
reverse(
'document_states:setup_workflow_transition_delete',
args=(workflow_transition.pk,)
), follow=True
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.role.permissions.add(
permission_workflow_edit.stored_permission
)
response = self.post(
'document_states:setup_workflow_transition_delete',
args=(workflow_transition.pk,), follow=True
)
self.assertEquals(response.status_code, 200)
self.assertEquals(WorkflowState.objects.count(), 2)
self.assertEquals(Workflow.objects.count(), 1)
self.assertEquals(WorkflowTransition.objects.count(), 0)
self.assertEquals(WorkflowState.on_organization.count(), 2)
self.assertEquals(Workflow.on_organization.count(), 1)
self.assertEquals(WorkflowTransition.on_organization.count(), 0)

View File

@@ -47,7 +47,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
).dispatch(request, *args, **kwargs)
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):
return {
@@ -64,7 +64,7 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
class WorkflowDocumentListView(DocumentListView):
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:
Permission.check_permissions(
@@ -80,7 +80,7 @@ class WorkflowDocumentListView(DocumentListView):
).dispatch(request, *args, **kwargs)
def get_document_queryset(self):
return Document.objects.filter(
return Document.on_organization.filter(
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')
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):
@@ -175,7 +175,7 @@ class WorkflowInstanceTransitionView(FormView):
return self.get_workflow_instance().get_absolute_url()
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
@@ -185,29 +185,37 @@ class SetupWorkflowListView(SingleObjectListView):
'title': _('Workflows'),
'hide_link': True,
}
model = Workflow
view_permission = permission_workflow_view
def get_queryset(self):
return Workflow.on_organization.all()
class SetupWorkflowCreateView(SingleObjectCreateView):
form_class = WorkflowForm
model = Workflow
view_permission = permission_workflow_create
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
def get_queryset(self):
return Workflow.on_organization.all()
class SetupWorkflowEditView(SingleObjectEditView):
form_class = WorkflowForm
model = Workflow
view_permission = permission_workflow_edit
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
def get_queryset(self):
return Workflow.on_organization.all()
class SetupWorkflowDeleteView(SingleObjectDeleteView):
model = Workflow
view_permission = permission_workflow_delete
post_action_redirect = reverse_lazy('document_states:setup_workflow_list')
def get_queryset(self):
return Workflow.on_organization.all()
class SetupWorkflowDocumentTypesView(AssignRemoveView):
decode_content_type = True
@@ -229,7 +237,7 @@ class SetupWorkflowDocumentTypesView(AssignRemoveView):
}
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):
return AssignRemoveView.generate_choices(
@@ -273,7 +281,7 @@ class SetupWorkflowStateListView(SingleObjectListView):
return self.get_workflow().states.all()
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):
@@ -289,7 +297,7 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView):
}
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):
return self.get_workflow().states.all()
@@ -350,7 +358,7 @@ class SetupWorkflowTransitionListView(SingleObjectListView):
view_permission = permission_workflow_view
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):
return self.get_workflow().transitions.all()
@@ -385,7 +393,7 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView):
return kwargs
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):
return self.get_workflow().transitions.all()

View File

@@ -3,8 +3,8 @@ from __future__ import unicode_literals
from django.contrib import admin
from .models import (
DeletedDocument, Document, DocumentPage, DocumentType,
DocumentTypeFilename, DocumentVersion, RecentDocument
Document, DocumentPage, DocumentType, DocumentTypeFilename,
DocumentVersion, RecentDocument, TrashedDocument
)
@@ -29,14 +29,6 @@ class DocumentVersionInline(admin.StackedInline):
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)
class DocumentAdmin(admin.ModelAdmin):
date_hierarchy = 'date_added'
@@ -62,3 +54,11 @@ class RecentDocumentAdmin(admin.ModelAdmin):
list_display_links = ('document', 'datetime_accessed')
list_filter = ('user',)
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')

View File

@@ -27,7 +27,7 @@ from .permissions import (
permission_document_type_edit, permission_document_type_view
)
from .serializers import (
DeletedDocumentSerializer, DocumentPageImageSerializer,
TrashedDocumentSerializer, DocumentPageImageSerializer,
DocumentPageSerializer, DocumentSerializer,
DocumentTypeSerializer, DocumentVersionSerializer,
DocumentVersionRevertSerializer, NewDocumentSerializer,
@@ -37,7 +37,7 @@ from .serializers import (
logger = logging.getLogger(__name__)
class APIDeletedDocumentListView(generics.ListAPIView):
class APITrashedDocumentListView(generics.ListAPIView):
"""
Returns a list of all the trashed documents.
"""
@@ -46,10 +46,10 @@ class APIDeletedDocumentListView(generics.ListAPIView):
mayan_object_permissions = {'GET': (permission_document_view,)}
permission_classes = (MayanPermission,)
queryset = Document.trash.all()
serializer_class = DeletedDocumentSerializer
serializer_class = TrashedDocumentSerializer
class APIDeletedDocumentView(generics.RetrieveDestroyAPIView):
class APITrashedDocumentView(generics.RetrieveDestroyAPIView):
"""
Returns the selected trashed document details.
"""
@@ -59,17 +59,17 @@ class APIDeletedDocumentView(generics.RetrieveDestroyAPIView):
}
permission_classes = (MayanPermission,)
queryset = Document.trash.all()
serializer_class = DeletedDocumentSerializer
serializer_class = TrashedDocumentSerializer
def delete(self, *args, **kwargs):
"""
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.
"""
@@ -80,7 +80,7 @@ class APIDeletedDocumentRestoreView(generics.GenericAPIView):
permission_classes = (MayanPermission,)
queryset = Document.trash.all()
serializer_class = DeletedDocumentSerializer
serializer_class = TrashedDocumentSerializer
def post(self, *args, **kwargs):
self.get_object().restore()
@@ -103,7 +103,7 @@ class APIDocumentDownloadView(generics.RetrieveAPIView):
'GET': (permission_document_download,)
}
permission_classes = (MayanPermission,)
queryset = Document.objects.all()
queryset = Document.on_organization.all()
def get_serializer_class(self):
return None
@@ -127,7 +127,7 @@ class APIDocumentListView(generics.ListCreateAPIView):
mayan_object_permissions = {'GET': (permission_document_view,)}
mayan_view_permissions = {'POST': (permission_document_create,)}
permission_classes = (MayanPermission,)
queryset = Document.objects.all()
queryset = Document.on_organization.all()
def get_serializer_class(self):
if self.request.method == 'GET':
@@ -162,7 +162,7 @@ class APIDocumentVersionDownloadView(generics.RetrieveAPIView):
'GET': (permission_document_download,)
}
permission_classes = (MayanPermission,)
queryset = DocumentVersion.objects.all()
queryset = DocumentVersion.on_organization.all()
def get_serializer_class(self):
return None
@@ -189,7 +189,7 @@ class APIDocumentView(generics.RetrieveUpdateDestroyAPIView):
'DELETE': (permission_document_trash,)
}
permission_classes = (MayanPermission,)
queryset = Document.objects.all()
queryset = Document.on_organization.all()
serializer_class = DocumentSerializer
def delete(self, *args, **kwargs):
@@ -233,7 +233,7 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
}
mayan_permission_attribute_check = 'document'
permission_classes = (MayanPermission,)
queryset = DocumentPage.objects.all()
queryset = DocumentPage.on_organization.all()
serializer_class = DocumentPageImageSerializer
@@ -249,7 +249,7 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView):
}
mayan_permission_attribute_check = 'document'
permission_classes = (MayanPermission,)
queryset = DocumentPage.objects.all()
queryset = DocumentPage.on_organization.all()
serializer_class = DocumentPageSerializer
def get(self, *args, **kwargs):
@@ -279,7 +279,7 @@ class APIDocumentTypeListView(generics.ListCreateAPIView):
mayan_object_permissions = {'GET': (permission_document_type_view,)}
mayan_view_permissions = {'POST': (permission_document_type_create,)}
permission_classes = (MayanPermission,)
queryset = DocumentType.objects.all()
queryset = DocumentType.on_organization.all()
serializer_class = DocumentTypeSerializer
def get(self, *args, **kwargs):
@@ -309,7 +309,7 @@ class APIDocumentTypeView(generics.RetrieveUpdateDestroyAPIView):
'DELETE': (permission_document_type_delete,)
}
permission_classes = (MayanPermission,)
queryset = DocumentType.objects.all()
queryset = DocumentType.on_organization.all()
serializer_class = DocumentTypeSerializer
def delete(self, *args, **kwargs):
@@ -435,7 +435,7 @@ class APIDocumentVersionView(generics.RetrieveUpdateAPIView):
}
mayan_permission_attribute_check = 'document'
permission_classes = (MayanPermission,)
queryset = DocumentVersion.objects.all()
queryset = DocumentVersion.on_organization.all()
serializer_class = DocumentVersionSerializer
def patch(self, *args, **kwargs):
@@ -463,7 +463,7 @@ class APIDocumentVersionRevertView(generics.GenericAPIView):
}
mayan_permission_attribute_check = 'document'
permission_classes = (MayanPermission,)
queryset = DocumentVersion.objects.all()
queryset = DocumentVersion.on_organization.all()
serializer_class = DocumentVersionRevertSerializer
def post(self, *args, **kwargs):

View File

@@ -36,7 +36,7 @@ from .links import (
link_clear_image_cache, link_document_clear_transformations,
link_document_delete, link_document_document_type_edit,
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_multiple_trash, link_document_multiple_clear_transformations,
link_document_multiple_download, link_document_multiple_restore,
@@ -87,19 +87,19 @@ class DocumentsApp(MayanAppConfig):
APIEndPoint(app=self, version_string='1')
DeletedDocument = self.get_model('DeletedDocument')
Document = self.get_model('Document')
DocumentPage = self.get_model('DocumentPage')
DocumentType = self.get_model('DocumentType')
DocumentTypeFilename = self.get_model('DocumentTypeFilename')
DocumentVersion = self.get_model('DocumentVersion')
TrashedDocument = self.get_model('TrashedDocument')
MissingItem(
label=_('Create a document type'),
description=_(
'Every uploaded document must be assigned a document type, '
'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'
)
@@ -172,20 +172,20 @@ class DocumentsApp(MayanAppConfig):
)
SourceColumn(
source=DeletedDocument, label=_('Thumbnail'),
source=TrashedDocument, label=_('Thumbnail'),
func=lambda context: document_thumbnail(
context['object'],
gallery_name='documents:delete_document_list',
gallery_name='documents:trashed_document_list',
size=setting_thumbnail_size.value,
title=getattr(context['object'], 'label', None),
disable_title_link=True
)
)
SourceColumn(
source=DeletedDocument, label=_('Type'), attribute='document_type'
source=TrashedDocument, label=_('Type'), attribute='document_type'
)
SourceColumn(
source=DeletedDocument, label=_('Date time trashed'),
source=TrashedDocument, label=_('Date time trashed'),
attribute='deleted_date_time'
)
@@ -268,7 +268,7 @@ class DocumentsApp(MayanAppConfig):
menu_front_page.bind_links(
links=(
link_document_list_recent, link_document_list,
link_document_list_deleted
link_document_list_trashed
)
)
menu_setup.bind_links(links=(link_document_type_setup,))
@@ -304,7 +304,7 @@ class DocumentsApp(MayanAppConfig):
menu_sidebar.bind_links(
links=(link_trash_can_empty,),
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(
links=(link_document_restore, link_document_delete),
sources=(DeletedDocument,)
sources=(TrashedDocument,)
)
# Document facet links
@@ -354,7 +354,7 @@ class DocumentsApp(MayanAppConfig):
menu_multi_item.bind_links(
links=(
link_document_multiple_restore, link_document_multiple_delete
), sources=(DeletedDocument,)
), sources=(TrashedDocument,)
)
# Document pages
@@ -430,5 +430,5 @@ class DocumentsApp(MayanAppConfig):
dispatch_uid='create_default_document_type'
)
registry.register(DeletedDocument)
registry.register(TrashedDocument)
registry.register(Document)

View File

@@ -11,7 +11,7 @@ def create_default_document_type(sender, **kwargs):
app_label='documents', model_name='DocumentType'
)
if not DocumentType.objects.count():
if not DocumentType.on_organization.count():
document_type = DocumentType.objects.create(
label=DEFAULT_DOCUMENT_TYPE_LABEL
)

View File

@@ -140,9 +140,9 @@ link_document_list_recent = Link(
icon='fa fa-clock-o', text=_('Recent documents'),
view='documents:document_list_recent'
)
link_document_list_deleted = Link(
link_document_list_trashed = Link(
icon='fa fa-trash', text=_('Trash'),
view='documents:document_list_deleted'
view='documents:document_list_trashed'
)
# Tools

View File

@@ -23,9 +23,9 @@ class DocumentManager(models.Manager):
return self.get(uuid=uuid)
def get_queryset(self):
return TrashCanQuerySet(
return TrashedDocumentQuerySet(
self.model, using=self._db
).filter(in_trash=False)
)
def invalidate_cache(self):
for document in self.model.objects.all():
@@ -50,7 +50,7 @@ class DocumentTypeManager(models.Manager):
'Document type: %s, has a deletion period delta of: %s',
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(
'Document "%s" with id: %d, trashed on: %s, exceded '
'delete period', document, document.pk,
@@ -110,8 +110,48 @@ class NewVersionBlockManager(models.Manager):
return self.filter(document=document).exists()
class PassthroughManager(models.Manager):
pass
class OrganizationDocumentManager(models.Manager):
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):
@@ -130,25 +170,37 @@ class RecentDocumentManager(models.Manager):
return new_recent
def get_for_user(self, user):
document_model = apps.get_model('documents', 'document')
Document = apps.get_model('documents', 'Document')
if user.is_authenticated():
return document_model.objects.filter(
return Document.objects.filter(
recentdocument__user=user,
document_type__organization__id=settings.ORGANIZATION_ID
).order_by('-recentdocument__datetime_accessed')
else:
return document_model.objects.none()
return Document.objects.none()
class TrashCanManager(models.Manager):
class TrashedDocumentManager(models.Manager):
def get_queryset(self):
DocumentType = apps.get_model('documents', 'DocumentType')
return super(
TrashCanManager, self
TrashedDocumentManager, self
).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):
for instance in self:
instance.delete(to_trash=to_trash)

View File

@@ -37,7 +37,10 @@ from .exceptions import NewDocumentVersionNotAllowed
from .literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
from .managers import (
DocumentManager, DocumentTypeManager, NewVersionBlockManager,
PassthroughManager, RecentDocumentManager, TrashCanManager
OrganizationDocumentManager, OrganizationDocumentVersionManager,
OrganizationTrashedDocumentManager,
OrganizationDocumentTypeFilenameManager, OrganizationDocumentPage,
RecentDocumentManager, TrashedDocumentManager
)
from .permissions import permission_document_view
from .runtime import cache_storage_backend, storage_backend
@@ -99,7 +102,7 @@ class DocumentType(models.Model):
return self.label
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)
return super(DocumentType, self).delete(*args, **kwargs)
@@ -113,8 +116,8 @@ class DocumentType(models.Model):
verbose_name_plural = _('Documents types')
@property
def deleted_documents(self):
return DeletedDocument.objects.filter(document_type=self)
def trashed_documents(self):
return TrashedDocument.objects.filter(document_type=self)
def get_document_count(self, user):
queryset = self.documents
@@ -192,9 +195,14 @@ class Document(models.Model):
), 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()
passthrough = PassthroughManager()
trash = TrashCanManager()
on_organization = OrganizationDocumentManager()
trash = TrashedDocumentManager()
def __str__(self):
return self.label or ugettext('Document stub, id: %d') % self.pk
@@ -331,8 +339,9 @@ class Document(models.Model):
return 0
class DeletedDocument(Document):
objects = TrashCanManager()
class TrashedDocument(Document):
objects = TrashedDocumentManager()
on_organization = OrganizationTrashedDocumentManager()
class Meta:
proxy = True
@@ -379,6 +388,9 @@ class DocumentVersion(models.Model):
blank=True, editable=False, null=True, verbose_name=_('Checksum')
)
objects = models.Manager()
on_organization = OrganizationDocumentVersionManager()
def __str__(self):
return '{0} - {1}'.format(self.document, self.timestamp)
@@ -634,6 +646,9 @@ class DocumentTypeFilename(models.Model):
)
enabled = models.BooleanField(default=True, verbose_name=_('Enabled'))
objects = models.Manager()
on_organization = OrganizationDocumentTypeFilenameManager()
class Meta:
ordering = ('filename',)
unique_together = ('document_type', 'filename')
@@ -658,6 +673,9 @@ class DocumentPage(models.Model):
verbose_name=_('Page number')
)
objects = models.Manager()
on_organization = OrganizationDocumentPage()
def __str__(self):
return _(
'Page %(page_num)d out of %(total_pages)d of %(document)s'

View File

@@ -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):
document_type_label = serializers.SerializerMethodField()
latest_version = DocumentVersionSerializer(many=False, read_only=True)
@@ -156,7 +130,7 @@ class NewDocumentSerializer(serializers.ModelSerializer):
file = serializers.FileField(write_only=True)
def save(self, _user):
document = Document.objects.create(
document = Document.on_organization.create(
description=self.validated_data.get('description', ''),
document_type=self.validated_data['document_type'],
label=self.validated_data.get(
@@ -191,3 +165,29 @@ class RecentDocumentSerializer(serializers.ModelSerializer):
class Meta:
fields = ('document', 'datetime_accessed')
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'
)

View File

@@ -62,7 +62,7 @@ def task_get_document_page_image(document_page_id, *args, **kwargs):
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)
@@ -72,7 +72,7 @@ def task_update_page_count(self, version_id):
app_label='documents', model_name='DocumentVersion'
)
document_version = DocumentVersion.objects.get(pk=version_id)
document_version = DocumentVersion.on_organization.get(pk=version_id)
try:
document_version.update_page_count()
except OperationalError as exception:
@@ -95,12 +95,12 @@ def task_upload_new_document(self, document_type_id, shared_uploaded_file_id, de
)
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(
pk=shared_uploaded_file_id
)
if user_id:
user = get_user_model().objects.get(pk=user_id)
user = get_user_model().on_organization.get(pk=user_id)
else:
user = None
@@ -149,12 +149,12 @@ def task_upload_new_version(self, document_id, shared_uploaded_file_id, user_id,
)
try:
document = Document.objects.get(pk=document_id)
document = Document.on_organization.get(pk=document_id)
shared_file = SharedUploadedFile.objects.get(
pk=shared_uploaded_file_id
)
if user_id:
user = get_user_model().objects.get(pk=user_id)
user = get_user_model().on_organization.get(pk=user_id)
else:
user = None

View File

@@ -21,10 +21,13 @@ __all__ = (
# Filenames
TEST_COMPRESSED_DOCUMENTS_FILENAME = 'compressed_documents.zip'
TEST_DEU_DOCUMENT_FILENAME = 'deu_website.png'
TEST_DOCUMENT_DESCRIPTION = 'test description'
TEST_DOCUMENT_FILENAME = 'mayan_11_1.pdf'
TEST_DOCUMENT_TYPE = 'test_document_type'
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_MULTI_PAGE_TIFF = 'multi_page.tiff'
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',
TEST_SMALL_DOCUMENT_FILENAME
)
TEST_TRANSFORMATION_NAME = 'rotate'
TEST_TRANSFORMATION_ARGUMENT = 'degrees: 180'

View File

@@ -4,48 +4,32 @@ from __future__ import unicode_literals
import time
from json import loads
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from django.utils.six import BytesIO
from rest_framework import status
from rest_framework.test import APITestCase
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from .literals import (
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
class DocumentTypeAPITestCase(APITestCase):
class DocumentTypeAPITestCase(GenericAPITestCase):
"""
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):
self.assertEqual(DocumentType.objects.all().count(), 0)
self.assertEqual(DocumentType.on_organization.all().count(), 0)
self.client.post(
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(
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):
document_type = DocumentType.objects.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.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
document_type = DocumentType.objects.get(pk=document_type.pk)
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')
response = self.client.put(
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):
document_type = DocumentType.objects.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.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
document_type = DocumentType.objects.get(pk=document_type.pk)
self.assertEqual(document_type.label, TEST_DOCUMENT_TYPE + 'edited')
response = self.client.patch(
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):
document_type = DocumentType.objects.create(label=TEST_DOCUMENT_TYPE)
document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
self.client.delete(
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)
class DocumentAPITestCase(APITestCase):
class DocumentAPITestCase(GenericAPITestCase):
"""
Test document 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
)
self.document_type = DocumentType.objects.create(
super(DocumentAPITestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
def tearDown(self):
self.admin_user.delete()
self.document_type.delete()
super(DocumentAPITestCase, self).tearDown()
def test_document_upload(self):
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(
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'])
@@ -158,41 +145,9 @@ class DocumentAPITestCase(APITestCase):
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)
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):
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
document = self.document_type.new_document(
@@ -293,5 +248,34 @@ class DocumentAPITestCase(APITestCase):
del(buf)
# TODO: def test_document_set_document_type(self):
# pass
def test_trashed_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.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)

View File

@@ -49,7 +49,7 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase):
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
)
acl.permissions.add(
@@ -81,7 +81,7 @@ class DocumentsLinksTestCase(GenericDocumentViewTestCase):
def test_document_version_download_link_with_permission(self):
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
)
acl.permissions.add(permission_document_download.stored_permission)

View File

@@ -5,9 +5,11 @@ import time
from django.test import TestCase, override_settings
from organizations.tests.base import OrganizationTestCase
from ..exceptions import NewDocumentVersionNotAllowed
from ..literals import STUB_EXPIRATION_INTERVAL
from ..models import DeletedDocument, Document, DocumentType, NewVersionBlock
from ..models import Document, DocumentType, NewVersionBlock, TrashedDocument
from .literals import (
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_PATH, TEST_MULTI_PAGE_TIFF_PATH,
@@ -16,9 +18,10 @@ from .literals import (
@override_settings(OCR_AUTO_OCR=False)
class DocumentTestCase(TestCase):
class DocumentTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(DocumentTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -29,6 +32,7 @@ class DocumentTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(DocumentTestCase, self).tearDown()
def test_document_creation(self):
self.assertEqual(self.document_type.label, TEST_DOCUMENT_TYPE)
@@ -57,30 +61,30 @@ class DocumentTestCase(TestCase):
self.assertEqual(self.document.versions.count(), 3)
def test_restoring_documents(self):
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 1)
# Trash the document
self.document.delete()
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
# Restore the document
self.document.restore()
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 1)
def test_trashing_documents(self):
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 1)
# Trash the document
self.document.delete()
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
# Delete the document
self.document.delete()
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
def test_auto_trashing(self):
"""
@@ -97,13 +101,13 @@ class DocumentTestCase(TestCase):
# field
time.sleep(2)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
DocumentType.objects.check_trash_periods()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
def test_auto_delete(self):
"""
@@ -116,13 +120,13 @@ class DocumentTestCase(TestCase):
self.document_type.delete_time_unit = 'seconds'
self.document_type.save()
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.document.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
# Needed by MySQL as milliseconds value is not store in timestamp
# field
@@ -130,14 +134,16 @@ class DocumentTestCase(TestCase):
DocumentType.objects.check_delete_periods()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
@override_settings(OCR_AUTO_OCR=False)
class OfficeDocumentTestCase(TestCase):
class OfficeDocumentTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(OfficeDocumentTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -148,6 +154,7 @@ class OfficeDocumentTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(OfficeDocumentTestCase, self).tearDown()
def test_document_creation(self):
self.assertEqual(self.document.file_mimetype, 'application/msword')
@@ -162,9 +169,11 @@ class OfficeDocumentTestCase(TestCase):
@override_settings(OCR_AUTO_OCR=False)
class MultiPageTiffTestCase(TestCase):
class MultiPageTiffTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(MultiPageTiffTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -175,6 +184,7 @@ class MultiPageTiffTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(MultiPageTiffTestCase, self).tearDown()
def test_document_creation(self):
self.assertEqual(self.document.file_mimetype, 'image/tiff')
@@ -187,9 +197,11 @@ class MultiPageTiffTestCase(TestCase):
@override_settings(OCR_AUTO_OCR=False)
class DocumentVersionTestCase(TestCase):
class DocumentVersionTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(DocumentVersionTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -200,6 +212,7 @@ class DocumentVersionTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(DocumentVersionTestCase, self).tearDown()
def test_add_new_version(self):
self.assertEqual(self.document.versions.count(), 1)
@@ -236,23 +249,25 @@ class DocumentVersionTestCase(TestCase):
@override_settings(OCR_AUTO_OCR=False)
class DocumentManagerTestCase(TestCase):
class DocumentManagerTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(DocumentManagerTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
def tearDown(self):
self.document_type.delete()
super(DocumentManagerTestCase, self).tearDown()
def test_document_stubs_deletion(self):
document_stub = Document.objects.create(
document_stub = Document.on_organization.create(
document_type=self.document_type
)
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(
seconds=STUB_EXPIRATION_INTERVAL + 1
@@ -261,13 +276,14 @@ class DocumentManagerTestCase(TestCase):
Document.objects.delete_stubs()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
@override_settings(OCR_AUTO_OCR=False)
class NewVersionBlockTestCase(TestCase):
class NewVersionBlockTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(NewVersionBlockTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -279,6 +295,7 @@ class NewVersionBlockTestCase(TestCase):
def tearDown(self):
self.document.delete()
self.document_type.delete()
super(NewVersionBlockTestCase, self).tearDown()
def test_blocking(self):
NewVersionBlock.objects.block(document=self.document)

View 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)

View File

@@ -15,7 +15,7 @@ from user_management.tests.literals import (
from ..literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT
from ..models import (
DeletedDocument, Document, DocumentType, HASH_FUNCTION
Document, DocumentType, TrashedDocument, HASH_FUNCTION
)
from ..permissions import (
permission_document_create, permission_document_delete,
@@ -28,22 +28,18 @@ from ..permissions import (
)
from .literals import (
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_QUICK_LABEL,
TEST_SMALL_DOCUMENT_CHECKSUM, TEST_SMALL_DOCUMENT_PATH
TEST_DOCUMENT_TYPE, TEST_DOCUMENT_TYPE_EDITED_LABEL,
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)
class GenericDocumentViewTestCase(GenericViewTestCase):
def setUp(self):
super(GenericDocumentViewTestCase, self).setUp()
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -115,7 +111,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.document.document_type, self.document_type
)
document_type = DocumentType.objects.create(
document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
@@ -128,7 +124,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 403)
self.assertEqual(
Document.objects.get(pk=self.document.pk).document_type,
Document.on_organization.get(pk=self.document.pk).document_type,
self.document_type
)
@@ -141,7 +137,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.document.document_type, self.document_type
)
document_type = DocumentType.objects.create(
document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE_2_LABEL
)
@@ -160,7 +156,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.assertContains(response, text='success', status_code=200)
self.assertEqual(
Document.objects.get(pk=self.document.pk).document_type,
Document.on_organization.get(pk=self.document.pk).document_type,
document_type
)
@@ -170,10 +166,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
)
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
)
@@ -188,7 +184,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 302)
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):
@@ -197,10 +193,10 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
)
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
)
@@ -222,7 +218,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(
Document.objects.first().document_type, document_type
Document.on_organization.first().document_type, document_type
)
def test_document_download_user_view(self):
@@ -230,7 +226,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 1)
response = self.post(
'documents:document_download', args=(self.document.pk,)
@@ -262,7 +258,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 1)
response = self.post(
'documents:document_multiple_download',
@@ -296,7 +292,7 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 1)
response = self.post(
'documents:document_version_download', args=(
@@ -486,20 +482,20 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
def test_trash_can_empty_view_no_permissions(self):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
self.document.delete()
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
response = self.post('documents:trash_can_empty')
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):
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.document.delete()
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.role.permissions.add(
permission_empty_trash.stored_permission
@@ -509,8 +505,8 @@ class DocumentsViewsTestCase(GenericDocumentViewTestCase):
self.assertContains(
response, text='emptied successfully', status_code=200
)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
def test_document_version_revert_no_permission(self):
first_version = self.document.latest_version
@@ -560,7 +556,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.document_type.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
response = self.post(
'documents:document_type_create',
@@ -573,7 +569,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
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):
self.login(
@@ -582,7 +578,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.document_type.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
self.role.permissions.add(
permission_document_type_create.stored_permission
@@ -602,9 +598,9 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.assertContains(response, text='successfully', status_code=200)
self.assertEqual(DocumentType.objects.count(), 1)
self.assertEqual(DocumentType.on_organization.count(), 1)
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):
@@ -618,7 +614,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
)
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):
self.login(
@@ -638,7 +634,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
)
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):
self.login(
@@ -658,7 +654,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.assertEqual(response.status_code, 403)
self.assertEqual(
DocumentType.objects.get(pk=self.document_type.pk).label,
DocumentType.on_organization.get(pk=self.document_type.pk).label,
TEST_DOCUMENT_TYPE
)
@@ -687,7 +683,7 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.assertContains(response, 'successfully', status_code=200)
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
)
@@ -728,27 +724,27 @@ class DocumentTypeViewsTestCase(GenericDocumentViewTestCase):
self.assertEqual(self.document_type.filenames.count(), 1)
class DeletedDocumentTestCase(GenericDocumentViewTestCase):
class TrashedDocumentTestCase(GenericDocumentViewTestCase):
def test_document_restore_view_no_permission(self):
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.document.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
response = self.post(
'documents:document_restore', args=(self.document.pk,)
)
self.assertEqual(response.status_code, 403)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
def test_document_restore_view_with_permission(self):
self.login(
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD
)
self.document.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
self.role.permissions.add(
permission_document_restore.stored_permission
)
@@ -757,8 +753,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
follow=True
)
self.assertContains(response, text='restored', status_code=200)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 1)
def test_document_trash_no_permissions(self):
self.login(
@@ -770,8 +766,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
)
self.assertEqual(response.status_code, 403)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 1)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 1)
def test_document_trash_with_permissions(self):
self.login(
@@ -788,8 +784,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
)
self.assertContains(response, text='success', status_code=200)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
def test_document_delete_no_permissions(self):
self.login(
@@ -797,15 +793,15 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
)
self.document.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
response = self.post(
'documents:document_delete', args=(self.document.pk,),
)
self.assertEqual(response.status_code, 403)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
def test_document_delete_with_permissions(self):
self.login(
@@ -813,8 +809,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
)
self.document.delete()
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.on_organization.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 1)
self.role.permissions.add(
permission_document_delete.stored_permission
@@ -826,8 +822,8 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
)
self.assertContains(response, text='success', status_code=200)
self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 0)
self.assertEqual(TrashedDocument.on_organization.count(), 0)
self.assertEqual(Document.on_organization.count(), 0)
def test_deleted_document_list_view_no_permissions(self):
self.document.delete()
@@ -836,7 +832,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
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)
@@ -850,6 +846,6 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
self.role.permissions.add(
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)

View File

@@ -3,8 +3,8 @@ from __future__ import unicode_literals
from django.conf.urls import patterns, url
from .api_views import (
APIDeletedDocumentListView, APIDeletedDocumentRestoreView,
APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentView,
APITrashedDocumentListView, APITrashedDocumentRestoreView,
APITrashedDocumentView, APIDocumentDownloadView, APIDocumentView,
APIDocumentListView, APIDocumentVersionDownloadView,
APIDocumentPageImageView, APIDocumentPageView,
APIDocumentTypeDocumentListView, APIDocumentTypeListView,
@@ -14,17 +14,16 @@ from .api_views import (
)
from .settings import setting_print_size, setting_display_size
from .views import (
ClearImageCacheView, DeletedDocumentDeleteView,
DeletedDocumentDeleteManyView, DeletedDocumentListView, DocumentEditView,
DocumentListView, DocumentPageView, DocumentPageListView,
DocumentPageViewResetView, DocumentPreviewView, DocumentRestoreView,
DocumentRestoreManyView, DocumentTrashView, DocumentTrashManyView,
DocumentTypeCreateView, DocumentTypeDeleteView,
ClearImageCacheView, DocumentEditView, DocumentListView, DocumentPageView,
DocumentPageListView, DocumentPageViewResetView, DocumentPreviewView,
DocumentRestoreView, DocumentRestoreManyView, DocumentTrashView,
DocumentTrashManyView, DocumentTypeCreateView, DocumentTypeDeleteView,
DocumentTypeDocumentListView, DocumentTypeFilenameCreateView,
DocumentTypeFilenameDeleteView, DocumentTypeFilenameEditView,
DocumentTypeFilenameListView, DocumentTypeListView, DocumentTypeEditView,
DocumentVersionListView, DocumentVersionRevertView, DocumentView,
EmptyTrashCanView, RecentDocumentListView
EmptyTrashCanView, RecentDocumentListView, TrashedDocumentDeleteView,
TrashedDocumentDeleteManyView, TrashedDocumentListView
)
urlpatterns = patterns(
@@ -35,8 +34,8 @@ urlpatterns = patterns(
name='document_list_recent'
),
url(
r'^list/deleted/$', DeletedDocumentListView.as_view(),
name='document_list_deleted'
r'^list/deleted/$', TrashedDocumentListView.as_view(),
name='document_list_trashed'
),
url(
@@ -56,11 +55,11 @@ urlpatterns = patterns(
name='document_multiple_restore'
),
url(
r'^(?P<pk>\d+)/delete/$', DeletedDocumentDeleteView.as_view(),
r'^(?P<pk>\d+)/delete/$', TrashedDocumentDeleteView.as_view(),
name='document_delete'
),
url(
r'^multiple/delete/$', DeletedDocumentDeleteManyView.as_view(),
r'^multiple/delete/$', TrashedDocumentDeleteManyView.as_view(),
name='document_multiple_delete'
),
url(
@@ -242,18 +241,6 @@ urlpatterns = 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/recent/$', APIRecentDocumentListView.as_view(),
@@ -304,4 +291,16 @@ api_urls = patterns(
r'^document_types/$', APIDocumentTypeListView.as_view(),
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'
),
)

View File

@@ -38,8 +38,8 @@ from .forms import (
)
from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT, PAGE_RANGE_RANGE
from .models import (
DeletedDocument, Document, DocumentType, DocumentPage,
DocumentTypeFilename, DocumentVersion, RecentDocument
Document, DocumentType, DocumentPage, DocumentTypeFilename,
DocumentVersion, RecentDocument, TrashedDocument
)
from .permissions import (
permission_document_delete, permission_document_download,
@@ -81,84 +81,19 @@ class DocumentListView(SingleObjectListView):
'hide_links': True,
'title': _('All documents'),
}
object_permission = permission_document_view
def get_document_queryset(self):
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID).defer('description', 'uuid', 'date_added', 'language', 'in_trash', 'deleted_date_time').all()
return Document.on_organization.defer(
'description', 'uuid', 'date_added', 'language', 'in_trash',
'deleted_date_time'
).all()
def get_queryset(self):
self.queryset = self.get_document_queryset().filter(is_stub=False)
return super(DocumentListView, self).get_queryset()
class DeletedDocumentListView(DocumentListView):
object_permission = None
extra_context = {
'hide_link': True,
'title': _('Documents in trash'),
}
def get_document_queryset(self):
queryset = Document.trash.filter(document_type__organization__id=settings.ORGANIZATION_ID)
try:
Permission.check_permissions(
self.request.user, (permission_document_view,)
)
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, self.request.user, queryset
)
return DeletedDocument.objects.filter(
pk__in=queryset.values_list('pk', flat=True)
)
class DeletedDocumentDeleteView(ConfirmView):
extra_context = {
'title': _('Delete the selected document?')
}
def object_action(self, instance):
source_document = get_object_or_404(
Document.passthrough.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=instance.pk
)
try:
Permission.check_permissions(
self.request.user, (permission_document_delete,)
)
except PermissionDenied:
AccessControlList.objects.check_access(
permission_document_delete, self.request.user, source_document
)
instance.delete()
def view_action(self):
instance = get_object_or_404(DeletedDocument, pk=self.kwargs['pk'])
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s deleted.') % {
'document': instance
}
)
class DeletedDocumentDeleteManyView(MultipleInstanceActionMixin, DeletedDocumentDeleteView):
extra_context = {
'title': _('Delete the selected documents?')
}
success_message = '%(count)d document deleted.'
success_message_plural = '%(count)d documents deleted.'
def get_queryset(self):
return DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
class DocumentEditView(SingleObjectEditView):
form_class = DocumentForm
object_permission = permission_document_properties_edit
@@ -176,14 +111,14 @@ class DocumentEditView(SingleObjectEditView):
'title': _('Edit properties of document: %s') % self.get_object(),
}
def get_queryset(self):
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
def get_post_action_redirect(self):
return reverse(
'documents:document_properties', args=(self.get_object().pk,)
)
def get_queryset(self):
return Document.on_organization.all()
def get_save_extra_data(self):
return {
'_user': self.request.user
@@ -197,7 +132,7 @@ class DocumentRestoreView(ConfirmView):
def object_action(self, instance):
source_document = get_object_or_404(
Document.passthrough.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=instance.pk
Document, pk=instance.pk
)
try:
@@ -212,7 +147,7 @@ class DocumentRestoreView(ConfirmView):
instance.restore()
def view_action(self):
instance = get_object_or_404(DeletedDocument, pk=self.kwargs['pk'])
instance = get_object_or_404(TrashedDocument, pk=self.kwargs['pk'])
self.object_action(instance=instance)
@@ -231,7 +166,7 @@ class DocumentRestoreManyView(MultipleInstanceActionMixin, DocumentRestoreView):
success_message_plural = '%(count)d documents restored.'
def get_queryset(self):
return DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return TrashedDocument.on_organization.all()
class DocumentPageListView(SingleObjectListView):
@@ -251,7 +186,9 @@ class DocumentPageListView(SingleObjectListView):
).dispatch(request, *args, **kwargs)
def get_document(self):
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(
Document.on_organization, pk=self.kwargs['pk']
)
def get_queryset(self):
return self.get_document().pages.all()
@@ -306,7 +243,9 @@ class DocumentPageView(SimpleView):
}
def get_object(self):
return get_object_or_404(DocumentPage.objects.filter(document_version__document__document_type__organization__pk=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(
DocumentPage.on_organization, pk=self.kwargs['pk']
)
class DocumentPageViewResetView(RedirectView):
@@ -336,7 +275,7 @@ class DocumentPreviewView(SingleObjectDetailView):
}
def get_queryset(self):
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return Document.on_organization.all()
class DocumentTrashView(ConfirmView):
@@ -347,7 +286,7 @@ class DocumentTrashView(ConfirmView):
}
def get_object(self):
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
def get_post_action_redirect(self):
return reverse('documents:document_list_recent')
@@ -386,12 +325,14 @@ class DocumentTrashManyView(MultipleInstanceActionMixin, DocumentTrashView):
}
def get_queryset(self):
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return Document.on_organization.all()
class DocumentTypeDocumentListView(DocumentListView):
def get_document_type(self):
return get_object_or_404(DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(
DocumentType.on_organization, pk=self.kwargs['pk']
)
def get_document_queryset(self):
return self.get_document_type().documents.all()
@@ -414,7 +355,7 @@ class DocumentTypeListView(SingleObjectListView):
}
def get_queryset(self):
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
return DocumentType.on_organization.all()
class DocumentTypeCreateView(SingleObjectCreateView):
@@ -431,7 +372,7 @@ class DocumentTypeCreateView(SingleObjectCreateView):
}
def get_queryset(self):
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
return DocumentType.on_organization.all()
class DocumentTypeDeleteView(SingleObjectDeleteView):
@@ -446,7 +387,7 @@ class DocumentTypeDeleteView(SingleObjectDeleteView):
}
def get_queryset(self):
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
return DocumentType.on_organization.all()
class DocumentTypeEditView(SingleObjectEditView):
@@ -464,7 +405,7 @@ class DocumentTypeEditView(SingleObjectEditView):
}
def get_queryset(self):
return DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID)
return DocumentType.on_organization.all()
class DocumentTypeFilenameCreateView(SingleObjectCreateView):
@@ -486,7 +427,9 @@ class DocumentTypeFilenameCreateView(SingleObjectCreateView):
)
def get_document_type(self):
return get_object_or_404(DocumentType.objects.filter(organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(
DocumentType.on_organization, pk=self.kwargs['pk']
)
def get_extra_context(self):
return {
@@ -528,7 +471,7 @@ class DocumentTypeFilenameEditView(SingleObjectEditView):
)
def get_queryset(self):
return DocumentTypeFilename.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return DocumentTypeFilename.on_organization.all()
class DocumentTypeFilenameDeleteView(SingleObjectDeleteView):
@@ -555,15 +498,16 @@ class DocumentTypeFilenameDeleteView(SingleObjectDeleteView):
)
def get_queryset(self):
return DocumentTypeFilename.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return DocumentTypeFilename.on_organization.all()
class DocumentTypeFilenameListView(SingleObjectListView):
model = DocumentType
view_permission = permission_document_type_view
def get_document_type(self):
return get_object_or_404(DocumentType, pk=self.kwargs['pk'])
return get_object_or_404(
DocumentType.on_organization, pk=self.kwargs['pk']
)
def get_extra_context(self):
return {
@@ -597,7 +541,9 @@ class DocumentVersionListView(SingleObjectListView):
).dispatch(request, *args, **kwargs)
def get_document(self):
return get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk'])
return get_object_or_404(
Document.on_organization, pk=self.kwargs['pk']
)
def get_extra_context(self):
return {
@@ -623,7 +569,9 @@ class DocumentVersionRevertView(ConfirmView):
}
def get_object(self):
return get_object_or_404(DocumentVersion, pk=self.kwargs['pk'])
return get_object_or_404(
DocumentVersion.on_organization, pk=self.kwargs['pk']
)
def view_action(self):
try:
@@ -655,20 +603,20 @@ class DocumentView(SingleObjectDetailView):
}
def get_queryset(self):
return Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
return Document.on_organization.all()
class EmptyTrashCanView(ConfirmView):
action_cancel_redirect = post_action_redirect = reverse_lazy(
'documents:document_list_trashed'
)
extra_context = {
'title': _('Empty trash?')
}
view_permission = permission_empty_trash
action_cancel_redirect = post_action_redirect = reverse_lazy(
'documents:document_list_deleted'
)
def view_action(self):
for deleted_document in DeletedDocument.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID):
for deleted_document in TrashedDocument.on_organization.all():
deleted_document.delete()
messages.success(self.request, _('Trash emptied successfully'))
@@ -684,10 +632,81 @@ class RecentDocumentListView(DocumentListView):
return RecentDocument.objects.get_for_user(self.request.user)
class TrashedDocumentListView(DocumentListView):
extra_context = {
'hide_link': True,
'title': _('Documents in trash'),
}
object_permission = None
def get_document_queryset(self):
queryset = Document.trash.all()
try:
Permission.check_permissions(
self.request.user, (permission_document_view,)
)
except PermissionDenied:
queryset = AccessControlList.objects.filter_by_access(
permission_document_view, self.request.user, queryset
)
return TrashedDocument.on_organization.filter(
pk__in=queryset.values_list('pk', flat=True)
)
class TrashedDocumentDeleteView(ConfirmView):
extra_context = {
'title': _('Delete the selected document?')
}
def object_action(self, instance):
# Special case, we need to check the original document for the delete
# permission. Don't use .on_organization manager as it filters
# trashed documents and we don't want that.
source_document = get_object_or_404(
Document, pk=instance.pk
)
try:
Permission.check_permissions(
self.request.user, (permission_document_delete,)
)
except PermissionDenied:
AccessControlList.objects.check_access(
permission_document_delete, self.request.user, source_document
)
instance.delete()
def view_action(self):
instance = get_object_or_404(
TrashedDocument.on_organization, pk=self.kwargs['pk']
)
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s deleted.') % {
'document': instance
}
)
class TrashedDocumentDeleteManyView(MultipleInstanceActionMixin, TrashedDocumentDeleteView):
extra_context = {
'title': _('Delete the selected documents?')
}
success_message = '%(count)d document deleted.'
success_message_plural = '%(count)d documents deleted.'
def get_queryset(self):
return TrashedDocument.on_organization.all()
def document_document_type_edit(request, document_id=None, document_id_list=None):
post_action_redirect = None
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
queryset = Document.on_organization.all()
if document_id:
queryset = queryset.filter(pk=document_id)
@@ -768,7 +787,12 @@ def document_multiple_document_type_edit(request):
# TODO: Get rid of this view and convert widget to use API and base64 only images
def get_document_image(request, document_id, size=setting_preview_size.value):
document = get_object_or_404(Document.passthrough.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=document_id)
# Special case. Allow all active and trashed documents but filter by
# organization, to be able to produce thumbnails for all documents.
document = get_object_or_404(
Document.objects.filter(document_type__in=DocumentType.on_organization.all()),
pk=document_id
)
try:
Permission.check_permissions(request.user, (permission_document_view,))
except PermissionDenied:
@@ -800,7 +824,7 @@ def get_document_image(request, document_id, size=setting_preview_size.value):
def document_download(request, document_id=None, document_id_list=None, document_version_pk=None):
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))))
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
queryset = Document.on_organization.all()
if document_id:
documents = queryset.filter(pk=document_id)
@@ -832,11 +856,12 @@ def document_download(request, document_id=None, document_id_list=None, document
)
)
# TODO: check organization
if document_version_pk:
queryset = DocumentVersion.objects.filter(pk=document_version_pk)
queryset = DocumentVersion.on_organization.filter(
pk=document_version_pk
)
else:
queryset = DocumentVersion.objects.filter(
queryset = DocumentVersion.on_organization.filter(
pk__in=[document.latest_version.pk for document in documents]
)
@@ -951,12 +976,12 @@ def document_multiple_download(request):
def document_update_page_count(request, document_id=None, document_id_list=None):
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
queryset = Document.on_organization.all()
if document_id:
documents = queryset.filter(pk=document_id)
elif document_id_list:
documents = queryset.objects.filter(pk__in=document_id_list)
documents = queryset.filter(pk__in=document_id_list)
if not documents:
messages.error(request, _('At least one document must be selected.'))
@@ -1016,7 +1041,7 @@ def document_multiple_update_page_count(request):
def document_clear_transformations(request, document_id=None, document_id_list=None):
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
queryset = Document.on_organization.all()
if document_id:
documents = queryset.filter(pk=document_id)
@@ -1098,7 +1123,7 @@ def document_multiple_clear_transformations(request):
def document_page_navigation_next(request, document_page_id):
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
document_page = get_object_or_404(DocumentPage.on_organization, pk=document_page_id)
try:
Permission.check_permissions(request.user, (permission_document_view,))
@@ -1116,7 +1141,7 @@ def document_page_navigation_next(request, document_page_id):
def document_page_navigation_previous(request, document_page_id):
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
document_page = get_object_or_404(DocumentPage.on_organization, pk=document_page_id)
try:
Permission.check_permissions(request.user, (permission_document_view,))
@@ -1134,7 +1159,7 @@ def document_page_navigation_previous(request, document_page_id):
def document_page_navigation_first(request, document_page_id):
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
document_page = get_object_or_404(DocumentPage.on_organization, pk=document_page_id)
document_page = get_object_or_404(document_page.siblings, page_number=1)
try:
@@ -1148,7 +1173,7 @@ def document_page_navigation_first(request, document_page_id):
def document_page_navigation_last(request, document_page_id):
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
document_page = get_object_or_404(DocumentPage.on_organization, pk=document_page_id)
document_page = get_object_or_404(document_page.siblings, page_number=document_page.siblings.count())
try:
@@ -1162,7 +1187,7 @@ def document_page_navigation_last(request, document_page_id):
def transform_page(request, document_page_id, zoom_function=None, rotation_function=None):
document_page = get_object_or_404(DocumentPage.objects.filter(document__document_type__organization__id=settings.ORGANIZATION_ID), pk=document_page_id)
document_page = get_object_or_404(DocumentPage.on_organization, pk=document_page_id)
try:
Permission.check_permissions(request.user, (permission_document_view,))
@@ -1225,7 +1250,7 @@ def document_page_rotate_left(request, document_page_id):
def document_print(request, document_id):
document = get_object_or_404(Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=document_id)
document = get_object_or_404(Document.on_organization, pk=document_id)
try:
Permission.check_permissions(request.user, (permission_document_print,))

View File

@@ -2,10 +2,13 @@ from __future__ import unicode_literals
from django.contrib import admin
from organizations.admin import OrganizationAdminMixin
from .models import Folder
@admin.register(Folder)
class FolderAdmin(admin.ModelAdmin):
class FolderAdmin(OrganizationAdminMixin, admin.ModelAdmin):
filter_horizontal = ('documents',)
list_display = ('label', 'datetime_created')
list_display_links = ('label',)

View File

@@ -55,7 +55,7 @@ class APIFolderListView(generics.ListCreateAPIView):
mayan_object_permissions = {'GET': (permission_folder_view,)}
mayan_view_permissions = {'POST': (permission_folder_create,)}
permission_classes = (MayanPermission,)
queryset = Folder.objects.all()
queryset = Folder.on_organization.all()
def get_serializer_class(self):
if self.request.method == 'GET':
@@ -85,7 +85,7 @@ class APIFolderView(generics.RetrieveUpdateDestroyAPIView):
'DELETE': (permission_folder_delete,)
}
permission_classes = (MayanPermission,)
queryset = Folder.objects.all()
queryset = Folder.on_organization.all()
serializer_class = FolderSerializer
def delete(self, *args, **kwargs):

View File

@@ -1,10 +1,6 @@
from __future__ import absolute_import
from django.contrib.auth import get_user_model
from django.db import models
class FolderManager(models.Manager):
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, user=user)
return self.get(label=label)

View File

@@ -60,7 +60,9 @@ class NewFolderDocumentSerializer(serializers.Serializer):
def create(self, validated_data):
try:
document = Document.objects.get(pk=validated_data['document'])
document = Document.on_organization.get(
pk=validated_data['document']
)
validated_data['folder'].documents.add(document)
except Exception as exception:
raise ValidationError(exception)

View File

@@ -4,10 +4,9 @@ from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from rest_framework.test import APITestCase
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
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
class FolderAPITestCase(APITestCase):
class FolderAPITestCase(GenericAPITestCase):
"""
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):
response = self.client.post(
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['label'], TEST_FOLDER_LABEL)
self.assertEqual(Folder.objects.count(), 1)
self.assertEqual(Folder.on_organization.count(), 1)
self.assertEqual(folder.label, TEST_FOLDER_LABEL)
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(
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):
folder = Folder.objects.create(label=TEST_FOLDER_LABEL)
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
self.client.put(
reverse('rest_api:folder-detail', args=(folder.pk,)),
{'label': TEST_FOLDER_EDITED_LABEL}
)
folder = Folder.objects.first()
folder = Folder.on_organization.first()
self.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
@override_settings(OCR_AUTO_OCR=False)
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
)
@@ -88,9 +77,9 @@ class FolderAPITestCase(APITestCase):
@override_settings(OCR_AUTO_OCR=False)
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
)

View File

@@ -32,7 +32,7 @@ class FolderTestCase(TestCase):
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)
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):
folder = Folder.on_organization.create(label=TEST_FOLDER_LABEL)

View File

@@ -26,7 +26,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
)
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):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
@@ -41,11 +41,13 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}, follow=True
)
self.assertContains(response, text='created', status_code=200)
self.assertEqual(Folder.objects.count(), 1)
self.assertEqual(Folder.objects.first().label, TEST_FOLDER_LABEL)
self.assertEqual(Folder.on_organization.count(), 1)
self.assertEqual(
Folder.on_organization.first().label, TEST_FOLDER_LABEL
)
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)
@@ -60,17 +62,17 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
)
self.assertContains(response, text='exists', status_code=200)
self.assertEqual(Folder.objects.count(), 1)
self.assertEqual(Folder.objects.first().pk, folder.pk)
self.assertEqual(Folder.on_organization.count(), 1)
self.assertEqual(Folder.on_organization.first().pk, folder.pk)
def test_folder_delete_view_no_permission(self):
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,))
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):
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
@@ -79,19 +81,19 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_delete', args=(folder.pk,), follow=True
)
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):
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_edit', args=(folder.pk,), data={
@@ -99,7 +101,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}
)
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)
def test_folder_edit_view_with_permission(self):
@@ -109,7 +111,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_edit', args=(folder.pk,), data={
@@ -117,7 +119,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}, 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.assertEqual(folder.label, TEST_FOLDER_EDITED_LABEL)
@@ -126,7 +128,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_add_document', args=(self.document.pk,), data={
@@ -150,7 +152,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_add_document', args=(self.document.pk,), data={
@@ -158,7 +160,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}, 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.assertEqual(folder.documents.count(), 1)
self.assertQuerysetEqual(
@@ -170,7 +172,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_add_multiple_documents', data={
@@ -190,7 +192,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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(
'folders:folder_add_multiple_documents', data={
@@ -198,7 +200,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}, 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.assertEqual(folder.documents.count(), 1)
self.assertQuerysetEqual(
@@ -208,7 +210,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
def test_folder_remove_document_view_no_permission(self):
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)
@@ -223,7 +225,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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)
def test_folder_remove_document_view_with_permission(self):
@@ -233,7 +235,7 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
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)
self.assertEqual(folder.documents.count(), 1)
@@ -245,6 +247,6 @@ class FolderViewTestCase(GenericDocumentViewTestCase):
}, 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.assertEqual(folder.documents.count(), 0)

View File

@@ -39,7 +39,7 @@ class FolderCreateView(SingleObjectCreateView):
def form_valid(self, form):
try:
Folder.objects.get(label=form.cleaned_data['label'])
Folder.on_organization.get(label=form.cleaned_data['label'])
except Folder.DoesNotExist:
instance = form.save(commit=False)
instance.user = self.request.user
@@ -128,7 +128,7 @@ class FolderListView(SingleObjectListView):
class DocumentFolderListView(FolderListView):
def dispatch(self, request, *args, **kwargs):
self.document = get_object_or_404(
Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID), pk=self.kwargs['pk']
Document.on_organization, pk=self.kwargs['pk']
)
try:
@@ -154,7 +154,7 @@ class DocumentFolderListView(FolderListView):
def folder_add_document(request, document_id=None, document_id_list=None):
queryset = Document.objects.filter(document_type__organization__id=settings.ORGANIZATION_ID)
queryset = Document.on_organization.all()
if document_id:
queryset = queryset.filter(pk=document_id)
@@ -243,9 +243,9 @@ def folder_document_remove(request, folder_id, document_id=None, document_id_lis
folder = get_object_or_404(Folder.on_organization, pk=folder_id)
if document_id:
queryset = Document.objects.filter(pk=document_id)
queryset = Document.on_organization.filter(pk=document_id)
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:
messages.error(request, _('Must provide at least one folder document.'))

View File

@@ -2,11 +2,13 @@ from __future__ import unicode_literals
from django.contrib import admin
from organizations.admin import OrganizationAdminMixin
from .models import MetadataType
@admin.register(MetadataType)
class MetadataTypeAdmin(admin.ModelAdmin):
class MetadataTypeAdmin(OrganizationAdminMixin, admin.ModelAdmin):
list_display = (
'name', 'label', 'default', 'lookup', 'validation', 'parser'
)

View File

@@ -49,7 +49,7 @@ def save_metadata(metadata_dict, document, create=False):
"""
if create:
# 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,
metadata_type=get_object_or_404(
MetadataType,
@@ -57,7 +57,7 @@ def save_metadata(metadata_dict, document, create=False):
)
else:
try:
document_metadata = DocumentMetadata.objects.get(
document_metadata = DocumentMetadata.on_organization.get(
document=document,
metadata_type=get_object_or_404(
MetadataType,
@@ -92,7 +92,7 @@ def metadata_repr_as_list(metadata_list):
output = []
for metadata_dict in metadata_list:
try:
output.append('%s - %s' % (MetadataType.objects.get(
output.append('%s - %s' % (MetadataType.on_organization.get(
pk=metadata_dict['id']), metadata_dict.get('value', '')))
except:
pass
@@ -107,9 +107,9 @@ def set_bulk_metadata(document, metadata_dictionary):
]
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:
DocumentMetadata.objects.get_or_create(
DocumentMetadata.on_organization.get_or_create(
document=document, metadata_type=metadata_type, value=value
)

View File

@@ -31,7 +31,7 @@ from .serializers import (
class APIMetadataTypeListView(generics.ListCreateAPIView):
serializer_class = MetadataTypeSerializer
queryset = MetadataType.objects.all()
queryset = MetadataType.on_organization.all()
permission_classes = (MayanPermission,)
filter_backends = (MayanObjectPermissionsFilter,)
@@ -53,7 +53,7 @@ class APIMetadataTypeListView(generics.ListCreateAPIView):
class APIMetadataTypeView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = MetadataTypeSerializer
queryset = MetadataType.objects.all()
queryset = MetadataType.on_organization.all()
permission_classes = (MayanPermission,)
mayan_object_permissions = {
@@ -92,7 +92,7 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
permission_classes = (MayanPermission,)
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):
document = self.get_document()
@@ -151,7 +151,7 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = DocumentMetadataSerializer
queryset = DocumentMetadata.objects.all()
queryset = DocumentMetadata.on_organization.all()
permission_classes = (MayanPermission,)
mayan_object_permissions = {
@@ -220,7 +220,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
def get_queryset(self):
document_type = get_object_or_404(
DocumentType, pk=self.kwargs['document_type_pk']
DocumentType.on_organization, pk=self.kwargs['document_type_pk']
)
try:
Permission.check_permissions(
@@ -253,7 +253,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
Add an optional metadata type to a document type.
"""
document_type = get_object_or_404(
DocumentType, pk=self.kwargs['document_type_pk']
DocumentType.on_organization, pk=self.kwargs['document_type_pk']
)
try:
@@ -270,7 +270,7 @@ class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView):
if serializer.is_valid():
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(
metadata_type=metadata_type, required=self.required_metadata
@@ -313,7 +313,7 @@ class APIDocumentTypeMetadataTypeView(views.APIView):
"""
document_type_metadata_type = get_object_or_404(
DocumentTypeMetadataType, pk=self.kwargs['pk']
DocumentTypeMetadataType.on_organization, pk=self.kwargs['pk']
)
try:

View File

@@ -109,13 +109,13 @@ MetadataFormSet = formset_factory(MetadataForm, extra=0)
class AddMetadataForm(forms.Form):
metadata_type = forms.ModelChoiceField(
queryset=MetadataType.objects.all(), label=_('Metadata type')
queryset=MetadataType.on_organization.all(), label=_('Metadata type')
)
def __init__(self, *args, **kwargs):
document_type = kwargs.pop('document_type')
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(
'metadata_type', flat=True
)

View File

@@ -44,7 +44,7 @@ def post_post_document_type_change_metadata(sender, instance, **kwargs):
# Add new document type metadata types to document
for document_type_metadata_type in instance.document_type.metadata.filter(required=True):
DocumentMetadata.objects.create(
DocumentMetadata.on_organization.create(
document=instance,
metadata_type=document_type_metadata_type.metadata_type,
value=None

View File

@@ -1,6 +1,35 @@
from __future__ import unicode_literals
from django.apps import apps
from django.db import models
class MetadataTypeManager(models.Manager):
def get_by_natural_key(self, 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()
)

View 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', '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'),
),
]

View File

@@ -10,9 +10,15 @@ from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
from documents.models import Document, DocumentType
from organizations.models import Organization
from organizations.managers import CurrentOrganizationManager
from organizations.shortcuts import get_current_organization
from .classes import MetadataLookup
from .managers import MetadataTypeManager
from .managers import (
MetadataTypeManager, OrganizationDocumentMetadataManager,
OrganizationDocumentTypeMetadataTypeManager
)
from .settings import setting_available_parsers, setting_available_validators
@@ -35,7 +41,9 @@ class MetadataType(models.Model):
"""
Define a type of metadata
"""
organization = models.ForeignKey(
Organization, default=get_current_organization
)
name = models.CharField(
max_length=48,
help_text=_(
@@ -79,6 +87,12 @@ class MetadataType(models.Model):
)
objects = MetadataTypeManager()
on_organization = CurrentOrganizationManager()
class Meta:
ordering = ('label',)
verbose_name = _('Metadata type')
verbose_name_plural = _('Metadata types')
def __str__(self):
return self.label
@@ -86,11 +100,6 @@ class MetadataType(models.Model):
def natural_key(self):
return (self.name,)
class Meta:
ordering = ('label',)
verbose_name = _('Metadata type')
verbose_name_plural = _('Metadata types')
@staticmethod
def comma_splitter(string):
splitter = shlex.shlex(string.encode('utf-8'), posix=True)
@@ -158,6 +167,14 @@ class DocumentMetadata(models.Model):
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):
return unicode(self.metadata_type)
@@ -184,11 +201,6 @@ class DocumentMetadata(models.Model):
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
def is_required(self):
return self.metadata_type.get_required_for(
@@ -207,10 +219,13 @@ class DocumentTypeMetadataType(models.Model):
)
required = models.BooleanField(default=False, verbose_name=_('Required'))
def __str__(self):
return unicode(self.metadata_type)
objects = models.Manager()
on_organization = OrganizationDocumentTypeMetadataTypeManager()
class Meta:
unique_together = ('document_type', 'metadata_type')
verbose_name = _('Document type metadata type options')
verbose_name_plural = _('Document type metadata types options')
def __str__(self):
return unicode(self.metadata_type)

View File

@@ -47,7 +47,7 @@ class DocumentNewMetadataSerializer(serializers.Serializer):
)
def create(self, validated_data):
metadata_type = MetadataType.objects.get(
metadata_type = MetadataType.on_organization.get(
pk=validated_data['metadata_type_pk']
)
instance = self.document.metadata.create(

View File

@@ -15,7 +15,7 @@ def task_remove_metadata_type(document_type_id, metadata_type_id):
app_label='metadata', model_name='DocumentMetadata'
)
DocumentMetadata.objects.filter(
DocumentMetadata.on_organization.filter(
document__document_type__id=document_type_id,
metadata_type__id=metadata_type_id
).delete()
@@ -31,7 +31,7 @@ def task_add_required_metadata_type(document_type_id, metadata_type_id):
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)

View File

@@ -4,10 +4,9 @@ from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import override_settings
from rest_framework.test import APITestCase
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from rest_api.tests import GenericAPITestCase
from user_management.tests.literals import (
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'
class MetadataTypeAPITestCase(APITestCase):
class MetadataTypeAPITestCase(GenericAPITestCase):
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
)
super(MetadataTypeAPITestCase, self).setUp()
def tearDown(self):
self.admin_user.delete()
super(MetadataTypeAPITestCase, self).tearDown()
def test_metadata_type_create(self):
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.data['id'], metadata_type.pk)
@@ -56,7 +48,7 @@ class MetadataTypeAPITestCase(APITestCase):
self.assertEqual(metadata_type.name, TEST_METADATA_TYPE_NAME)
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
)
@@ -66,10 +58,10 @@ class MetadataTypeAPITestCase(APITestCase):
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):
metadata_type = MetadataType.objects.create(
metadata_type = MetadataType.on_organization.create(
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)
class DocumentTypeMetadataTypeAPITestCase(APITestCase):
class DocumentTypeMetadataTypeAPITestCase(GenericAPITestCase):
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
super(DocumentTypeMetadataTypeAPITestCase, self).setUp()
self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
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
)
def tearDown(self):
self.admin_user.delete()
self.document_type.delete()
super(DocumentTypeMetadataTypeAPITestCase, self).tearDown()
def test_document_type_metadata_type_optional_create(self):
response = self.client.post(
@@ -122,7 +107,7 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
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
).first()
@@ -142,7 +127,7 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
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
).first()
@@ -169,23 +154,16 @@ class DocumentTypeMetadataTypeAPITestCase(APITestCase):
self.assertEqual(self.document_type.metadata.all().count(), 0)
class DocumentMetadataAPITestCase(APITestCase):
class DocumentMetadataAPITestCase(GenericAPITestCase):
@override_settings(OCR_AUTO_OCR=False)
def setUp(self):
self.admin_user = get_user_model().objects.create_superuser(
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
password=TEST_ADMIN_PASSWORD
)
super(DocumentMetadataAPITestCase, self).setUp()
self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
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
)
@@ -199,8 +177,8 @@ class DocumentMetadataAPITestCase(APITestCase):
)
def tearDown(self):
self.admin_user.delete()
self.document_type.delete()
super(DocumentMetadataAPITestCase, self).tearDown()
def test_document_metadata_create(self):
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
)

View File

@@ -2,10 +2,11 @@ from __future__ import unicode_literals
from django.core.files.base import File
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.tests import TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_TYPE
from organizations.tests import OrganizationTestCase
from ..models import MetadataType, DocumentMetadata
@@ -18,13 +19,14 @@ from .literals import (
@override_settings(OCR_AUTO_OCR=False)
class MetadataTestCase(TestCase):
class MetadataTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(MetadataTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
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
)
@@ -37,6 +39,7 @@ class MetadataTestCase(TestCase):
def tearDown(self):
self.document_type.delete()
super(MetadataTestCase, self).tearDown()
def test_no_default(self):
document_metadata = DocumentMetadata(

View 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
)

View File

@@ -30,7 +30,7 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase):
def setUp(self):
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
)
@@ -94,11 +94,11 @@ class DocumentMetadataTestCase(GenericDocumentViewTestCase):
permission_metadata_document_edit.stored_permission
)
document_type_2 = DocumentType.objects.create(
document_type_2 = DocumentType.on_organization.create(
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
)

View File

@@ -36,11 +36,11 @@ from .permissions import (
def metadata_edit(request, document_id=None, document_id_list=None):
if document_id:
documents = Document.objects.filter(pk=document_id)
documents = Document.on_organization.filter(pk=document_id)
if not documents:
raise Document.DoesNotExist
elif document_id_list:
documents = Document.objects.filter(pk__in=document_id_list)
documents = Document.on_organization.filter(pk__in=document_id_list)
try:
Permission.check_permissions(
@@ -186,11 +186,11 @@ def metadata_multiple_edit(request):
def metadata_add(request, document_id=None, document_id_list=None):
if document_id:
documents = Document.objects.filter(pk=document_id)
documents = Document.on_organization.filter(pk=document_id)
if not documents:
raise Document.DoesNotExist
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:
messages.error(
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']
for document in documents:
try:
document_metadata, created = DocumentMetadata.objects.get_or_create(
document_metadata, created = DocumentMetadata.on_organization.get_or_create(
document=document,
metadata_type=metadata_type,
defaults={'value': ''}
@@ -331,11 +331,11 @@ def metadata_multiple_add(request):
def metadata_remove(request, document_id=None, document_id_list=None):
if document_id:
documents = Document.objects.filter(pk=document_id)
documents = Document.on_organization.filter(pk=document_id)
if not documents:
raise Document.DoesNotExist
elif document_id_list:
documents = Document.objects.filter(pk__in=document_id_list)
documents = Document.on_organization.filter(pk__in=document_id_list)
try:
Permission.check_permissions(
@@ -416,10 +416,10 @@ def metadata_remove(request, document_id=None, document_id_list=None):
for form in formset.forms:
if form.cleaned_data['update']:
metadata_type = get_object_or_404(
MetadataType, pk=form.cleaned_data['id']
MetadataType.on_organization, pk=form.cleaned_data['id']
)
try:
document_metadata = DocumentMetadata.objects.get(
document_metadata = DocumentMetadata.on_organization.get(
document=document, metadata_type=metadata_type
)
document_metadata.delete()
@@ -494,7 +494,7 @@ class DocumentMetadataListView(SingleObjectListView):
)
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):
document = self.get_document()
@@ -512,13 +512,14 @@ class DocumentMetadataListView(SingleObjectListView):
class MetadataTypeCreateView(SingleObjectCreateView):
extra_context = {'title': _('Create metadata type')}
form_class = MetadataTypeForm
model = MetadataType
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
view_permission = permission_metadata_type_create
def get_queryset(self):
return MetadataType.on_organization.all()
class MetadataTypeDeleteView(SingleObjectDeleteView):
model = MetadataType
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
view_permission = permission_metadata_type_delete
@@ -529,10 +530,12 @@ class MetadataTypeDeleteView(SingleObjectDeleteView):
'title': _('Delete the metadata type: %s?') % self.get_object(),
}
def get_queryset(self):
return MetadataType.on_organization.all()
class MetadataTypeEditView(SingleObjectEditView):
form_class = MetadataTypeForm
model = MetadataType
post_action_redirect = reverse_lazy('metadata:setup_metadata_type_list')
view_permission = permission_metadata_type_edit
@@ -542,13 +545,13 @@ class MetadataTypeEditView(SingleObjectEditView):
'title': _('Edit metadata type: %s') % self.get_object(),
}
def get_queryset(self):
return MetadataType.on_organization.all()
class MetadataTypeListView(SingleObjectListView):
view_permission = permission_metadata_type_view
def get_queryset(self):
return MetadataType.objects.all()
def get_extra_context(self):
return {
'extra_columns': (
@@ -561,6 +564,9 @@ class MetadataTypeListView(SingleObjectListView):
'title': _('Metadata types'),
}
def get_queryset(self):
return MetadataType.on_organization.all()
class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
decode_content_type = True
@@ -572,12 +578,12 @@ class SetupDocumentTypeMetadataOptionalView(AssignRemoveView):
self.get_object().metadata.create(metadata_type=item, required=False)
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):
return AssignRemoveView.generate_choices(
set(MetadataType.objects.all()) - set(
MetadataType.objects.filter(
set(MetadataType.on_organization.all()) - set(
MetadataType.on_organization.filter(
id__in=self.get_object().metadata.values_list(
'metadata_type', flat=True
)

View File

@@ -1,7 +1,10 @@
from django.conf import settings
from django.db import models
from django.db.models import Q
from django.utils import timezone
from organizations.managers import CurrentOrganizationManager
class MessageManager(models.Manager):
def get_for_now(self):
@@ -9,3 +12,10 @@ class MessageManager(models.Manager):
return self.filter(enabled=True).filter(
Q(start_datetime__isnull=True) | Q(start_datetime__lte=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}
)

View File

@@ -13,12 +13,36 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='MessageOfTheDay',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=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)),
(
'id', models.AutoField(
verbose_name='ID', serialize=False, auto_created=True,
primary_key=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={
'verbose_name': 'Message of the day',

View File

@@ -14,11 +14,15 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='messageoftheday',
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(
model_name='messageoftheday',
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
),
),
]

View File

@@ -14,21 +14,35 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='message',
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(
model_name='message',
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(
model_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(
model_name='message',
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
),
),
]

View 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'
),
),
]

View File

@@ -4,11 +4,17 @@ from django.db import models
from django.utils.encoding import python_2_unicode_compatible
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
class Message(models.Model):
organization = models.ForeignKey(
Organization, default=get_current_organization
)
label = models.CharField(
max_length=32, help_text=_('Short description of this message.'),
verbose_name=_('Label')
@@ -30,6 +36,7 @@ class Message(models.Model):
)
objects = MessageManager()
on_organization = OrganizationMessageManager()
class Meta:
verbose_name = _('Message')

View File

@@ -9,4 +9,4 @@ register = Library()
@register.inclusion_tag('motd/messages.html')
def motd():
return {'messages': Message.objects.get_for_now()}
return {'messages': Message.on_organization.get_for_now()}

View 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'

View File

@@ -5,20 +5,27 @@ from datetime import timedelta
from django.test import TestCase
from django.utils import timezone
from organizations.models import Organization
from organizations.utils import create_default_organization
from ..models import Message
TEST_LABEL = 'test label'
TEST_MESSAGE = 'test message'
from .literals import TEST_MESSAGE_LABEL, TEST_MESSAGE_TEXT
class MOTDTestCase(TestCase):
def setUp(self):
self.motd = Message.objects.create(
label=TEST_LABEL, message=TEST_MESSAGE
create_default_organization()
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):
queryset = Message.objects.get_for_now()
queryset = Message.on_organization.get_for_now()
self.assertEqual(queryset.exists(), True)
@@ -26,7 +33,7 @@ class MOTDTestCase(TestCase):
self.motd.start_datetime = timezone.now() - timedelta(days=1)
self.motd.save()
queryset = Message.objects.get_for_now()
queryset = Message.on_organization.get_for_now()
self.assertEqual(queryset.first(), self.motd)
@@ -35,7 +42,7 @@ class MOTDTestCase(TestCase):
self.motd.end_datetime = timezone.now() - timedelta(days=1)
self.motd.save()
queryset = Message.objects.get_for_now()
queryset = Message.on_organization.get_for_now()
self.assertEqual(queryset.exists(), False)
@@ -43,6 +50,6 @@ class MOTDTestCase(TestCase):
self.motd.enabled = False
self.motd.save()
queryset = Message.objects.get_for_now()
queryset = Message.on_organization.get_for_now()
self.assertEqual(queryset.exists(), False)

View 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
)

View File

@@ -10,7 +10,9 @@ urlpatterns = patterns(
'',
url(r'^list/$', MessageListView.as_view(), name='message_list'),
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(
r'^(?P<pk>\d+)/delete/$', MessageDeleteView.as_view(),
name='message_delete'

View File

@@ -21,7 +21,6 @@ logger = logging.getLogger(__name__)
class MessageCreateView(SingleObjectCreateView):
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
model = Message
view_permission = permission_message_create
def get_extra_context(self):
@@ -29,9 +28,11 @@ class MessageCreateView(SingleObjectCreateView):
'title': _('Create message'),
}
def get_queryset(self):
return Message.on_organization.all()
class MessageDeleteView(SingleObjectDeleteView):
model = Message
object_permission = permission_message_delete
post_action_redirect = reverse_lazy('motd:message_list')
@@ -42,10 +43,12 @@ class MessageDeleteView(SingleObjectDeleteView):
'title': _('Delete the message: %s?') % self.get_object(),
}
def get_queryset(self):
return Message.on_organization.all()
class MessageEditView(SingleObjectEditView):
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
model = Message
object_permission = permission_message_edit
post_action_redirect = reverse_lazy('motd:message_list')
@@ -55,9 +58,11 @@ class MessageEditView(SingleObjectEditView):
'title': _('Edit message: %s') % self.get_object(),
}
def get_queryset(self):
return Message.on_organization.all()
class MessageListView(SingleObjectListView):
model = Message
object_permission = permission_message_view
def get_extra_context(self):
@@ -65,3 +70,6 @@ class MessageListView(SingleObjectListView):
'hide_link': True,
'title': _('Messages'),
}
def get_queryset(self):
return Message.on_organization.all()

View File

@@ -16,7 +16,7 @@ class APIDocumentOCRView(generics.GenericAPIView):
'POST': (permission_ocr_document,)
}
permission_classes = (MayanPermission,)
queryset = Document.objects.all()
queryset = Document.on_organization.all()
def get_serializer_class(self):
return None
@@ -44,7 +44,7 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
'POST': (permission_ocr_document,)
}
permission_classes = (MayanPermission,)
queryset = DocumentVersion.objects.all()
queryset = DocumentVersion.on_organization.all()
def get_serializer_class(self):
return None
@@ -83,7 +83,7 @@ class APIDocumentPageContentView(generics.RetrieveAPIView):
}
permission_classes = (MayanPermission,)
serializer_class = DocumentPageContentSerializer
queryset = DocumentPage.objects.all()
queryset = DocumentPage.on_organization.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()

View File

@@ -60,7 +60,7 @@ class OCRBackendBase(object):
image = document_page.get_image()
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_content.content = self.execute(

View File

@@ -57,5 +57,5 @@ class DocumentContentForm(forms.Form):
class DocumentTypeSelectForm(forms.Form):
document_type = forms.ModelChoiceField(
queryset=DocumentType.objects.all(), label=('Document type')
queryset=DocumentType.on_organization.all(), label=('Document type')
)

View File

@@ -20,6 +20,6 @@ def initialize_new_ocr_settings(sender, instance, **kwargs):
DocumentTypeSettings = get_model('ocr', 'DocumentTypeSettings')
if kwargs['created']:
DocumentTypeSettings.objects.create(
DocumentTypeSettings.on_organization.create(
document_type=instance, auto_ocr=setting_auto_ocr.value
)

View 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(),
)

View File

@@ -6,6 +6,12 @@ from django.utils.translation import ugettext_lazy as _
from documents.models import DocumentPage, DocumentType, DocumentVersion
from .managers import (
OrganizationDocumentTypeSettingsManager,
OrganizationDocumentVersionOCRErrorManager,
OrganizationDocumentPageContentManager
)
class DocumentTypeSettings(models.Model):
"""
@@ -20,6 +26,9 @@ class DocumentTypeSettings(models.Model):
verbose_name=_('Automatically queue newly created documents for OCR.')
)
objects = models.Manager()
on_organization = OrganizationDocumentTypeSettingsManager()
class Meta:
verbose_name = _('Document type 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'))
def __str__(self):
return unicode(self.document_version)
objects = models.Manager()
on_organization = OrganizationDocumentVersionOCRErrorManager()
class Meta:
ordering = ('datetime_submitted',)
verbose_name = _('Document Version OCR Error')
verbose_name_plural = _('Document Version OCR Errors')
def __str__(self):
return unicode(self.document_version)
@python_2_unicode_compatible
class DocumentPageContent(models.Model):
@@ -55,9 +67,12 @@ class DocumentPageContent(models.Model):
)
content = models.TextField(blank=True, verbose_name=_('Content'))
def __str__(self):
return unicode(self.document_page)
objects = models.Manager()
on_organization = OrganizationDocumentPageContentManager()
class Meta:
verbose_name = _('Document page content')
verbose_name_plural = _('Document pages contents')
def __str__(self):
return unicode(self.document_page)

View File

@@ -92,7 +92,7 @@ class Parser(object):
file_object = document_page.document_version.get_intermidiate_file()
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_content.content = self.execute(

View File

@@ -35,7 +35,9 @@ def task_do_ocr(self, document_version_pk):
logger.debug('acquired lock: %s', lock_id)
document_version = None
try:
document_version = DocumentVersion.objects.get(pk=document_version_pk)
document_version = DocumentVersion.on_organization.get(
pk=document_version_pk
)
logger.info(
'Starting document OCR for document version: %s',
document_version
@@ -53,7 +55,7 @@ def task_do_ocr(self, document_version_pk):
exception
)
if document_version:
entry, created = DocumentVersionOCRError.objects.get_or_create(
entry, created = DocumentVersionOCRError.on_organization.get_or_create(
document_version=document_version
)
@@ -72,7 +74,7 @@ def task_do_ocr(self, document_version_pk):
'OCR complete for document version: %s', document_version
)
try:
entry = DocumentVersionOCRError.objects.get(
entry = DocumentVersionOCRError.on_organization.get(
document_version=document_version
)
except DocumentVersionOCRError.DoesNotExist:

View File

@@ -1,36 +1,23 @@
from __future__ import unicode_literals
import json
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from documents.models import DocumentType
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
from user_management.tests import (
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
)
from rest_api.tests import GenericAPITestCase
class OCRAPITestCase(APITestCase):
class OCRAPITestCase(GenericAPITestCase):
"""
Test the OCR app 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
)
super(OCRAPITestCase, self).setUp()
self.client.login(
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
)
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -41,6 +28,7 @@ class OCRAPITestCase(APITestCase):
def tearDown(self):
self.document_type.delete()
super(OCRAPITestCase, self).tearDown()
def test_submit_document(self):
response = self.client.post(
@@ -81,5 +69,5 @@ class OCRAPITestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(
'Mayan EDMS Documentation' in json.loads(response.content)['content']
'Mayan EDMS Documentation' in response.data['content']
)

View File

@@ -2,30 +2,31 @@
from __future__ import unicode_literals
from django.core.files.base import File
from django.test import TestCase
from documents.models import DocumentType
from documents.settings import setting_language_choices
from documents.tests import (
TEST_DEU_DOCUMENT_PATH, TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
)
from organizations.tests import OrganizationTestCase
class DocumentOCRTestCase(TestCase):
class DocumentOCRTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(DocumentOCRTestCase, self).setUp()
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(file_object),
file_object=file_object,
)
def tearDown(self):
self.document.delete()
self.document_type.delete()
super(DocumentOCRTestCase, self).tearDown()
def test_ocr_language_backends_end(self):
content = self.document.pages.first().ocr_content.content
@@ -33,9 +34,11 @@ class DocumentOCRTestCase(TestCase):
self.assertTrue('Mayan EDMS Documentation' in content)
class GermanOCRSupportTestCase(TestCase):
class GermanOCRSupportTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(GermanOCRSupportTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
@@ -49,11 +52,12 @@ class GermanOCRSupportTestCase(TestCase):
with open(TEST_DEU_DOCUMENT_PATH) as file_object:
self.document = self.document_type.new_document(
file_object=File(file_object), language=language_code
file_object=file_object, language=language_code
)
def tearDown(self):
self.document_type.delete()
super(GermanOCRSupportTestCase, self).tearDown()
def test_ocr_language_backends_end(self):
content = self.document.pages.first().ocr_content.content

View File

@@ -0,0 +1,95 @@
from __future__ import unicode_literals
from django.test import override_settings
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 DocumentPageContent
@override_settings(OCR_AUTO_OCR=False)
class OrganizationOCRViewTestCase(OrganizationViewTestCase):
def create_document_type(self):
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
def create_document(self):
self.create_document_type()
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
self.document = self.document_type.new_document(
file_object=file_object
)
def test_document_content_view(self):
self.create_document()
self.document.submit_for_ocr()
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.get(
'ocr:document_content', args=(self.document.pk,)
)
self.assertContains(response, text='Mayan', status_code=200)
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.get(
'ocr:document_content', args=(self.document.pk,)
)
self.assertEqual(response.status_code, 404)
def test_document_submit_view(self):
self.create_document()
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.post(
'ocr:document_submit', args=(self.document.pk,), follow=True
)
self.assertContains(response, text='uccess', status_code=200)
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.post(
'ocr:document_submit', args=(self.document.pk,), follow=True
)
self.assertEqual(response.status_code, 404)
def test_document_submit_all_view(self):
self.create_document()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
self.post('ocr:document_submit_all', follow=True)
with self.assertRaises(DocumentPageContent.DoesNotExist):
# Use .objects manager to make sure we get all document pages
# and that it indeed doesn't exists = no OCR happened.
DocumentPageContent.objects.get(
document_page=self.document.pages.first()
)
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
self.post('ocr:document_submit_all', follow=True)
self.assertIn(
'Mayan', self.document.pages.first().ocr_content.content
)
def test_document_type_ocr_settings_view(self):
self.create_document_type()
with self.settings(ORGANIZATION_ID=self.organization_a.pk):
response = self.get(
'ocr:document_type_ocr_settings', args=(self.document_type.pk,)
)
self.assertEqual(response.status_code, 200)
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
response = self.get(
'ocr:document_type_ocr_settings', args=(self.document_type.pk,)
)
self.assertEqual(response.status_code, 404)

View File

@@ -1,32 +1,34 @@
from __future__ import unicode_literals
from django.core.files.base import File
from django.test import TestCase, override_settings
from django.test import override_settings
from documents.models import DocumentType
from documents.tests import (
TEST_DOCUMENT_PATH, TEST_DOCUMENT_TYPE, TEST_HYBRID_DOCUMENT_PATH
)
from organizations.tests import OrganizationTestCase
from ..classes import TextExtractor
from ..parsers import PDFMinerParser, PopplerParser
@override_settings(OCR_AUTO_OCR=False)
class ParserTestCase(TestCase):
class ParserTestCase(OrganizationTestCase):
def setUp(self):
super(ParserTestCase, self).setUp()
self.document_type = DocumentType.objects.create(
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
with open(TEST_DOCUMENT_PATH) as file_object:
self.document = self.document_type.new_document(
file_object=File(file_object)
file_object=file_object
)
def tearDown(self):
self.document_type.delete()
super(ParserTestCase, self).tearDown()
def test_pdfminer_parser(self):
parser = PDFMinerParser()
@@ -48,19 +50,22 @@ class ParserTestCase(TestCase):
@override_settings(OCR_AUTO_OCR=False)
class TextExtractorTestCase(TestCase):
class TextExtractorTestCase(OrganizationTestCase):
def setUp(self):
self.document_type = DocumentType.objects.create(
super(TextExtractorTestCase, self).setUp()
self.document_type = DocumentType.on_organization.create(
label=TEST_DOCUMENT_TYPE
)
with open(TEST_HYBRID_DOCUMENT_PATH) as file_object:
self.document = self.document_type.new_document(
file_object=File(file_object)
file_object=file_object
)
def tearDown(self):
self.document_type.delete()
super(TextExtractorTestCase, self).tearDown()
def test_text_extractor(self):
TextExtractor.process_document_version(

View File

@@ -32,7 +32,7 @@ class DocumentAllSubmitView(ConfirmView):
def view_action(self):
count = 0
for document in Document.objects.all():
for document in Document.on_organization.all():
document.submit_for_ocr()
count += 1
@@ -49,7 +49,7 @@ class DocumentSubmitView(ConfirmView):
}
def get_object(self):
return Document.objects.get(pk=self.kwargs['pk'])
return get_object_or_404(Document.on_organization, pk=self.kwargs['pk'])
def object_action(self, instance):
try:
@@ -77,7 +77,6 @@ class DocumentSubmitView(ConfirmView):
class DocumentSubmitManyView(MultipleInstanceActionMixin, DocumentSubmitView):
model = Document
success_message = '%(count)d document submitted to the OCR queue.'
success_message_plural = '%(count)d documents submitted to the OCR queue.'
@@ -87,6 +86,9 @@ class DocumentSubmitManyView(MultipleInstanceActionMixin, DocumentSubmitView):
'title': _('Submit the selected documents to the OCR queue?')
}
def get_queryset(self):
return Document.on_organization.all()
class DocumentTypeSubmitView(FormView):
form_class = DocumentTypeSelectForm
@@ -99,7 +101,6 @@ class DocumentTypeSubmitView(FormView):
def form_valid(self, form):
count = 0
print form.cleaned_data
for document in form.cleaned_data['document_type'].documents.all():
document.submit_for_ocr()
count += 1
@@ -123,7 +124,7 @@ class DocumentTypeSettingsEditView(SingleObjectEditView):
def get_object(self, queryset=None):
return get_object_or_404(
DocumentType, pk=self.kwargs['pk']
DocumentType.on_organization, pk=self.kwargs['pk']
).ocr_settings
def get_extra_context(self):
@@ -136,7 +137,6 @@ class DocumentTypeSettingsEditView(SingleObjectEditView):
class DocumentOCRContent(SingleObjectDetailView):
form_class = DocumentContentForm
model = Document
object_permission = permission_ocr_content_view
def dispatch(self, request, *args, **kwargs):
@@ -154,6 +154,9 @@ class DocumentOCRContent(SingleObjectDetailView):
'title': _('OCR result for document: %s') % self.get_object(),
}
def get_queryset(self):
return Document.on_organization.all()
class EntryListView(SingleObjectListView):
extra_context = {
@@ -163,4 +166,4 @@ class EntryListView(SingleObjectListView):
view_permission = permission_ocr_document
def get_queryset(self):
return DocumentVersionOCRError.objects.all()
return DocumentVersionOCRError.on_organization.all()

View File

@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from .models import Organization
@@ -9,3 +10,27 @@ from .models import Organization
class OrganizationAdmin(admin.ModelAdmin):
list_display = ('label',)
search_fields = ('label',)
class OrganizationAdminMixin(object):
def __init__(self, *args, **kwargs):
super(OrganizationAdminMixin, self).__init__(*args, **kwargs)
self.list_display = ('organization',) + self.list_display
self.list_filter = self.list_filter + ('organization',)
self.ordering = self.ordering or ()
self.ordering = ('organization',) + self.ordering
def get_fieldsets(self, *args, **kwargs):
result = super(OrganizationAdminMixin, self).get_fieldsets(*args, **kwargs)
if 'organization' in result[0][1]['fields']:
try:
result[0][1]['fields'] = result[0][1]['fields'] - ('organization',)
except TypeError:
result[0][1]['fields'].remove('organization')
result = ((_('Organizations'), {
'fields': ('organization',),
},),) + tuple(result)
return result

View File

@@ -1,4 +1,4 @@
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
import logging
import string
@@ -118,9 +118,13 @@ class Organization(models.Model):
role.organization_groups.add(group)
account.organization_groups.add(group)
Permission.invalidate_cache()
for permission in Permission.all():
role.permissions.add(permission.stored_permission)
Organization.objects.clear_cache()
return account, password_value
else:
logger.error(

View File

@@ -0,0 +1 @@
from .base import OrganizationTestCase #NOQA

View File

@@ -0,0 +1,15 @@
from __future__ import unicode_literals
from django.test import TestCase
from ..models import Organization
from ..utils import create_default_organization
class OrganizationTestCase(TestCase):
def setUp(self):
create_default_organization()
def tearDown(self):
Organization.objects.all().delete()
Organization.objects.clear_cache()

View File

@@ -7,12 +7,16 @@ from ..models import Organization
from .literals import TEST_ORGANIZATION_LABEL, TEST_ORGANIZATION_EDITED_LABEL
class OrganizationTestCase(TestCase):
class OrganizationModelTestCase(TestCase):
def setUp(self):
self.organization = Organization.objects.create(
label=TEST_ORGANIZATION_LABEL
)
def tearDown(self):
self.organization.delete()
Organization.objects.clear_cache()
def test_basic(self):
queryset = Organization.objects.get_for_now()

View File

@@ -1,11 +1,11 @@
from __future__ import absolute_import, unicode_literals
from documents.tests.test_views import GenericDocumentViewTestCase
from common.tests.test_views import GenericViewTestCase
from ..models import Organization
class OrganizationViewTestCase(GenericDocumentViewTestCase):
class OrganizationViewTestCase(GenericViewTestCase):
def setUp(self):
super(OrganizationViewTestCase, self).setUp()
# Create two organizations
@@ -17,7 +17,7 @@ class OrganizationViewTestCase(GenericDocumentViewTestCase):
)
# Create an organization admin for organization B
username, password = self.organization_b.create_admin()
user, password = self.organization_b.create_admin()
with self.settings(ORGANIZATION_ID=self.organization_b.pk):
# Login organization admin for organization B
self.login(username=username, password=password)
self.login(username=user.username, password=password)

View File

@@ -42,3 +42,5 @@ def create_default_organization(verbosity=2, interactive=True, using=DEFAULT_DB_
cursor.execute(command)
Organization.objects.clear_cache()
return Organization.objects.get(pk=1)

View File

@@ -10,16 +10,16 @@ from permissions import Permission
class MayanObjectPermissionsFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
required_permission = getattr(
required_permissions = getattr(
view, 'mayan_object_permissions', {}
).get(request.method, None)
if required_permission:
if required_permissions:
try:
Permission.check_permissions(request.user, required_permission)
Permission.check_permissions(request.user, required_permissions)
except PermissionDenied:
return AccessControlList.objects.filter_by_access(
required_permission[0], request.user, queryset
required_permissions[0], request.user, queryset
)
else:
return queryset

View File

@@ -1,6 +1,4 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import absolute_import, unicode_literals
from django.core.exceptions import PermissionDenied
@@ -27,24 +25,24 @@ class MayanPermission(BasePermission):
return True
def has_object_permission(self, request, view, obj):
required_permission = getattr(
required_permissions = getattr(
view, 'mayan_object_permissions', {}
).get(request.method, None)
if required_permission:
if required_permissions:
try:
Permission.check_permissions(request.user, required_permission)
Permission.check_permissions(request.user, required_permissions)
except PermissionDenied:
try:
if hasattr(view, 'mayan_permission_attribute_check'):
AccessControlList.objects.check_access(
permissions=required_permission,
permissions=required_permissions,
user=request.user, obj=obj,
related=view.mayan_permission_attribute_check
)
else:
AccessControlList.objects.check_access(
required_permission, request.user, obj
required_permissions, request.user, obj
)
except PermissionDenied:
return False

Some files were not shown because too many files have changed in this diff Show More