diff --git a/docs/releases/2.2.rst b/docs/releases/2.2.rst index f273d0109d..b3ff76e2f3 100644 --- a/docs/releases/2.2.rst +++ b/docs/releases/2.2.rst @@ -26,6 +26,7 @@ the user links - Stop loading theme fonts from the web - Add support for attaching multiple tags to single or multiple documents. - Refactor the workflow for removing tags from single and multiple documents. +- Move new version creation blocking from the documents app to the checkouts app Removals -------- @@ -84,11 +85,12 @@ Backward incompatible changes Bugs fixed or issues closed =========================== +* `GitLab issue #294 `_ Move new version creation blocking from the documents app to the checkouts app * `GitLab issue #301 `_ Remove the installation app * `GitLab issue #307 `_ Enter multiple Tags at once * `GitLab issue #311 `_ acl page return ContentType:Document * `GitLab issue #319 `_ TransformationResize issue with very "long" image * `GitLab issue #342 `_ Tags should be of unordered / unsorted data type -* `GitLab issue #342 `_ Bootstrap's dependency on fonts.googleapis.com causes Mayan EDMS web interface load slowly if public internet is unreachable +* `GitLab issue #343 `_ Bootstrap's dependency on fonts.googleapis.com causes Mayan EDMS web interface load slowly if public internet is unreachable .. _PyPI: https://pypi.python.org/pypi/mayan-edms/ diff --git a/mayan/apps/checkouts/apps.py b/mayan/apps/checkouts/apps.py index 153dc0aec2..759f5fcfdd 100644 --- a/mayan/apps/checkouts/apps.py +++ b/mayan/apps/checkouts/apps.py @@ -6,6 +6,7 @@ from kombu import Exchange, Queue from django.apps import apps from django.core.urlresolvers import reverse_lazy +from django.db.models.signals import pre_save from django.utils.translation import ugettext_lazy as _ from acls import ModelPermission @@ -14,6 +15,7 @@ from common.classes import DashboardWidget from mayan.celery import app from rest_api.classes import APIEndPoint +from .handlers import check_new_version_creation from .links import ( link_checkin_document, link_checkout_document, link_checkout_info, link_checkout_list @@ -40,6 +42,9 @@ class CheckoutsApp(MayanAppConfig): Document = apps.get_model( app_label='documents', model_name='Document' ) + DocumentVersion = apps.get_model( + app_label='documents', model_name='DocumentVersion' + ) DocumentCheckout = self.get_model('DocumentCheckout') @@ -117,3 +122,9 @@ class CheckoutsApp(MayanAppConfig): 'checkouts:checkin_document' ) ) + + pre_save.connect( + check_new_version_creation, + dispatch_uid='check_new_version_creation', + sender=DocumentVersion + ) diff --git a/mayan/apps/checkouts/exceptions.py b/mayan/apps/checkouts/exceptions.py index ca26c0ce33..168090eed6 100644 --- a/mayan/apps/checkouts/exceptions.py +++ b/mayan/apps/checkouts/exceptions.py @@ -23,3 +23,10 @@ class DocumentAlreadyCheckedOut(DocumentCheckoutError): """ def __unicode__(self): return ugettext('Document already checked out.') + + +class NewDocumentVersionNotAllowed(DocumentCheckoutError): + """ + Uploading new versions for this document is not allowed + """ + pass diff --git a/mayan/apps/checkouts/handlers.py b/mayan/apps/checkouts/handlers.py new file mode 100644 index 0000000000..9ab432f99c --- /dev/null +++ b/mayan/apps/checkouts/handlers.py @@ -0,0 +1,18 @@ +from __future__ import unicode_literals + +from django.apps import apps + +from .exceptions import NewDocumentVersionNotAllowed + + +def check_new_version_creation(sender, instance, **kwargs): + """ + Make sure that new version creation is allowed for this document + """ + + NewVersionBlock = apps.get_model( + app_label='checkouts', model_name='NewVersionBlock' + ) + + if NewVersionBlock.objects.is_blocked(instance.document): + raise NewDocumentVersionNotAllowed diff --git a/mayan/apps/checkouts/managers.py b/mayan/apps/checkouts/managers.py index 5af640227f..064c4bda37 100644 --- a/mayan/apps/checkouts/managers.py +++ b/mayan/apps/checkouts/managers.py @@ -87,3 +87,14 @@ class DocumentCheckoutManager(models.Manager): return True else: return not checkout_info.block_new_version + + +class NewVersionBlockManager(models.Manager): + def block(self, document): + self.get_or_create(document=document) + + def unblock(self, document): + self.filter(document=document).delete() + + def is_blocked(self, document): + return self.filter(document=document).exists() diff --git a/mayan/apps/checkouts/migrations/0006_newversionblock.py b/mayan/apps/checkouts/migrations/0006_newversionblock.py new file mode 100644 index 0000000000..49f9e01ad1 --- /dev/null +++ b/mayan/apps/checkouts/migrations/0006_newversionblock.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.11 on 2016-12-22 05:34 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0036_auto_20161222_0534'), + ('checkouts', '0005_auto_20160122_0756'), + ] + + operations = [ + migrations.CreateModel( + name='NewVersionBlock', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documents.Document', verbose_name='Document')), + ], + options={ + 'verbose_name': 'New version block', + 'verbose_name_plural': 'New version blocks', + }, + ), + ] diff --git a/mayan/apps/checkouts/models.py b/mayan/apps/checkouts/models.py index 45ec9e81b8..3ab39c28d3 100644 --- a/mayan/apps/checkouts/models.py +++ b/mayan/apps/checkouts/models.py @@ -10,11 +10,11 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ -from documents.models import Document, NewVersionBlock +from documents.models import Document from .events import event_document_check_out from .exceptions import DocumentAlreadyCheckedOut -from .managers import DocumentCheckoutManager +from .managers import DocumentCheckoutManager, NewVersionBlockManager logger = logging.getLogger(__name__) @@ -86,3 +86,13 @@ class DocumentCheckout(models.Model): class Meta: verbose_name = _('Document checkout') verbose_name_plural = _('Document checkouts') + + +class NewVersionBlock(models.Model): + document = models.ForeignKey(Document, verbose_name=_('Document')) + + objects = NewVersionBlockManager() + + class Meta: + verbose_name = _('New version block') + verbose_name_plural = _('New version blocks') diff --git a/mayan/apps/checkouts/tests/test_models.py b/mayan/apps/checkouts/tests/test_models.py index 74a5b256a3..ba2c5174c6 100644 --- a/mayan/apps/checkouts/tests/test_models.py +++ b/mayan/apps/checkouts/tests/test_models.py @@ -7,7 +7,7 @@ from django.contrib.auth import get_user_model from django.test import TestCase, override_settings from django.utils.timezone import now -from documents.exceptions import NewDocumentVersionNotAllowed +from common.tests import BaseTestCase from documents.models import DocumentType from documents.tests.literals import ( TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH @@ -16,8 +16,11 @@ from user_management.tests.literals import ( TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD ) -from ..exceptions import DocumentAlreadyCheckedOut, DocumentNotCheckedOut -from ..models import DocumentCheckout +from ..exceptions import ( + DocumentAlreadyCheckedOut, DocumentNotCheckedOut, + NewDocumentVersionNotAllowed +) +from ..models import DocumentCheckout, NewVersionBlock @override_settings(OCR_AUTO_OCR=False) @@ -116,3 +119,58 @@ class DocumentCheckoutTestCase(TestCase): DocumentCheckout.objects.check_in_expired_check_outs() self.assertFalse(self.document.is_checked_out()) + + def test_blocking_new_versions(self): + NewVersionBlock.objects.block(document=self.document) + + with self.assertRaises(NewDocumentVersionNotAllowed): + with open(TEST_SMALL_DOCUMENT_PATH) as file_object: + self.document.new_version(file_object=file_object) + + +@override_settings(OCR_AUTO_OCR=False) +class NewVersionBlockTestCase(BaseTestCase): + def setUp(self): + super(NewVersionBlockTestCase, self).setUp() + + self.document_type = DocumentType.objects.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): + self.document.delete() + self.document_type.delete() + super(NewVersionBlockTestCase, self).tearDown() + + def test_blocking(self): + NewVersionBlock.objects.block(document=self.document) + + self.assertEqual(NewVersionBlock.objects.count(), 1) + self.assertEqual( + NewVersionBlock.objects.first().document, self.document + ) + + def test_unblocking(self): + NewVersionBlock.objects.create(document=self.document) + + NewVersionBlock.objects.unblock(document=self.document) + + self.assertEqual(NewVersionBlock.objects.count(), 0) + + def test_is_blocked(self): + NewVersionBlock.objects.create(document=self.document) + + self.assertTrue( + NewVersionBlock.objects.is_blocked(document=self.document) + ) + + NewVersionBlock.objects.all().delete() + + self.assertFalse( + NewVersionBlock.objects.is_blocked(document=self.document) + ) diff --git a/mayan/apps/documents/exceptions.py b/mayan/apps/documents/exceptions.py index fa6b615645..80f86ad910 100644 --- a/mayan/apps/documents/exceptions.py +++ b/mayan/apps/documents/exceptions.py @@ -6,10 +6,3 @@ class DocumentException(Exception): Base documents warning """ pass - - -class NewDocumentVersionNotAllowed(DocumentException): - """ - Uploading new versions for this document is not allowed - """ - pass diff --git a/mayan/apps/documents/managers.py b/mayan/apps/documents/managers.py index 2f1ec97197..2ca7084578 100644 --- a/mayan/apps/documents/managers.py +++ b/mayan/apps/documents/managers.py @@ -98,17 +98,6 @@ class DocumentTypeManager(models.Manager): return self.get(label=label) -class NewVersionBlockManager(models.Manager): - def block(self, document): - self.get_or_create(document=document) - - def unblock(self, document): - self.filter(document=document).delete() - - def is_blocked(self, document): - return self.filter(document=document).exists() - - class PassthroughManager(models.Manager): pass diff --git a/mayan/apps/documents/migrations/0036_auto_20161222_0534.py b/mayan/apps/documents/migrations/0036_auto_20161222_0534.py new file mode 100644 index 0000000000..df8e31754f --- /dev/null +++ b/mayan/apps/documents/migrations/0036_auto_20161222_0534.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.11 on 2016-12-22 05:34 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0035_auto_20161102_0633'), + ] + + operations = [ + migrations.RemoveField( + model_name='newversionblock', + name='document', + ), + migrations.DeleteModel( + name='NewVersionBlock', + ), + ] diff --git a/mayan/apps/documents/models.py b/mayan/apps/documents/models.py index fc83ad76c9..ec965dc24c 100644 --- a/mayan/apps/documents/models.py +++ b/mayan/apps/documents/models.py @@ -28,11 +28,10 @@ from .events import ( event_document_properties_edit, event_document_type_change, event_document_version_revert ) -from .exceptions import NewDocumentVersionNotAllowed from .literals import DEFAULT_DELETE_PERIOD, DEFAULT_DELETE_TIME_UNIT from .managers import ( - DocumentManager, DocumentTypeManager, NewVersionBlockManager, - PassthroughManager, RecentDocumentManager, TrashCanManager + DocumentManager, DocumentTypeManager, PassthroughManager, + RecentDocumentManager, TrashCanManager ) from .permissions import permission_document_view from .runtime import cache_storage_backend, storage_backend @@ -390,8 +389,6 @@ class DocumentVersion(models.Model): if new_document_version: logger.info('Creating new version for document: %s', self.document) - if NewVersionBlock.objects.is_blocked(self.document): - raise NewDocumentVersionNotAllowed try: with transaction.atomic(): @@ -821,16 +818,6 @@ class DocumentPageResult(DocumentPage): verbose_name_plural = _('Document pages') -class NewVersionBlock(models.Model): - document = models.ForeignKey(Document, verbose_name=_('Document')) - - objects = NewVersionBlockManager() - - class Meta: - verbose_name = _('New version block') - verbose_name_plural = _('New version blocks') - - @python_2_unicode_compatible class RecentDocument(models.Model): """ diff --git a/mayan/apps/documents/tests/test_models.py b/mayan/apps/documents/tests/test_models.py index a272d18b0b..32d4465d8f 100644 --- a/mayan/apps/documents/tests/test_models.py +++ b/mayan/apps/documents/tests/test_models.py @@ -6,9 +6,8 @@ import time from common.tests import BaseTestCase from django.test import override_settings -from ..exceptions import NewDocumentVersionNotAllowed from ..literals import STUB_EXPIRATION_INTERVAL -from ..models import DeletedDocument, Document, DocumentType, NewVersionBlock +from ..models import DeletedDocument, Document, DocumentType from .literals import ( TEST_DOCUMENT_TYPE, TEST_DOCUMENT_PATH, TEST_MULTI_PAGE_TIFF_PATH, @@ -275,58 +274,3 @@ class DocumentManagerTestCase(BaseTestCase): Document.objects.delete_stubs() self.assertEqual(Document.objects.count(), 0) - - -@override_settings(OCR_AUTO_OCR=False) -class NewVersionBlockTestCase(BaseTestCase): - def setUp(self): - super(NewVersionBlockTestCase, self).setUp() - - self.document_type = DocumentType.objects.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): - self.document.delete() - self.document_type.delete() - super(NewVersionBlockTestCase, self).tearDown() - - def test_blocking(self): - NewVersionBlock.objects.block(document=self.document) - - self.assertEqual(NewVersionBlock.objects.count(), 1) - self.assertEqual( - NewVersionBlock.objects.first().document, self.document - ) - - def test_unblocking(self): - NewVersionBlock.objects.create(document=self.document) - - NewVersionBlock.objects.unblock(document=self.document) - - self.assertEqual(NewVersionBlock.objects.count(), 0) - - def test_is_blocked(self): - NewVersionBlock.objects.create(document=self.document) - - self.assertTrue( - NewVersionBlock.objects.is_blocked(document=self.document) - ) - - NewVersionBlock.objects.all().delete() - - self.assertFalse( - NewVersionBlock.objects.is_blocked(document=self.document) - ) - - def test_blocking_new_versions(self): - NewVersionBlock.objects.block(document=self.document) - - with self.assertRaises(NewDocumentVersionNotAllowed): - with open(TEST_SMALL_DOCUMENT_PATH) as file_object: - self.document.new_version(file_object=file_object) diff --git a/mayan/apps/sources/links.py b/mayan/apps/sources/links.py index 3ae238d1c5..9ec5234e64 100644 --- a/mayan/apps/sources/links.py +++ b/mayan/apps/sources/links.py @@ -20,7 +20,7 @@ from .permissions import ( def document_new_version_not_blocked(context): NewVersionBlock = apps.get_model( - app_label='documents', model_name='NewVersionBlock' + app_label='checkouts', model_name='NewVersionBlock' ) return not NewVersionBlock.objects.is_blocked(context['object']) diff --git a/mayan/apps/sources/tests/test_views.py b/mayan/apps/sources/tests/test_views.py index b5e45aaadf..f0a572551b 100644 --- a/mayan/apps/sources/tests/test_views.py +++ b/mayan/apps/sources/tests/test_views.py @@ -9,9 +9,10 @@ from django.test.client import Client from django.test import TestCase, override_settings from acls.models import AccessControlList +from checkouts.models import NewVersionBlock from common.tests.test_views import GenericViewTestCase from common.utils import fs_cleanup, mkdtemp -from documents.models import Document, DocumentType, NewVersionBlock +from documents.models import Document, DocumentType from documents.permissions import permission_document_create from documents.tests import ( TEST_DOCUMENT_PATH, TEST_SMALL_DOCUMENT_PATH, TEST_DOCUMENT_DESCRIPTION, diff --git a/mayan/apps/sources/views.py b/mayan/apps/sources/views.py index 0fb4d16c60..ae9d1cadf6 100644 --- a/mayan/apps/sources/views.py +++ b/mayan/apps/sources/views.py @@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from acls.models import AccessControlList +from checkouts.models import NewVersionBlock from common import menu_facet from common.models import SharedUploadedFile from common.utils import encapsulate @@ -15,7 +16,7 @@ from common.views import ( SingleObjectEditView, SingleObjectListView ) from common.widgets import two_state_template -from documents.models import DocumentType, Document, NewVersionBlock +from documents.models import DocumentType, Document from documents.permissions import ( permission_document_create, permission_document_new_version )