From fd77632e3c256d4aeba99903c57380d8a246f7cf Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 10:47:06 +0100 Subject: [PATCH 01/26] Add django 4.2 support --- .github/workflows/test.yml | 4 +- djangocms_moderation/__init__.py | 1 - djangocms_moderation/admin.py | 82 +++++++++++++------ djangocms_moderation/admin_actions.py | 4 +- djangocms_moderation/contrib/__init__.py | 0 .../contrib/moderation_forms/__init__.py | 0 .../contrib/moderation_forms/cms_plugins.py | 32 -------- .../migrations/0001_initial.py | 23 ------ .../moderation_forms/migrations/__init__.py | 0 .../contrib/moderation_forms/models.py | 10 --- djangocms_moderation/helpers.py | 8 +- djangocms_moderation/models.py | 1 + djangocms_moderation/views.py | 4 +- tests/requirements/dj22_cms40.txt | 6 -- tests/requirements/dj32_cms40.txt | 6 ++ tests/requirements/dj42_cms41.txt | 7 ++ tests/requirements/requirements_base.txt | 10 +-- tests/settings.py | 10 ++- tests/test_cms_toolbars.py | 32 +++++--- tests/test_handlers.py | 44 ---------- tests/test_monkeypatch.py | 22 +++-- 21 files changed, 125 insertions(+), 181 deletions(-) delete mode 100644 djangocms_moderation/contrib/__init__.py delete mode 100644 djangocms_moderation/contrib/moderation_forms/__init__.py delete mode 100644 djangocms_moderation/contrib/moderation_forms/cms_plugins.py delete mode 100644 djangocms_moderation/contrib/moderation_forms/migrations/0001_initial.py delete mode 100644 djangocms_moderation/contrib/moderation_forms/migrations/__init__.py delete mode 100644 djangocms_moderation/contrib/moderation_forms/models.py delete mode 100644 tests/requirements/dj22_cms40.txt create mode 100644 tests/requirements/dj42_cms41.txt delete mode 100644 tests/test_handlers.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa839f07..d468aea2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,9 +10,9 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.7, 3.8, 3.9 ] # latest release minus two + python-version: [ 3.9, "3.10", "3.11", "3.12" ] # latest release minus two requirements-file: [ - dj22_cms40.txt, + dj42_cms41.txt, dj32_cms40.txt, ] os: [ diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 1e525f4c..9389d356 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,2 @@ __version__ = "2.1.6" -default_app_config = "djangocms_moderation.apps.ModerationConfig" diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index 460bb8ee..90a6fef5 100644 --- a/djangocms_moderation/admin.py +++ b/djangocms_moderation/admin.py @@ -16,7 +16,7 @@ from cms.toolbar.utils import get_object_preview_url from cms.utils.helpers import is_editable_model -from adminsortable2.admin import SortableInlineAdminMixin +from adminsortable2.admin import SortableInlineAdminMixin, SortableAdminMixin from treebeard.admin import TreeAdmin from . import constants, signals @@ -73,12 +73,17 @@ def has_add_permission(self, request): def has_delete_permission(self, request, obj=None): return False + @admin.display( + description=_("Status") + ) def show_user(self, obj): _name = obj.get_by_user_name() return gettext("By {user}").format(user=_name) - show_user.short_description = _("Status") + @admin.display( + description=_("Form Submission") + ) def form_submission(self, obj): instance = get_form_submission_for_step( obj.moderation_request, obj.step_approved @@ -96,7 +101,6 @@ def form_submission(self, obj): '{}', url, obj.step_approved.role.name ) - form_submission.short_description = _("Form Submission") def get_readonly_fields(self, request, obj=None): if obj.user_can_moderate(request.user) or obj.user_is_author(request.user): @@ -107,6 +111,7 @@ def get_readonly_fields(self, request, obj=None): return self.fields +@admin.register(ModerationRequestTreeNode) class ModerationRequestTreeAdmin(TreeAdmin): """ This admin is purely for the change list of Moderation Requests using the treebeard nodes to @@ -177,6 +182,9 @@ def get_list_display(self, request): ] return list_display + @admin.display( + description=_("actions") + ) def list_display_actions(self, obj): """Display links to state change endpoints """ @@ -184,7 +192,6 @@ def list_display_actions(self, obj): "", "{}", ((action(obj),) for action in self.get_list_display_actions()) ) - list_display_actions.short_description = _("actions") def get_list_display_actions(self): actions = [] @@ -210,6 +217,9 @@ def _get_configured_fields(self, request): return fields + @admin.display( + description=_('ID') + ) def get_id(self, obj): return format_html( '{id}', @@ -219,22 +229,30 @@ def get_id(self, obj): ), id=obj.moderation_request_id, ) - get_id.short_description = _('ID') + @admin.display( + description=_('Content type') + ) def get_content_type(self, obj): return ContentType.objects.get_for_model( obj.moderation_request.version.versionable.grouper_model ) - get_content_type.short_description = _('Content type') + @admin.display( + description=_('Title') + ) def get_title(self, obj): return obj.moderation_request.version.content - get_title.short_description = _('Title') + @admin.display( + description=_('Author') + ) def get_version_author(self, obj): return obj.moderation_request.version.created_by - get_version_author.short_description = _('Author') + @admin.display( + description=_("Preview") + ) def get_preview_link(self, obj): content = obj.moderation_request.version.content if is_editable_model(content.__class__): @@ -254,8 +272,10 @@ def get_preview_link(self, obj): object_preview_url, ) - get_preview_link.short_description = _("Preview") + @admin.display( + description=_('Reviewer') + ) def get_reviewer(self, obj): last_action = obj.moderation_request.get_last_action() if not last_action: @@ -264,7 +284,6 @@ def get_reviewer(self, obj): next_step = obj.moderation_request.get_next_required() return next_step.role.name return last_action._get_user_name(last_action.by_user) - get_reviewer.short_description = _('Reviewer') def get_status(self, obj): # We can have moderation requests without any action (e.g. the @@ -466,6 +485,7 @@ def _traverse_moderation_nodes(node_item): return HttpResponseRedirect(redirect_url) +@admin.register(ModerationRequest) class ModerationRequestAdmin(admin.ModelAdmin): class Media: js = ('admin/js/jquery.init.js', 'djangocms_moderation/js/actions.js',) @@ -813,11 +833,13 @@ def changelist_view(self, request, extra_context=None): return tree_node_admin.changelist_view(request, extra_context) +@admin.register(Role) class RoleAdmin(admin.ModelAdmin): list_display = ["name", "user", "group", "confirmation_page"] fields = ["name", "user", "group", "confirmation_page"] +@admin.register(CollectionComment) class CollectionCommentAdmin(admin.ModelAdmin): list_display = ["date_created", "message", "author"] fields = ["collection", "message", "author"] @@ -900,6 +922,7 @@ def get_readonly_fields(self, request, obj=None): return self.list_display +@admin.register(RequestComment) class RequestCommentAdmin(admin.ModelAdmin): list_display = ["date_created", "message", "get_author"] fields = ["moderation_request", "message", "author"] @@ -907,10 +930,12 @@ class RequestCommentAdmin(admin.ModelAdmin): class Media: css = {"all": ("djangocms_moderation/css/comments_changelist.css",)} + @admin.display( + description=_("User") + ) def get_author(self, obj): return obj.author_name - get_author.short_description = _("User") def get_changeform_initial_data(self, request): data = {"author": request.user} @@ -990,7 +1015,8 @@ def get_extra(self, request, obj=None, **kwargs): return 1 -class WorkflowAdmin(admin.ModelAdmin): +@admin.register(Workflow) +class WorkflowAdmin(SortableAdminMixin, admin.ModelAdmin): inlines = [WorkflowStepInline] list_display = ["name", "is_default"] fields = [ @@ -1002,6 +1028,7 @@ class WorkflowAdmin(admin.ModelAdmin): ] +@admin.register(ModerationCollection) class ModerationCollectionAdmin(admin.ModelAdmin): class Media: js = ("admin/js/jquery.init.js", "djangocms_moderation/js/actions.js",) @@ -1033,11 +1060,16 @@ def get_list_display(self, request): def job_id(self, obj): return obj.pk + @admin.display( + description=_('reviewers') + ) def commaseparated_reviewers(self, obj): reviewers = self.model.objects.reviewers(obj) return ", ".join(map(get_user_model().get_full_name, reviewers)) - commaseparated_reviewers.short_description = _('reviewers') + @admin.display( + description=_("actions") + ) def list_display_actions(self, obj): """Display links to state change endpoints """ @@ -1045,7 +1077,6 @@ def list_display_actions(self, obj): "", "{}", ((action(obj),) for action in self.get_list_display_actions()) ) - list_display_actions.short_description = _("actions") def get_list_display_actions(self): actions = [self.get_edit_link, self.get_requests_link] @@ -1136,6 +1167,7 @@ def has_delete_permission(self, request, obj=None): return False +@admin.register(ConfirmationPage) class ConfirmationPageAdmin(PlaceholderAdminMixin, admin.ModelAdmin): view_on_site = True @@ -1153,6 +1185,7 @@ def _url(regex, fn, name, **kwargs): return url_patterns + super().get_urls() +@admin.register(ConfirmationFormSubmission) class ConfirmationFormSubmissionAdmin(admin.ModelAdmin): list_display = ["moderation_request", "for_step", "submitted_at"] fields = [ @@ -1178,16 +1211,23 @@ def change_view(self, request, object_id, form_url="", extra_context=None): request, object_id, form_url, extra_context=extra_context ) + @admin.display( + description=_("Request") + ) def moderation_request(self, obj): return obj.moderation_request_id - moderation_request.short_description = _("Request") + @admin.display( + description=_("By User") + ) def show_user(self, obj): return obj.get_by_user_name() - show_user.short_description = _("By User") + @admin.display( + description=_("Form Data") + ) def form_data(self, obj): data = obj.get_form_data() return format_html_join( @@ -1199,15 +1239,5 @@ def form_data(self, obj): ), ) - form_data.short_description = _("Form Data") -admin.site.register(ModerationRequestTreeNode, ModerationRequestTreeAdmin) -admin.site.register(ModerationRequest, ModerationRequestAdmin) -admin.site.register(CollectionComment, CollectionCommentAdmin) -admin.site.register(RequestComment, RequestCommentAdmin) -admin.site.register(ModerationCollection, ModerationCollectionAdmin) -admin.site.register(Role, RoleAdmin) -admin.site.register(Workflow, WorkflowAdmin) -admin.site.register(ConfirmationPage, ConfirmationPageAdmin) -admin.site.register(ConfirmationFormSubmission, ConfirmationFormSubmissionAdmin) diff --git a/djangocms_moderation/admin_actions.py b/djangocms_moderation/admin_actions.py index 07fc3eea..926e1282 100644 --- a/djangocms_moderation/admin_actions.py +++ b/djangocms_moderation/admin_actions.py @@ -148,14 +148,14 @@ def add_items_to_collection(modeladmin, request, queryset): args=(), ), version_ids=",".join(version_ids), - return_to_url=request.META.get("HTTP_REFERER", ""), + return_to_url=request.headers.get("referer", ""), ) return HttpResponseRedirect(admin_url) else: modeladmin.message_user( request, _("No suitable items found to add to moderation collection") ) - return HttpResponseRedirect(request.META.get("HTTP_REFERER", "")) + return HttpResponseRedirect(request.headers.get("referer", "")) add_items_to_collection.short_description = _("Add to moderation collection") diff --git a/djangocms_moderation/contrib/__init__.py b/djangocms_moderation/contrib/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/djangocms_moderation/contrib/moderation_forms/__init__.py b/djangocms_moderation/contrib/moderation_forms/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/djangocms_moderation/contrib/moderation_forms/cms_plugins.py b/djangocms_moderation/contrib/moderation_forms/cms_plugins.py deleted file mode 100644 index 61e23625..00000000 --- a/djangocms_moderation/contrib/moderation_forms/cms_plugins.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.utils.translation import gettext_lazy as _ - -from cms.plugin_pool import plugin_pool - -from aldryn_forms.cms_plugins import FormPlugin - -from djangocms_moderation.helpers import get_page_or_404 -from djangocms_moderation.signals import confirmation_form_submission - -from .models import ModerationForm - - -class ModerationFormPlugin(FormPlugin): - name = _("Moderation Form") - model = ModerationForm - fieldsets = ((None, {"fields": ("name",)}),) - - def form_valid(self, instance, request, form): - fields = form.get_serialized_fields(is_confirmation=False) - fields_as_dicts = [field._asdict() for field in fields] - page = get_page_or_404(request.GET.get("page"), request.GET.get("language")) - - confirmation_form_submission.send( - sender=self.__class__, - page=page, - language=request.GET.get("language"), - user=request.user, - form_data=fields_as_dicts, - ) - - -plugin_pool.register_plugin(ModerationFormPlugin) diff --git a/djangocms_moderation/contrib/moderation_forms/migrations/0001_initial.py b/djangocms_moderation/contrib/moderation_forms/migrations/0001_initial.py deleted file mode 100644 index c2ab629c..00000000 --- a/djangocms_moderation/contrib/moderation_forms/migrations/0001_initial.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 1.11.12 on 2018-05-03 09:15 -from django.db import migrations - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [("aldryn_forms", "0011_auto_20180110_1300")] - - operations = [ - migrations.CreateModel( - name="ModerationForm", - fields=[], - options={ - "verbose_name": "Moderation Form", - "verbose_name_plural": "Moderation Forms", - "proxy": True, - "indexes": [], - }, - bases=("aldryn_forms.formplugin",), - ) - ] diff --git a/djangocms_moderation/contrib/moderation_forms/migrations/__init__.py b/djangocms_moderation/contrib/moderation_forms/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/djangocms_moderation/contrib/moderation_forms/models.py b/djangocms_moderation/contrib/moderation_forms/models.py deleted file mode 100644 index 72027ccd..00000000 --- a/djangocms_moderation/contrib/moderation_forms/models.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.utils.translation import gettext_lazy as _ - -from aldryn_forms.models import FormPlugin - - -class ModerationForm(FormPlugin): - class Meta: - proxy = True - verbose_name = _("Moderation Form") - verbose_name_plural = _("Moderation Forms") diff --git a/djangocms_moderation/helpers.py b/djangocms_moderation/helpers.py index 25249302..b7fdcf03 100644 --- a/djangocms_moderation/helpers.py +++ b/djangocms_moderation/helpers.py @@ -21,9 +21,13 @@ User = get_user_model() try: - from djangocms_version_locking.helpers import content_is_unlocked_for_user + from djangocms_versioning.helpers import content_is_unlocked_for_user except ImportError: - content_is_unlocked_for_user = None + try: + # Before djangocms-versioning 2.0.0, version locking was in a separate package + from djangocms_version_locking.helpers import content_is_unlocked_for_user + except ImportError: + content_is_unlocked_for_user = None def get_page_or_404(obj_id, language): diff --git a/djangocms_moderation/models.py b/djangocms_moderation/models.py index 1e92dccc..166daf58 100644 --- a/djangocms_moderation/models.py +++ b/djangocms_moderation/models.py @@ -144,6 +144,7 @@ class Workflow(models.Model): class Meta: verbose_name = _("Workflow") verbose_name_plural = _("Workflows") + ordering = ("name",) def __str__(self): return self.name diff --git a/djangocms_moderation/views.py b/djangocms_moderation/views.py index 03f899e3..bab4d071 100644 --- a/djangocms_moderation/views.py +++ b/djangocms_moderation/views.py @@ -6,7 +6,7 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils.decorators import method_decorator -from django.utils.http import is_safe_url +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext_lazy as _, ngettext from django.views.generic import FormView @@ -83,7 +83,7 @@ def _get_success_redirect(self): """ return_to_url = self.request.GET.get("return_to_url") if return_to_url: - url_is_safe = is_safe_url( + url_is_safe = url_has_allowed_host_and_scheme( url=return_to_url, allowed_hosts=self.request.get_host(), require_https=self.request.is_secure(), diff --git a/tests/requirements/dj22_cms40.txt b/tests/requirements/dj22_cms40.txt deleted file mode 100644 index 4e51203c..00000000 --- a/tests/requirements/dj22_cms40.txt +++ /dev/null @@ -1,6 +0,0 @@ --r ./requirements_base.txt - -Django>=2.2,<3.0 - -django-admin-sortable2<1.0 -django_polymorphic==2.0.3 diff --git a/tests/requirements/dj32_cms40.txt b/tests/requirements/dj32_cms40.txt index 6dd211b1..55f6c506 100644 --- a/tests/requirements/dj32_cms40.txt +++ b/tests/requirements/dj32_cms40.txt @@ -4,3 +4,9 @@ Django>=3.2,<4.0 django-admin-sortable2<2 django_polymorphic + +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/django-cms/djangocms-text-ckeditor/tarball/support/4.0.x#egg=djangocms-text-ckeditor +https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning +https://github.com/FidelityInternational/djangocms-version-locking/tarball/1.2.0#egg=djangocms-version-locking +https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias diff --git a/tests/requirements/dj42_cms41.txt b/tests/requirements/dj42_cms41.txt new file mode 100644 index 00000000..d2ef85cc --- /dev/null +++ b/tests/requirements/dj42_cms41.txt @@ -0,0 +1,7 @@ +-r ./requirements_base.txt + +Django>=4.2,<5.0 +django-cms>=4.1,<4.2 + +djangocms-versioning>=2.0.0 +djangocms-alias>=2.0.0 diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 0c3d55fa..6d9a0dbe 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -1,4 +1,3 @@ -aldryn-forms cachetools coverage django-classy-tags @@ -9,13 +8,8 @@ django-app-helper factory-boy flake8 isort +ruff mock pyflakes>=2.1.1 python-dateutil>=2.4 - -# Unreleased django-cms 4.0 compatible packages -https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms -https://github.com/django-cms/djangocms-text-ckeditor/tarball/support/4.0.x#egg=djangocms-text-ckeditor -https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning -https://github.com/FidelityInternational/djangocms-version-locking/tarball/1.2.0#egg=djangocms-version-locking -https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias +djangocms-text-ckeditor diff --git a/tests/settings.py b/tests/settings.py index c016c9f2..239b606c 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -4,9 +4,7 @@ "tests.utils.app_1", "tests.utils.app_2", "djangocms_versioning", - "djangocms_version_locking", # the following 4 apps are related - "aldryn_forms", "filer", "easy_thumbnails", "captcha", @@ -26,11 +24,17 @@ "djangocms_version_locking": None, "filer": None, "djangocms_moderation": None, - "aldryn_forms": None, }, "DEFAULT_AUTO_FIELD": "django.db.models.AutoField", + "DJANGOCMS_VERSIONING_LOCK_VERSIONS": True, + "CMS_CONFIRM_VERSION4": True, } +from cms import __version__ as cms_version + +if cms_version < "4.1.0": + HELPER_SETTINGS["INSTALLED_APPS"].append("djangocms_version_locking") + def run(): from djangocms_helper import runner diff --git a/tests/test_cms_toolbars.py b/tests/test_cms_toolbars.py index 62727ebe..08838e80 100644 --- a/tests/test_cms_toolbars.py +++ b/tests/test_cms_toolbars.py @@ -8,6 +8,7 @@ from cms.test_utils.testcases import CMSTestCase from cms.toolbar.toolbar import CMSToolbar +from djangocms_versioning import __version__ as versioning_version from djangocms_versioning.test_utils.factories import PageVersionFactory from djangocms_moderation import constants @@ -72,7 +73,8 @@ def func(button): return button.name == callable_or_name for button_list in toolbar.get_right_items(): - found = found + [button for button in button_list.buttons if func(button)] + if hasattr(button_list, "buttons"): + found = found + [button for button in button_list.buttons if func(button)] return found def _button_exists(self, callable_or_name, toolbar): @@ -187,16 +189,17 @@ def test_add_edit_button_with_version_lock(self): toolbar.populate() toolbar.post_template_populate() - self.assertTrue( - self._button_exists( - lambda button: button.name.endswith("Edit"), toolbar.toolbar + if versioning_version < "2": + self.assertTrue(self._button_exists("Edit", toolbar.toolbar)) + # Edit button should not be clickable + button = self._find_buttons("Edit", toolbar.toolbar) + self.assertTrue(button[0].disabled) + else: + self.assertFalse( + self._button_exists( + lambda button: button.name.endswith("Edit"), toolbar.toolbar + ) ) - ) - # Edit button should not be clickable - button = self._find_buttons( - lambda button: button.name.endswith("Edit"), toolbar.toolbar - ) - self.assertTrue(button[0].disabled) def test_add_edit_button(self): user = self.get_superuser() @@ -222,9 +225,12 @@ def test_add_edit_button(self): toolbar.populate() toolbar.post_template_populate() - self.assertTrue(self._button_exists("Edit", toolbar.toolbar)) - button = self._find_buttons("Edit", toolbar.toolbar) - self.assertTrue(button[0].disabled) + if versioning_version < "2": + self.assertTrue(self._button_exists("Edit", toolbar.toolbar)) + button = self._find_buttons("Edit", toolbar.toolbar) + self.assertTrue(button[0].disabled) + else: + self.assertFalse(self._button_exists("Edit", toolbar.toolbar)) def test_add_edit_button_without_toolbar_object(self): toolbar = self._get_toolbar(None) diff --git a/tests/test_handlers.py b/tests/test_handlers.py deleted file mode 100644 index de31e72a..00000000 --- a/tests/test_handlers.py +++ /dev/null @@ -1,44 +0,0 @@ -from unittest import skip - -from djangocms_moderation.contrib.moderation_forms.cms_plugins import ( - ModerationFormPlugin, -) -from djangocms_moderation.handlers import moderation_confirmation_form_submission -from djangocms_moderation.models import ConfirmationFormSubmission, ConfirmationPage - -from .utils.base import BaseTestCase - - -@skip("Confirmation page feature doesn't support 1.0.x yet") -class ModerationConfirmationFormSubmissionTest(BaseTestCase): - def setUp(self): - self.cp = ConfirmationPage.objects.create(name="Checklist Form") - self.role1.confirmation_page = self.cp - self.role1.save() - - def test_throws_exception_when_form_data_is_invalid(self): - with self.assertRaises(ValueError) as context: - moderation_confirmation_form_submission( - sender=ModerationFormPlugin, - page=self.pg1_version, - language="en", - user=self.user, - form_data=[{"label": "Question 1", "answer": "Yes"}], - ) - self.assertTrue( - "Each field dict should contain label and value keys." - in str(context.exception) - ) - - def test_creates_new_form_submission_when_form_data_is_valid(self): - moderation_confirmation_form_submission( - sender=ModerationFormPlugin, - page=self.pg1_version, - language="en", - user=self.user, - form_data=[{"label": "Question 1", "value": "Yes"}], - ) - result = ConfirmationFormSubmission.objects.filter( - request=self.moderation_request1 - ) - self.assertEqual(result.count(), 1) diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index 6f5dad2f..cfc07fa7 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -14,6 +14,7 @@ PlaceholderFactory, ) +from djangocms_moderation.helpers import is_obj_version_unlocked from djangocms_moderation.monkeypatch import _is_placeholder_review_unlocked from .utils.base import BaseTestCase, MockRequest @@ -41,8 +42,9 @@ def test_get_edit_link(self, mock_is_obj_review_locked): ) # We test that moderation check is called when getting an edit link self.assertTrue(mock_is_obj_review_locked.called) - # Edit link is inactive as `mock_is_obj_review_locked` is True - self.assertIn("inactive", edit_link) + # Edit link is removed as `mock_is_obj_review_locked` is True + self.assertEqual("", edit_link) + # self.assertIn("inactive", edit_link) @mock.patch("djangocms_moderation.monkeypatch.is_registered_for_moderation") @mock.patch("djangocms_moderation.monkeypatch.is_obj_review_locked") @@ -76,14 +78,19 @@ def test_get_archive_link(self, _mock): ), args=(version.pk,), ) - _mock.return_value = True - archive_link = self.version_admin._get_archive_link(version, self.mock_request) + from djangocms_versioning import __version__ as versioning_version + if versioning_version != "2.0.0": + archive_link = self.version_admin._get_archive_link(version, self.mock_request) + else: + # Bug in djangocms-verisoning 2.0.0: _get_archive_link does not call check_archive + # So we do it by hand + version.check_archive.as_bool(self.mock_request.user) + archive_link = "" # We test that moderation check is called when getting an edit link self.assertEqual(1, _mock.call_count) - # Edit link is inactive as `is_obj_review_locked` is True - self.assertIn("inactive", archive_link) - self.assertNotIn(archive_url, archive_link) + # Edit link is unavailable + self.assertEqual("", archive_link) _mock.return_value = None archive_link = self.version_admin._get_archive_link(version, self.mock_request) @@ -120,6 +127,7 @@ def test_get_moderation_link(self): draft_version = PageVersionFactory(created_by=self.user3) # Request has self.user, so the moderation link won't be displayed. # This is version lock in place + self.assertFalse(is_obj_version_unlocked(draft_version.content, self.user)) link = self.version_admin._get_moderation_link(draft_version, self.mock_request) self.assertEqual("", link) From 9916e643aff898c488a280a09ad13b5b7bc120f4 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 11:41:04 +0100 Subject: [PATCH 02/26] Update test settings --- djangocms_moderation/__init__.py | 1 - djangocms_moderation/admin.py | 13 +------------ tests/settings.py | 2 ++ tests/test_monkeypatch.py | 3 ++- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 9389d356..edc60b35 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,2 +1 @@ __version__ = "2.1.6" - diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index 90a6fef5..a9f1ed5c 100644 --- a/djangocms_moderation/admin.py +++ b/djangocms_moderation/admin.py @@ -16,7 +16,7 @@ from cms.toolbar.utils import get_object_preview_url from cms.utils.helpers import is_editable_model -from adminsortable2.admin import SortableInlineAdminMixin, SortableAdminMixin +from adminsortable2.admin import SortableAdminMixin, SortableInlineAdminMixin from treebeard.admin import TreeAdmin from . import constants, signals @@ -80,7 +80,6 @@ def show_user(self, obj): _name = obj.get_by_user_name() return gettext("By {user}").format(user=_name) - @admin.display( description=_("Form Submission") ) @@ -101,7 +100,6 @@ def form_submission(self, obj): '{}', url, obj.step_approved.role.name ) - def get_readonly_fields(self, request, obj=None): if obj.user_can_moderate(request.user) or obj.user_is_author(request.user): # Omit 'message' from readonly_fields when current user is a reviewer @@ -192,7 +190,6 @@ def list_display_actions(self, obj): "", "{}", ((action(obj),) for action in self.get_list_display_actions()) ) - def get_list_display_actions(self): actions = [] if conf.REQUEST_COMMENTS_ENABLED: @@ -272,7 +269,6 @@ def get_preview_link(self, obj): object_preview_url, ) - @admin.display( description=_('Reviewer') ) @@ -936,7 +932,6 @@ class Media: def get_author(self, obj): return obj.author_name - def get_changeform_initial_data(self, request): data = {"author": request.user} moderation_request_id = utils.extract_filter_param_from_changelist_url( @@ -1077,7 +1072,6 @@ def list_display_actions(self, obj): "", "{}", ((action(obj),) for action in self.get_list_display_actions()) ) - def get_list_display_actions(self): actions = [self.get_edit_link, self.get_requests_link] if conf.COLLECTION_COMMENTS_ENABLED: @@ -1217,14 +1211,12 @@ def change_view(self, request, object_id, form_url="", extra_context=None): def moderation_request(self, obj): return obj.moderation_request_id - @admin.display( description=_("By User") ) def show_user(self, obj): return obj.get_by_user_name() - @admin.display( description=_("Form Data") ) @@ -1238,6 +1230,3 @@ def form_data(self, obj): for d in data ), ) - - - diff --git a/tests/settings.py b/tests/settings.py index 239b606c..c5d164de 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -24,6 +24,7 @@ "djangocms_version_locking": None, "filer": None, "djangocms_moderation": None, + "djangocms_text_ckeditor": None, }, "DEFAULT_AUTO_FIELD": "django.db.models.AutoField", "DJANGOCMS_VERSIONING_LOCK_VERSIONS": True, @@ -32,6 +33,7 @@ from cms import __version__ as cms_version + if cms_version < "4.1.0": HELPER_SETTINGS["INSTALLED_APPS"].append("djangocms_version_locking") diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index cfc07fa7..4992a897 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -95,7 +95,8 @@ def test_get_archive_link(self, _mock): _mock.return_value = None archive_link = self.version_admin._get_archive_link(version, self.mock_request) # We test that moderation check is called when getting the link - self.assertEqual(2, _mock.call_count) + if versioning_version != "2.0.0": + self.assertEqual(2, _mock.call_count) # Archive link is active there as `get_active_moderation_request` is None self.assertNotIn("inactive", archive_link) self.assertIn(archive_url, archive_link) From 52eb8d5e1ba10bb5228b470371f7e17463724680 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 11:43:06 +0100 Subject: [PATCH 03/26] More flake8 linting --- tests/settings.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/settings.py b/tests/settings.py index c5d164de..d9a7fea6 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,3 +1,5 @@ +from cms import __version__ as cms_version + HELPER_SETTINGS = { "SECRET_KEY": "moderationtestsuitekey", "INSTALLED_APPS": [ @@ -31,9 +33,6 @@ "CMS_CONFIRM_VERSION4": True, } -from cms import __version__ as cms_version - - if cms_version < "4.1.0": HELPER_SETTINGS["INSTALLED_APPS"].append("djangocms_version_locking") From 1685a78a1b81d03f348b42aa29421c8f8b744bde Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 11:53:58 +0100 Subject: [PATCH 04/26] Fix test for django CMS 4.0 toolbar --- tests/test_cms_toolbars.py | 6 ++++-- tests/test_monkeypatch.py | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/test_cms_toolbars.py b/tests/test_cms_toolbars.py index 08838e80..18b8bcbf 100644 --- a/tests/test_cms_toolbars.py +++ b/tests/test_cms_toolbars.py @@ -190,9 +190,11 @@ def test_add_edit_button_with_version_lock(self): toolbar.post_template_populate() if versioning_version < "2": - self.assertTrue(self._button_exists("Edit", toolbar.toolbar)) + self.assertTrue( + self._button_exists(lambda button: button.name.endswith("Edit"), toolbar.toolbar) + ) # Edit button should not be clickable - button = self._find_buttons("Edit", toolbar.toolbar) + button = self._find_buttons(lambda button: button.name.endswith("Edit"), toolbar.toolbar) self.assertTrue(button[0].disabled) else: self.assertFalse( diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index 4992a897..29b73e43 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -6,6 +6,7 @@ from cms.models import PageContent from cms.models.fields import PlaceholderRelationField +from djangocms_versioning import __version__ as versioning_version from djangocms_versioning import versionables from djangocms_versioning.admin import VersionAdmin from djangocms_versioning.constants import DRAFT, PUBLISHED @@ -42,8 +43,12 @@ def test_get_edit_link(self, mock_is_obj_review_locked): ) # We test that moderation check is called when getting an edit link self.assertTrue(mock_is_obj_review_locked.called) - # Edit link is removed as `mock_is_obj_review_locked` is True - self.assertEqual("", edit_link) + if versioning_version < "2": + # Edit link is inactive as `mock_is_obj_review_locked` is True + self.assertIn("inactive", edit_link) + else: + # Edit link is removed as `mock_is_obj_review_locked` is True + self.assertEqual("", edit_link) # self.assertIn("inactive", edit_link) @mock.patch("djangocms_moderation.monkeypatch.is_registered_for_moderation") @@ -79,7 +84,6 @@ def test_get_archive_link(self, _mock): args=(version.pk,), ) _mock.return_value = True - from djangocms_versioning import __version__ as versioning_version if versioning_version != "2.0.0": archive_link = self.version_admin._get_archive_link(version, self.mock_request) else: @@ -89,8 +93,12 @@ def test_get_archive_link(self, _mock): archive_link = "" # We test that moderation check is called when getting an edit link self.assertEqual(1, _mock.call_count) - # Edit link is unavailable - self.assertEqual("", archive_link) + if versioning_version < "2": + # Edit link is inactive as `mock_is_obj_review_locked` is True + self.assertIn("inactive", archive_link) + else: + # Edit link is unavailable + self.assertEqual("", archive_link) _mock.return_value = None archive_link = self.version_admin._get_archive_link(version, self.mock_request) From 2980c128bede0c104cc02404f1cf242ada710c9e Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 11:55:31 +0100 Subject: [PATCH 05/26] add django 5.0 for tests --- .github/workflows/test.yml | 1 + tests/requirements/dj50_cms41.txt | 7 +++++++ tests/settings.py | 1 + tests/test_monkeypatch.py | 3 +-- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/requirements/dj50_cms41.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d468aea2..ecdc1921 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,7 @@ jobs: matrix: python-version: [ 3.9, "3.10", "3.11", "3.12" ] # latest release minus two requirements-file: [ + dj50_cms41.txt, dj42_cms41.txt, dj32_cms40.txt, ] diff --git a/tests/requirements/dj50_cms41.txt b/tests/requirements/dj50_cms41.txt new file mode 100644 index 00000000..53ea1d28 --- /dev/null +++ b/tests/requirements/dj50_cms41.txt @@ -0,0 +1,7 @@ +-r ./requirements_base.txt + +Django>=5.0,<5.1 +django-cms>=4.1,<4.2 + +djangocms-versioning>=2.0.0 +djangocms-alias>=2.0.0 diff --git a/tests/settings.py b/tests/settings.py index d9a7fea6..067e6670 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,5 +1,6 @@ from cms import __version__ as cms_version + HELPER_SETTINGS = { "SECRET_KEY": "moderationtestsuitekey", "INSTALLED_APPS": [ diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index 29b73e43..c6c512ed 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -6,8 +6,7 @@ from cms.models import PageContent from cms.models.fields import PlaceholderRelationField -from djangocms_versioning import __version__ as versioning_version -from djangocms_versioning import versionables +from djangocms_versioning import __version__ as versioning_version, versionables from djangocms_versioning.admin import VersionAdmin from djangocms_versioning.constants import DRAFT, PUBLISHED from djangocms_versioning.test_utils.factories import ( From e313846648d878250ba52e3c2bf11e5315427ecd Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 12:07:57 +0100 Subject: [PATCH 06/26] Add Django 4.2 plus CMS 4.0 for testing --- .github/workflows/test.yml | 3 ++- setup.py | 13 ++++++++++++- tests/requirements/dj42_cms40.txt | 12 ++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/requirements/dj42_cms40.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ecdc1921..631cf808 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,10 +10,11 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ 3.9, "3.10", "3.11", "3.12" ] # latest release minus two + python-version: [ "3.10", "3.11", "3.12" ] # latest release minus two requirements-file: [ dj50_cms41.txt, dj42_cms41.txt, + dj42_cms40.txt, dj32_cms40.txt, ] os: [ diff --git a/setup.py b/setup.py index 86e47922..8259e8e9 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ INSTALL_REQUIREMENTS = [ - "Django>=2.2", + "Django>=3.2", "django-cms", "django-sekizai>=0.7", "django-admin-sortable2>=0.6.4", @@ -22,7 +22,18 @@ description=djangocms_moderation.__doc__, long_description=open("README.rst").read(), classifiers=[ + 'Development Status :: 5 - Production/Stable', + "Programming Language :: Python", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Framework :: Django", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", + "Framework :: Django CMS", + "Framework :: Django CMS :: 4.0", + "Framework :: Django CMS :: 4.1", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt new file mode 100644 index 00000000..907416aa --- /dev/null +++ b/tests/requirements/dj42_cms40.txt @@ -0,0 +1,12 @@ +-r ./requirements_base.txt + +Django>=4.2,<5.0 + +django-admin-sortable2<2 +django_polymorphic + +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/django-cms/djangocms-text-ckeditor/tarball/support/4.0.x#egg=djangocms-text-ckeditor +https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning +https://github.com/FidelityInternational/djangocms-version-locking/tarball/1.2.0#egg=djangocms-version-locking +https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias From f3c31de60825a7bdd917df3ba1ef363065aed18b Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 12:13:43 +0100 Subject: [PATCH 07/26] Update test dependencies --- tests/requirements/dj42_cms40.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt index 907416aa..a6826f4d 100644 --- a/tests/requirements/dj42_cms40.txt +++ b/tests/requirements/dj42_cms40.txt @@ -2,11 +2,10 @@ Django>=4.2,<5.0 -django-admin-sortable2<2 +django-admin-sortable2 django_polymorphic https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms -https://github.com/django-cms/djangocms-text-ckeditor/tarball/support/4.0.x#egg=djangocms-text-ckeditor https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning https://github.com/FidelityInternational/djangocms-version-locking/tarball/1.2.0#egg=djangocms-version-locking https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias From 56811e0e926a9656bc5e427f18d90575a863efba Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 16:58:09 +0100 Subject: [PATCH 08/26] Undo tooling changes --- tests/requirements/requirements_base.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 6d9a0dbe..7f095b1f 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -8,7 +8,6 @@ django-app-helper factory-boy flake8 isort -ruff mock pyflakes>=2.1.1 python-dateutil>=2.4 From 310c13d87b7ab56f0a22b4d1290889a1605b07e2 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Thu, 14 Mar 2024 15:03:57 +0000 Subject: [PATCH 09/26] fix: fix check errors --- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 3 +++ tests/requirements/dj42_cms40.txt | 4 ++-- tests/requirements/requirements_base.txt | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fe6c95e4..5527c571 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - name: Install flake8 run: pip install --upgrade flake8 - name: Run flake8 @@ -29,7 +29,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: '3.10' - run: python -m pip install isort - name: isort uses: liskin/gh-problem-matcher-wrap@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 631cf808..fd4be451 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,9 @@ jobs: os: [ ubuntu-20.04, ] + exclude: + - requirements-file: [dj32_cms40.txt, dj42_cms40.txt] + python-version: "3.12" #cms40 not support py3.12 yet steps: - uses: actions/checkout@v4 diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt index a6826f4d..deb73e0f 100644 --- a/tests/requirements/dj42_cms40.txt +++ b/tests/requirements/dj42_cms40.txt @@ -6,6 +6,6 @@ django-admin-sortable2 django_polymorphic https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms -https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning -https://github.com/FidelityInternational/djangocms-version-locking/tarball/1.2.0#egg=djangocms-version-locking +https://github.com/django-cms/djangocms-versioning/tarball/support/django-cms-4.0.x#egg=djangocms-versioning +https://github.com/joshyu/djangocms-version-locking/tarball/feat/django-42-compatible#egg=djangocms-version-locking https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 7f095b1f..3d7e24f8 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -1,6 +1,6 @@ cachetools coverage -django-classy-tags +django-classy-tags>=0.7.2 django-filer django-sekizai django-simple-captcha From 57ee74c0c70573588cecc9bb58d20216c757c60a Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Thu, 14 Mar 2024 15:09:30 +0000 Subject: [PATCH 10/26] fix: test.yml --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd4be451..a1104efb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,10 @@ jobs: ubuntu-20.04, ] exclude: - - requirements-file: [dj32_cms40.txt, dj42_cms40.txt] + - requirements-file: [ + dj32_cms40.txt, + dj42_cms40.txt + ] python-version: "3.12" #cms40 not support py3.12 yet steps: From a2d1fa2695c02c1ff462f3786be1aa8f2928b960 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Thu, 14 Mar 2024 15:15:43 +0000 Subject: [PATCH 11/26] fix: test.yml 2 --- .github/workflows/test.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a1104efb..37217349 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,11 +21,10 @@ jobs: ubuntu-20.04, ] exclude: - - requirements-file: [ - dj32_cms40.txt, - dj42_cms40.txt - ] + - requirements-file: "dj42_cms40.txt" python-version: "3.12" #cms40 not support py3.12 yet + - requirements-file: "dj32_cms40.txt" + python-version: "3.12" steps: - uses: actions/checkout@v4 From 1c50da3c1629bbfd857077565488de4e7d2da287 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 01:22:42 +0000 Subject: [PATCH 12/26] fix: update pre-commit-config versions --- .pre-commit-config.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bcd3de1a..accaa9e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,38 +12,38 @@ repos: rev: v2.31.0 hooks: - id: pyupgrade - args: ["--py37-plus"] + args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade rev: '1.4.0' hooks: - id: django-upgrade - args: [--target-version, "2.2"] + args: [--target-version, "4.2"] - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 7.0.0 hooks: - id: flake8 - repo: https://github.com/asottile/yesqa - rev: v1.3.0 + rev: v1.5.0 hooks: - id: yesqa - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.5.0 hooks: - id: check-merge-conflict - id: debug-statements - id: mixed-line-ending - id: trailing-whitespace - +# upgrade the isort version to fix compatiable issue withe peotry: https://stackoverflow.com/questions/75269700/pre-commit-fails-to-install-isort-5-11-4-with-error-runtimeerror-the-poetry-co - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/codespell-project/codespell - rev: v2.1.0 + rev: v2.2.6 hooks: - id: codespell From ec35312eea0bee02ddc5ff10ace74e21715770d0 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 01:42:50 +0000 Subject: [PATCH 13/26] fix: update code styles using pre-commit run --- djangocms_moderation/admin.py | 2 +- djangocms_moderation/apps.py | 4 ++-- djangocms_moderation/backends.py | 2 +- djangocms_moderation/cms_toolbars.py | 4 ++-- djangocms_moderation/emails.py | 4 ++-- djangocms_moderation/handlers.py | 2 +- djangocms_moderation/helpers.py | 2 +- .../migrations/0001_initial.py | 6 +++--- .../migrations/0003_auto_20180903_1206.py | 2 +- .../migrations/0011_auto_20181008_1328.py | 2 +- djangocms_moderation/models.py | 10 +++++----- .../djangocms_moderation/css/actions.css | 10 +++++----- .../base_confirmation_build.html | 2 +- djangocms_moderation/utils.py | 2 +- docs/comment.rst | 2 +- docs/conf.py | 1 - docs/lock.rst | 6 +++--- docs/moderation_collection.rst | 12 +++++------ docs/overview.rst | 2 +- tests/test_admin_actions.py | 14 ++++++------- tests/test_app_registration.py | 2 +- tests/test_cms_toolbars.py | 6 +++--- tests/test_forms.py | 4 ++-- tests/test_helpers.py | 11 +++++----- tests/test_integration.py | 2 +- tests/test_models.py | 4 ++-- tests/test_moderation_flows.py | 2 +- tests/test_monkeypatch.py | 2 +- tests/test_views.py | 20 +++++++++---------- tests/utils/factories.py | 2 +- tests/utils/moderated_polls/models.py | 2 +- .../versioned_none_moderated_app/models.py | 2 +- 32 files changed, 74 insertions(+), 76 deletions(-) diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index a9f1ed5c..f9320fa2 100644 --- a/djangocms_moderation/admin.py +++ b/djangocms_moderation/admin.py @@ -93,7 +93,7 @@ def form_submission(self, obj): opts = ConfirmationFormSubmission._meta url = reverse( - "admin:{}_{}_change".format(opts.app_label, opts.model_name), + f"admin:{opts.app_label}_{opts.model_name}_change", args=[instance.pk], ) return format_html( diff --git a/djangocms_moderation/apps.py b/djangocms_moderation/apps.py index cbeed482..b2b7dbf1 100644 --- a/djangocms_moderation/apps.py +++ b/djangocms_moderation/apps.py @@ -7,6 +7,6 @@ class ModerationConfig(AppConfig): verbose_name = _("django CMS Moderation") def ready(self): - import djangocms_moderation.handlers # noqa: F401 - import djangocms_moderation.monkeypatch # noqa: F401 + import djangocms_moderation.handlers + import djangocms_moderation.monkeypatch import djangocms_moderation.signals # noqa: F401 diff --git a/djangocms_moderation/backends.py b/djangocms_moderation/backends.py index 416606c0..565e9390 100644 --- a/djangocms_moderation/backends.py +++ b/djangocms_moderation/backends.py @@ -20,4 +20,4 @@ def sequential_number_with_identifier_prefix_backend(**kwargs): semi-sequential numbers, prefixed with `workflow.identifier` field, if set """ moderation_request = kwargs["moderation_request"] - return "{}{}".format(moderation_request.workflow.identifier, moderation_request.pk) + return f"{moderation_request.workflow.identifier}{moderation_request.pk}" diff --git a/djangocms_moderation/cms_toolbars.py b/djangocms_moderation/cms_toolbars.py index 6181cfb7..7679807a 100644 --- a/djangocms_moderation/cms_toolbars.py +++ b/djangocms_moderation/cms_toolbars.py @@ -68,7 +68,7 @@ def _add_moderation_buttons(self): opts = ModerationRequest._meta codename = get_permission_codename("add", opts) if not self.request.user.has_perm( - "{app_label}.{codename}".format(app_label=opts.app_label, codename=codename) + f"{opts.app_label}.{codename}" ): return version = Version.objects.get_for_content(self.toolbar.obj) @@ -91,7 +91,7 @@ def _add_moderation_menu(self): opts = ModerationCollection._meta codename = get_permission_codename("change", opts) if not self.request.user.has_perm( - "{app_label}.{codename}".format(app_label=opts.app_label, codename=codename) + f"{opts.app_label}.{codename}" ): return admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER) diff --git a/djangocms_moderation/emails.py b/djangocms_moderation/emails.py index 319a53e6..761c3b5b 100644 --- a/djangocms_moderation/emails.py +++ b/djangocms_moderation/emails.py @@ -35,7 +35,7 @@ def _send_email( "job_id": collection.job_id, "by_user": by_user, } - template = "djangocms_moderation/emails/moderation-request/{}".format(template) + template = f"djangocms_moderation/emails/moderation-request/{template}" # TODO What language should the email be sent in? e.g. `with force_language(lang):` subject = force_str(subject) @@ -61,7 +61,7 @@ def notify_collection_author(collection, moderation_requests, action, by_user): moderation_requests=moderation_requests, recipients=[collection.author.email], subject=email_subjects[action], - template="{}.txt".format(action), + template=f"{action}.txt", by_user=by_user, ) return status diff --git a/djangocms_moderation/handlers.py b/djangocms_moderation/handlers.py index 768ba335..50a9f9e4 100644 --- a/djangocms_moderation/handlers.py +++ b/djangocms_moderation/handlers.py @@ -11,7 +11,7 @@ def moderation_confirmation_form_submission( sender, page, language, user, form_data, **kwargs ): for field_data in form_data: - if not set(("label", "value")).issubset(field_data): + if not {"label", "value"}.issubset(field_data): raise ValueError("Each field dict should contain label and value keys.") # TODO Confirmation pages are not used/working in 1.0.x yet diff --git a/djangocms_moderation/helpers.py b/djangocms_moderation/helpers.py index b7fdcf03..d266f7cd 100644 --- a/djangocms_moderation/helpers.py +++ b/djangocms_moderation/helpers.py @@ -79,7 +79,7 @@ def get_active_moderation_request(content_object): If this returns None, it means there is no active_moderation request for this object, and it means it can be submitted for new moderation """ - from djangocms_moderation.models import ModerationRequest # noqa + from djangocms_moderation.models import ModerationRequest version = Version.objects.get_for_content(content_object) diff --git a/djangocms_moderation/migrations/0001_initial.py b/djangocms_moderation/migrations/0001_initial.py index b1464ba4..0efd9497 100644 --- a/djangocms_moderation/migrations/0001_initial.py +++ b/djangocms_moderation/migrations/0001_initial.py @@ -454,14 +454,14 @@ class Migration(migrations.Migration): ), ), migrations.AlterUniqueTogether( - name="workflowstep", unique_together=set([("role", "workflow")]) + name="workflowstep", unique_together={("role", "workflow")} ), migrations.AlterUniqueTogether( name="moderationrequest", - unique_together=set([("collection", "object_id", "content_type")]), + unique_together={("collection", "object_id", "content_type")}, ), migrations.AlterUniqueTogether( name="confirmationformsubmission", - unique_together=set([("request", "for_step")]), + unique_together={("request", "for_step")}, ), ] diff --git a/djangocms_moderation/migrations/0003_auto_20180903_1206.py b/djangocms_moderation/migrations/0003_auto_20180903_1206.py index 8bbc691c..46e11fa2 100644 --- a/djangocms_moderation/migrations/0003_auto_20180903_1206.py +++ b/djangocms_moderation/migrations/0003_auto_20180903_1206.py @@ -22,7 +22,7 @@ class Migration(migrations.Migration): preserve_default=False, ), migrations.AlterUniqueTogether( - name="moderationrequest", unique_together=set([("collection", "version")]) + name="moderationrequest", unique_together={("collection", "version")} ), migrations.RemoveField(model_name="moderationrequest", name="content_type"), migrations.RemoveField(model_name="moderationrequest", name="object_id"), diff --git a/djangocms_moderation/migrations/0011_auto_20181008_1328.py b/djangocms_moderation/migrations/0011_auto_20181008_1328.py index 23829d74..db52ec3a 100644 --- a/djangocms_moderation/migrations/0011_auto_20181008_1328.py +++ b/djangocms_moderation/migrations/0011_auto_20181008_1328.py @@ -13,7 +13,7 @@ class Migration(migrations.Migration): # AlterUniqueTogether needs to happen before removing field migrations.AlterUniqueTogether( name="confirmationformsubmission", - unique_together=set([("moderation_request", "for_step")]), + unique_together={("moderation_request", "for_step")}, ), migrations.RemoveField(model_name="confirmationformsubmission", name="request"), migrations.RemoveField(model_name="moderationrequestaction", name="request"), diff --git a/djangocms_moderation/models.py b/djangocms_moderation/models.py index 166daf58..1dbcc854 100644 --- a/djangocms_moderation/models.py +++ b/djangocms_moderation/models.py @@ -241,7 +241,7 @@ def __str__(self): @property def job_id(self): - return "{}".format(self.pk) + return f"{self.pk}" @property def author_name(self): @@ -445,7 +445,7 @@ class Meta: ordering = ["id"] def __str__(self): - return "{} {}".format(self.pk, self.version_id) + return f"{self.pk} {self.version_id}" @cached_property def workflow(self): @@ -652,7 +652,7 @@ class Meta: verbose_name_plural = _("Actions") def __str__(self): - return "{} - {}".format(self.moderation_request_id, self.get_action_display()) + return f"{self.moderation_request_id} - {self.get_action_display()}" def get_by_user_name(self): if not self.to_user: @@ -687,7 +687,7 @@ def save(self, **kwargs): if next_step: self.to_role_id = next_step.role_id - super(ModerationRequestAction, self).save(**kwargs) + super().save(**kwargs) class AbstractComment(models.Model): @@ -744,7 +744,7 @@ class ConfirmationFormSubmission(models.Model): ) def __str__(self): - return "{} - {}".format(self.request_id, self.for_step) + return f"{self.request_id} - {self.for_step}" class Meta: verbose_name = _("Confirmation Form Submission") diff --git a/djangocms_moderation/static/djangocms_moderation/css/actions.css b/djangocms_moderation/static/djangocms_moderation/css/actions.css index e86c02d9..d1c2c989 100644 --- a/djangocms_moderation/static/djangocms_moderation/css/actions.css +++ b/djangocms_moderation/static/djangocms_moderation/css/actions.css @@ -33,9 +33,9 @@ a.btn.cms-moderation-action-btn span { display: inline-block; } span.svg-juxtaposed-font { - text-rendering: auto; - display: inline-block; - line-height: 1rem; - vertical-align: 20%; - margin-top: -2px !important; + text-rendering: auto; + display: inline-block; + line-height: 1rem; + vertical-align: 20%; + margin-top: -2px !important; } \ No newline at end of file diff --git a/djangocms_moderation/templates/djangocms_moderation/base_confirmation_build.html b/djangocms_moderation/templates/djangocms_moderation/base_confirmation_build.html index 89b3eab2..cc55a8dd 100644 --- a/djangocms_moderation/templates/djangocms_moderation/base_confirmation_build.html +++ b/djangocms_moderation/templates/djangocms_moderation/base_confirmation_build.html @@ -16,7 +16,7 @@ {% block post_content %}{% endblock %} - + {% render_block "js" %} {% block extrajs %}{% endblock %} diff --git a/djangocms_moderation/utils.py b/djangocms_moderation/utils.py index 1f29c242..cc19722b 100644 --- a/djangocms_moderation/utils.py +++ b/djangocms_moderation/utils.py @@ -17,7 +17,7 @@ def get_absolute_url(location, site=None): scheme = "https" else: scheme = "http" - domain = "{}://{}".format(scheme, site.domain) + domain = f"{scheme}://{site.domain}" return urljoin(domain, location) diff --git a/docs/comment.rst b/docs/comment.rst index 8bdc8c25..d9959034 100644 --- a/docs/comment.rst +++ b/docs/comment.rst @@ -2,7 +2,7 @@ Comment ================================================ -Comments may be added to various moderation entities: +Comments may be added to various moderation entities: * :ref:`moderation_collection` * :ref:`moderation_request` * :ref:`moderation_request_action` \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 1273cc40..6e6aa86e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # diff --git a/docs/lock.rst b/docs/lock.rst index 06e7a0b9..05553d0f 100644 --- a/docs/lock.rst +++ b/docs/lock.rst @@ -2,8 +2,8 @@ Moderation Review Lock ================================================ -As soon as a :ref:`moderation_collection` status becomes in review then its drafts are automatically locked, in the sense that their content can no longer be edited (not at all, not by anyone, not even the collection author). Also once a collection is in Review then content versions cannot be added to the collection. This means that once you’ve clicked “Submit for review”: +As soon as a :ref:`moderation_collection` status becomes in review then its drafts are automatically locked, in the sense that their content can no longer be edited (not at all, not by anyone, not even the collection author). Also once a collection is in Review then content versions cannot be added to the collection. This means that once you’ve clicked “Submit for review”: * Collection Lock: New drafts cannot be added to the :ref:`moderation_collection` * Version Lock: Drafts in the :ref:`moderation_collection` cannot be edited unless rejected - -Once a version is published the Moderation Version Lock is removed automatically. \ No newline at end of file + +Once a version is published the Moderation Version Lock is removed automatically. \ No newline at end of file diff --git a/docs/moderation_collection.rst b/docs/moderation_collection.rst index cdddf4fc..4a3cc12a 100644 --- a/docs/moderation_collection.rst +++ b/docs/moderation_collection.rst @@ -3,10 +3,10 @@ Moderation Collection ================================================ -A Moderation Collection is primarily intended as a way of being able to group draft content versions together for: -a) review and +A Moderation Collection is primarily intended as a way of being able to group draft content versions together for: +a) review and b) publishing - + The rules for adding items to a Collection, removing items from a Collection and the actions that can be taken on items the Collection may vary by :ref:`workflow`. Publishing is a `djangocms-versioning` feature, thus `djangocms-moderation` depends on and extends the functionality made available by the Versioning addon. @@ -16,7 +16,7 @@ Collections are stateful. The available states are: * In review * Archived * Cancelled - + Drafts can only be added to a Collection during the `Collecting` phase (see :ref:`lock`) Buttons @@ -76,7 +76,7 @@ A collection can also be flagged as cancelled. This is similar to Archived excep Bulk Actions ------------------------------------------------- -These will appear in the Collection’s action drop-down for each content-type registered with Moderation. +These will appear in the Collection’s action drop-down for each content-type registered with Moderation. Remove from collection ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,4 +92,4 @@ Flags a draft as being in need of further editing Submit for review ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Useful for items that have been flagged for rework - resubmits them for review, sending out notifications again. +Useful for items that have been flagged for rework - resubmits them for review, sending out notifications again. diff --git a/docs/overview.rst b/docs/overview.rst index 57207d65..806bfec9 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -5,7 +5,7 @@ Overview Moderation provides an approval workflow mechanism for organisations who need to ensure that content is approved before it is published. It is designed to extend and compliment the Versioning addon and has that as a dependency. -The general idea is that a draft version can be submitted for moderation. This involves adding that draft to a :ref:`moderation_collection`, which can be thought of as a chapter, edition or batch of content that aims to all be published simultaneously. Various drafts can be added to the same :ref:`moderation_collection`. +The general idea is that a draft version can be submitted for moderation. This involves adding that draft to a :ref:`moderation_collection`, which can be thought of as a chapter, edition or batch of content that aims to all be published simultaneously. Various drafts can be added to the same :ref:`moderation_collection`. Drafts within the :ref:`moderation_collection` can then be approved rejected by various parties according to :ref:`role`s defined within the :ref:`workflow` assigned to the :ref:`moderation_collection`. diff --git a/tests/test_admin_actions.py b/tests/test_admin_actions.py index b0c3fbd8..c026b84d 100644 --- a/tests/test_admin_actions.py +++ b/tests/test_admin_actions.py @@ -1,5 +1,5 @@ -import mock import unittest +from unittest import mock from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.auth.models import Group @@ -66,7 +66,7 @@ def setUp(self): # Set up the url data self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" # Asserts to check data set up is ok. Ideally wouldn't need them, but # the set up is so complex that it's safer to have them. @@ -409,7 +409,7 @@ def setUp(self): # Set up the url data self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" # Asserts to check data set up is ok. Ideally wouldn't need them, but # the set up is so complex that it's safer to have them. @@ -586,7 +586,7 @@ def setUp(self): # Set up the url data self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" # Asserts to check data set up is ok. Ideally wouldn't need them, but # the set up is so complex that it's safer to have them. @@ -813,7 +813,7 @@ def setUp(self): self.client.force_login(self.user) self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" # Asserts to check data set up is ok. Ideally wouldn't need them, but # the set up is so complex that it's safer to have them. @@ -963,7 +963,7 @@ def setUp(self): id=6, moderation_request=self.moderation_request2) self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" @mock.patch.object(ModerationRequestTreeAdmin, "has_delete_permission", mock.Mock(return_value=True)) def test_delete_selected_action_cannot_be_accessed_if_not_collection_author(self): @@ -1112,7 +1112,7 @@ def setUp(self): # Generate url and POST data self.url = reverse("admin:djangocms_moderation_moderationrequesttreenode_changelist") - self.url += "?moderation_request__collection__id={}".format(self.collection.pk) + self.url += f"?moderation_request__collection__id={self.collection.pk}" self.data = get_url_data(self, "delete_selected") def tearDown(self): diff --git a/tests/test_app_registration.py b/tests/test_app_registration.py index 1b2423dc..eb28f94a 100644 --- a/tests/test_app_registration.py +++ b/tests/test_app_registration.py @@ -1,7 +1,7 @@ try: from unittest.mock import Mock except ImportError: - from mock import Mock + from unittest.mock import Mock from unittest import TestCase, skip from unittest.mock import patch diff --git a/tests/test_cms_toolbars.py b/tests/test_cms_toolbars.py index 18b8bcbf..7b539ae8 100644 --- a/tests/test_cms_toolbars.py +++ b/tests/test_cms_toolbars.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from django.contrib.auth.models import Permission, User from django.test.client import RequestFactory @@ -141,7 +141,7 @@ def test_page_in_collection_collection(self): self.assertTrue( self._button_exists( - 'In collection "{} ({})"'.format(collection.name, collection.id), + f'In collection "{collection.name} ({collection.id})"', toolbar.toolbar, ) ) @@ -159,7 +159,7 @@ def test_page_in_collection_moderating(self): self.assertTrue( self._button_exists( - 'In moderation "{} ({})"'.format(collection.name, collection.id), + f'In moderation "{collection.name} ({collection.id})"', toolbar.toolbar, ) ) diff --git a/tests/test_forms.py b/tests/test_forms.py index 571e591f..4439508a 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from django.contrib.auth.models import User from django.forms import HiddenInput @@ -248,7 +248,7 @@ def test_collection_choice_should_be_limited_to_current_user_and_collecting_stat form = CollectionItemsForm(data=data, user=user) self.assertEqual( - form.is_valid(), fixture[1], "{} failed".format(fixture[0]) + form.is_valid(), fixture[1], f"{fixture[0]} failed" ) if not form.is_valid(): self.assertIn("collection", form.errors) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index ebdacf10..40cc4777 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -1,6 +1,5 @@ import json -import mock -from unittest import skip +from unittest import mock, skip from django.template.defaultfilters import truncatechars from django.urls import reverse @@ -115,7 +114,7 @@ def setUp(self): def test_get_moderation_button_title_and_url_when_collection(self): title, url = get_moderation_button_title_and_url(self.mr) - self.assertEqual(title, 'In collection "C1 ({})"'.format(self.collection.id)) + self.assertEqual(title, f'In collection "C1 ({self.collection.id})"') self.assertEqual(url, self.expected_url) def test_get_moderation_button_title_and_url_when_in_review(self): @@ -123,7 +122,7 @@ def test_get_moderation_button_title_and_url_when_in_review(self): self.collection.save() title, url = get_moderation_button_title_and_url(self.mr) - self.assertEqual(title, 'In moderation "C1 ({})"'.format(self.collection.id)) + self.assertEqual(title, f'In moderation "C1 ({self.collection.id})"') self.assertEqual(url, self.expected_url) def test_get_moderation_button_truncated_title_and_url(self): @@ -135,7 +134,7 @@ def test_get_moderation_button_truncated_title_and_url(self): self.assertEqual( title, # By default, truncate will shorten the name - 'In collection "{} ({})"'.format(expected_title, self.collection.id), + f'In collection "{expected_title} ({self.collection.id})"', ) with mock.patch("djangocms_moderation.helpers.COLLECTION_NAME_LENGTH_LIMIT", 3): title, url = get_moderation_button_title_and_url(self.mr) @@ -143,7 +142,7 @@ def test_get_moderation_button_truncated_title_and_url(self): self.assertEqual( title, # As the limit is only 3, the truncate will produce `...` - 'In collection "{} ({})"'.format(expected_title, self.collection.id), + f'In collection "{expected_title} ({self.collection.id})"', ) with mock.patch( diff --git a/tests/test_integration.py b/tests/test_integration.py index c0d1b01f..9110714e 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from djangocms_versioning.test_utils.factories import PageVersionFactory diff --git a/tests/test_models.py b/tests/test_models.py index b00b6bfa..53d48d5d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from mock import patch +from unittest.mock import patch from django.contrib.auth.models import Permission, User from django.core.exceptions import ValidationError @@ -390,7 +390,7 @@ def test_compliance_number_sequential_number_with_identifier_prefix_backend(self request.refresh_from_db() self.assertIsNone(request.compliance_number) - expected = "SSO{}".format(request.pk) + expected = f"SSO{request.pk}" request.set_compliance_number() request.refresh_from_db() self.assertEqual(request.compliance_number, expected) diff --git a/tests/test_moderation_flows.py b/tests/test_moderation_flows.py index 0531e02e..41fe54d1 100644 --- a/tests/test_moderation_flows.py +++ b/tests/test_moderation_flows.py @@ -53,7 +53,7 @@ def _process_moderation_request(self, user, action, message="Test message"): self.client.force_login(user) response = self.client.post( get_admin_url( - name="cms_moderation_{}_request".format(action), + name=f"cms_moderation_{action}_request", language="en", args=(self.page.pk, "en"), ), diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index c6c512ed..3817b0b6 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from django.contrib import admin from django.urls import reverse diff --git a/tests/test_views.py b/tests/test_views.py index b7ecee19..bfbd24f5 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,4 +1,4 @@ -import mock +from unittest import mock from django.contrib.admin.widgets import RelatedFieldWidgetWrapper from django.contrib.messages import get_messages @@ -629,7 +629,7 @@ def test_initial_form_values_when_collection_id_passed(self): pg_version = PageVersionFactory() poll_version = PollVersionFactory() self.url += "?collection_id=" + str(collection.pk) - self.url += "&version_ids={},{}".format(pg_version.pk, poll_version.pk) + self.url += f"&version_ids={pg_version.pk},{poll_version.pk}" response = self.client.get(self.url) @@ -648,7 +648,7 @@ def test_initial_form_values_when_collection_id_passed(self): def test_initial_form_values_when_collection_id_not_passed(self): pg_version = PageVersionFactory() poll_version = PollVersionFactory() - self.url += "?version_ids={},{}".format(pg_version.pk, poll_version.pk) + self.url += f"?version_ids={pg_version.pk},{poll_version.pk}" response = self.client.get(self.url) @@ -774,7 +774,7 @@ def test_tree_nodes_are_created(self): class SubmitCollectionForModerationViewTest(BaseViewTestCase): def setUp(self): - super(SubmitCollectionForModerationViewTest, self).setUp() + super().setUp() self.url = reverse( "admin:cms_moderation_submit_collection_for_moderation", args=(self.collection2.pk,), @@ -820,7 +820,7 @@ def test_submit_collection_for_moderation(self, cancel_mock): class ModerationRequestChangeListView(BaseViewTestCase): def setUp(self): - super(ModerationRequestChangeListView, self).setUp() + super().setUp() self.collection_submit_url = reverse( "admin:cms_moderation_submit_collection_for_moderation", args=(self.collection2.pk,), @@ -1038,7 +1038,7 @@ def test_moderation_workflow_node_deletion_1(self): # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') - changelist_url += "?moderation_request__collection__id={}".format(self.collection.pk) + changelist_url += f"?moderation_request__collection__id={self.collection.pk}" response = self.client.get(changelist_url) self.assertEqual(response.status_code, 200) @@ -1093,7 +1093,7 @@ def test_moderation_workflow_node_deletion_2(self): # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') - changelist_url += "?moderation_request__collection__id={}".format(self.collection.pk) + changelist_url += f"?moderation_request__collection__id={self.collection.pk}" response = self.client.get(changelist_url) self.assertEqual(response.status_code, 200) @@ -1149,7 +1149,7 @@ def test_moderation_workflow_node_deletion_3(self): # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') - changelist_url += "?moderation_request__collection__id={}".format(self.collection.pk) + changelist_url += f"?moderation_request__collection__id={self.collection.pk}" response = self.client.get(changelist_url) self.assertEqual(response.status_code, 200) @@ -1209,7 +1209,7 @@ def test_moderation_workflow_node_deletion_4(self): # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') - changelist_url += "?moderation_request__collection__id={}".format(self.collection.pk) + changelist_url += f"?moderation_request__collection__id={self.collection.pk}" response = self.client.get(changelist_url) self.assertEqual(response.status_code, 200) @@ -1268,7 +1268,7 @@ def test_moderation_workflow_node_deletion_5(self): # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') - changelist_url += "?moderation_request__collection__id={}".format(self.collection.pk) + changelist_url += f"?moderation_request__collection__id={self.collection.pk}" response = self.client.get(changelist_url) self.assertEqual(response.status_code, 200) diff --git a/tests/utils/factories.py b/tests/utils/factories.py index 811f0955..2c9c442b 100644 --- a/tests/utils/factories.py +++ b/tests/utils/factories.py @@ -134,7 +134,7 @@ class UserFactory(DjangoModelFactory): first_name = factory.Faker("first_name") last_name = factory.Faker("last_name") email = factory.LazyAttribute( - lambda u: "%s.%s@example.com" % (u.first_name.lower(), u.last_name.lower()) + lambda u: f"{u.first_name.lower()}.{u.last_name.lower()}@example.com" ) class Meta: diff --git a/tests/utils/moderated_polls/models.py b/tests/utils/moderated_polls/models.py index 81147b54..eb0785ba 100644 --- a/tests/utils/moderated_polls/models.py +++ b/tests/utils/moderated_polls/models.py @@ -10,7 +10,7 @@ class Poll(models.Model): name = models.TextField() def __str__(self): - return "{} ({})".format(self.name, self.pk) + return f"{self.name} ({self.pk})" class PollContent(models.Model): diff --git a/tests/utils/versioned_none_moderated_app/models.py b/tests/utils/versioned_none_moderated_app/models.py index 674ff192..dafffb62 100644 --- a/tests/utils/versioned_none_moderated_app/models.py +++ b/tests/utils/versioned_none_moderated_app/models.py @@ -9,7 +9,7 @@ class NoneModeratedPoll(models.Model): name = models.TextField() def __str__(self): - return "{} ({})".format(self.name, self.pk) + return f"{self.name} ({self.pk})" class NoneModeratedPollContent(models.Model): From 3a3dd395405911954ce494a63b2145d90fb43789 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 06:24:11 +0000 Subject: [PATCH 14/26] fix: test_views.py --- tests/test_views.py | 414 ++++++++++++++++++++++++++++---------------- 1 file changed, 262 insertions(+), 152 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index bfbd24f5..984d9f08 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -5,6 +5,7 @@ from django.test import TransactionTestCase from django.urls import reverse +from cms import __version__ as cms_version from cms.test_utils.testcases import CMSTestCase from cms.utils.urlutils import add_url_parameters, admin_reverse @@ -237,7 +238,11 @@ def test_add_pages_moderated_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - self.assertEqual(stored_collection.count(), 3) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + self.assertEqual(stored_collection.count(), 1) + else: + self.assertEqual(stored_collection.count(), 3) mr = ModerationRequest.objects.filter( collection=collection, version=page_version ) @@ -248,17 +253,24 @@ def test_add_pages_moderated_children_to_collection(self): mr1 = ModerationRequest.objects.filter( collection=collection, version=poll1_version ) - self.assertEqual(mr1.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request=mr1.first()).count(), 1 - ) mr2 = ModerationRequest.objects.filter( collection=collection, version=poll2_version ) - self.assertEqual(mr2.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request=mr2.first()).count(), 1 - ) + + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + #poll1_version is locked, so will not be added to collection + self.assertEqual(mr1.count(), 0) + self.assertEqual(mr2.count(), 0) + else: + self.assertEqual(mr1.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request=mr1.first()).count(), 1 + ) + self.assertEqual(mr2.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request=mr2.first()).count(), 1 + ) def test_add_pages_moderated_duplicated_children_to_collection(self): """ @@ -303,13 +315,19 @@ def test_add_pages_moderated_duplicated_children_to_collection(self): ).count(), 1, ) - self.assertEqual(stored_collection.filter(version=poll_version).count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_version) - ).count(), - 1, - ) + mr = stored_collection.filter(version=poll_version) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + #poll1_version is locked, so will not be added to collection + self.assertEqual(mr.count(), 0) + else: + self.assertEqual(mr.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_version) + ).count(), + 1, + ) def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( self @@ -349,7 +367,12 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - self.assertEqual(stored_collection.count(), 2) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + self.assertEqual(stored_collection.count(), 1) + else: + self.assertEqual(stored_collection.count(), 2) + self.assertEqual(stored_collection.filter(version=page_version).count(), 1) self.assertEqual( ModerationRequestTreeNode.objects.filter( @@ -357,13 +380,20 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( ).count(), 1, ) - self.assertEqual(stored_collection.filter(version=poll1_version).count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll1_version) - ).count(), - 1, - ) + mr1 = stored_collection.filter(version=poll1_version) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + #poll1_version is locked, so will not be added to collection + self.assertEqual(mr1.count(), 0) + else: + self.assertEqual(mr1.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll1_version) + ).count(), + 1, + ) + self.assertEqual(stored_collection.filter(version=poll2_version).count(), 0) self.assertEqual( ModerationRequestTreeNode.objects.filter( @@ -423,7 +453,12 @@ def test_add_pages_moderated_traversed_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - self.assertEqual(stored_collection.count(), 4) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + self.assertEqual(stored_collection.count(), 1) + else: + self.assertEqual(stored_collection.count(), 4) + self.assertEqual(stored_collection.filter(version=page_version).count(), 1) self.assertEqual( ModerationRequestTreeNode.objects.filter( @@ -431,31 +466,44 @@ def test_add_pages_moderated_traversed_children_to_collection(self): ).count(), 1, ) - self.assertEqual(stored_collection.filter(version=poll_version).count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_version) - ).count(), - 1, - ) - self.assertEqual( - stored_collection.filter(version=poll_child_1_version).count(), 1 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_child_1_version) - ).count(), - 1, - ) - self.assertEqual( - stored_collection.filter(version=poll_child_2_version).count(), 1 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_child_2_version) - ).count(), - 1, - ) + + mr = stored_collection.filter(version=poll_version) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + #poll1_version is locked, so will not be added to collection + self.assertEqual(mr.count(), 0) + self.assertEqual( + stored_collection.filter(version=poll_child_1_version).count(), 0 + ) + self.assertEqual( + stored_collection.filter(version=poll_child_2_version).count(), 0 + ) + else: + self.assertEqual(mr.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_version) + ).count(), + 1, + ) + self.assertEqual( + stored_collection.filter(version=poll_child_1_version).count(), 1 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_child_1_version) + ).count(), + 1, + ) + self.assertEqual( + stored_collection.filter(version=poll_child_2_version).count(), 1 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_child_2_version) + ).count(), + 1, + ) def test_adding_non_page_item_doesnt_trigger_nested_collection_mechanism(self): user = self.get_superuser() @@ -748,28 +796,34 @@ def test_tree_nodes_are_created(self): ) # The correct amount of nodes exist - self.assertEqual(nodes.count(), 6) + if cms_version < "4.1.0": + self.assertEqual(nodes.count(), 1) + else: + self.assertEqual(nodes.count(), 6) # Now assert the tree structure... # Check root refers to correct version & has correct number of children root = ModerationRequestTreeNode.get_root_nodes().get() self.assertEqual(root.moderation_request.version, page_version) - self.assertEqual(root.get_children().count(), 2) - # Check first child of root has correct tree - poll_node = root.get_children().get(moderation_request__version=poll_version) - self.assertEqual(poll_node.get_children().count(), 1) - poll_child_node = poll_node.get_children().get() - self.assertEqual(poll_child_node.moderation_request.version, poll_child_version) - self.assertEqual(poll_child_node.get_children().count(), 1) - poll_grandchild_node = poll_child_node.get_children().get() - self.assertEqual(poll_grandchild_node.moderation_request.version, poll_grandchild_version) - # Check second child of root has correct tree - poll_child_node2 = root.get_children().get(moderation_request__version=poll_child_version) - self.assertNotEqual(poll_child_node, poll_child_node2) - self.assertEqual(poll_child_node2.moderation_request.version, poll_child_version) - self.assertEqual(poll_child_node2.get_children().count(), 1) - poll_grandchild_node2 = poll_child_node2.get_children().get() - self.assertNotEqual(poll_grandchild_node, poll_grandchild_node2) - self.assertEqual(poll_grandchild_node2.moderation_request.version, poll_grandchild_version) + if cms_version < "4.1.0": + self.assertEqual(root.get_children().count(), 0) + else: + self.assertEqual(root.get_children().count(), 2) + # Check first child of root has correct tree + poll_node = root.get_children().get(moderation_request__version=poll_version) + self.assertEqual(poll_node.get_children().count(), 1) + poll_child_node = poll_node.get_children().get() + self.assertEqual(poll_child_node.moderation_request.version, poll_child_version) + self.assertEqual(poll_child_node.get_children().count(), 1) + poll_grandchild_node = poll_child_node.get_children().get() + self.assertEqual(poll_grandchild_node.moderation_request.version, poll_grandchild_version) + # Check second child of root has correct tree + poll_child_node2 = root.get_children().get(moderation_request__version=poll_child_version) + self.assertNotEqual(poll_child_node, poll_child_node2) + self.assertEqual(poll_child_node2.moderation_request.version, poll_child_version) + self.assertEqual(poll_child_node2.get_children().count(), 1) + poll_grandchild_node2 = poll_child_node2.get_children().get() + self.assertNotEqual(poll_grandchild_node, poll_grandchild_node2) + self.assertEqual(poll_grandchild_node2.moderation_request.version, poll_grandchild_version) class SubmitCollectionForModerationViewTest(BaseViewTestCase): @@ -976,33 +1030,42 @@ def _add_pages_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) # The correct amount of moderation requests has been created - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 4 - ) - # The correct amount of tree nodes has been created - # Poll is repeated twice and will therefore have an additional node - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 5 - ) + mr = ModerationRequest.objects.filter(collection=self.collection) # The tree structure for page_1_version is correct - root_1 = ModerationRequestTreeNode.get_root_nodes().get( - moderation_request__version=self.page_1_version) - self.assertEqual(root_1.get_children().count(), 1) - child_1 = root_1.get_children().get() - self.assertEqual(child_1.moderation_request.version, self.poll_version) - self.assertEqual(child_1.get_children().count(), 1) - grandchild = child_1.get_children().get() - self.assertEqual( - grandchild.moderation_request.version, self.poll_child_version) - # The tree structure for page_2_version is correct - root_2 = ModerationRequestTreeNode.get_root_nodes().get( - moderation_request__version=self.page_2_version) - self.assertEqual(root_2.get_children().count(), 1) - child_2 = root_2.get_children().get() - self.assertEqual(child_2.moderation_request.version, self.poll_child_version) - self.assertEqual(grandchild.moderation_request, child_2.moderation_request) + root_1 = ModerationRequestTreeNode.get_root_nodes().get(moderation_request__version=self.page_1_version) + if cms_version < "4.1.0": + #version-locking override `_add_nested_children` add locked state checking + self.assertEqual(mr.count(), 2) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 2 + ) + self.assertEqual(root_1.get_children().count(), 0) + + else: + self.assertEqual(mr.count(), 4) + # The correct amount of tree nodes has been created + # Poll is repeated twice and will therefore have an additional node + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 5 + ) + self.assertEqual(root_1.get_children().count(), 1) + + child_1 = root_1.get_children().get() + self.assertEqual(child_1.moderation_request.version, self.poll_version) + self.assertEqual(child_1.get_children().count(), 1) + grandchild = child_1.get_children().get() + self.assertEqual( + grandchild.moderation_request.version, self.poll_child_version) + + # The tree structure for page_2_version is correct + root_2 = ModerationRequestTreeNode.get_root_nodes().get( + moderation_request__version=self.page_2_version) + self.assertEqual(root_2.get_children().count(), 1) + child_2 = root_2.get_children().get() + self.assertEqual(child_2.moderation_request.version, self.poll_child_version) + self.assertEqual(grandchild.moderation_request, child_2.moderation_request) def test_moderation_workflow_node_deletion_1(self): """ @@ -1046,19 +1109,33 @@ def test_moderation_workflow_node_deletion_1(self): # The whole of the page_2_version tree should have been removed. # Additionally, poll_child_version should have been removed from # the page_1_version tree. - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 2 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 2 - ) - self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) - root = ModerationRequestTreeNode.get_root_nodes().get() - self.assertEqual(root.moderation_request.version, self.page_1_version) - self.assertEqual(root.get_children().count(), 1) - self.assertEqual(root.get_children().get().moderation_request.version, self.poll_version) + if cms_version < "4.1.0": + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 1 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 1 + ) + self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) + root = ModerationRequestTreeNode.get_root_nodes().get() + self.assertEqual(root.moderation_request.version, self.page_1_version) + self.assertEqual(root.get_children().count(), 0) + else: + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 2 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 2 + ) + self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) + root = ModerationRequestTreeNode.get_root_nodes().get() + self.assertEqual(root.moderation_request.version, self.page_1_version) + self.assertEqual(root.get_children().count(), 1) + self.assertEqual(root.get_children().get().moderation_request.version, self.poll_version) def test_moderation_workflow_node_deletion_2(self): """ @@ -1138,14 +1215,16 @@ def test_moderation_workflow_node_deletion_3(self): # Now remove poll_version from the collection page_1_root = ModerationRequestTreeNode.get_root_nodes().get( moderation_request__version=self.page_1_version) - poll_1_node = page_1_root.get_children().get() - delete_url = "{}?ids={}&collection_id={}".format( - reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), - ",".join([str(poll_1_node.pk)]), - self.collection.pk, - ) - response = self.client.post(delete_url, follow=True) - self.assertEqual(response.status_code, 200) + page_1_root_children = page_1_root.get_children() + if page_1_root_children.count() > 0: + poll_1_node = page_1_root_children.get() + delete_url = "{}?ids={}&collection_id={}".format( + reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), + ",".join([str(poll_1_node.pk)]), + self.collection.pk, + ) + response = self.client.post(delete_url, follow=True) + self.assertEqual(response.status_code, 200) # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') @@ -1198,14 +1277,16 @@ def test_moderation_workflow_node_deletion_4(self): # Now remove poll_version from the collection page_1_root = ModerationRequestTreeNode.get_root_nodes().get( moderation_request__version=self.page_1_version) - poll_grandchild_node = page_1_root.get_children().get().get_children().get() - delete_url = "{}?ids={}&collection_id={}".format( - reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), - ",".join([str(poll_grandchild_node.pk)]), - self.collection.pk, - ) - response = self.client.post(delete_url, follow=True) - self.assertEqual(response.status_code, 200) + page_1_root_children = page_1_root.get_children() + if page_1_root_children.count() > 0: + poll_grandchild_node = page_1_root_children.get().get_children().get() + delete_url = "{}?ids={}&collection_id={}".format( + reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), + ",".join([str(poll_grandchild_node.pk)]), + self.collection.pk, + ) + response = self.client.post(delete_url, follow=True) + self.assertEqual(response.status_code, 200) # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') @@ -1215,22 +1296,34 @@ def test_moderation_workflow_node_deletion_4(self): # Check the data # Only the roots (page_1_version and page_2_version) should remain - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 3 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 3 - ) + if cms_version < "4.1.0": + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 2 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 2 + ) + else: + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 3 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 3 + ) self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 2) root_1 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_1_version).get() root_2 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_2_version).get() - self.assertEqual(root_1.get_children().count(), 1) + if cms_version < "4.1.0": + self.assertEqual(root_1.get_children().count(), 0) + else: + self.assertEqual(root_1.get_children().count(), 1) self.assertEqual(root_2.get_children().count(), 0) - self.assertEqual(root_1.get_children().get().moderation_request.version, self.poll_version) def test_moderation_workflow_node_deletion_5(self): """ @@ -1257,14 +1350,16 @@ def test_moderation_workflow_node_deletion_5(self): # Now remove poll_version from the collection page_2_root = ModerationRequestTreeNode.get_root_nodes().get( moderation_request__version=self.page_2_version) - poll_child_node = page_2_root.get_children().get() - delete_url = "{}?ids={}&collection_id={}".format( - reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), - ",".join([str(poll_child_node.pk)]), - self.collection.pk, - ) - response = self.client.post(delete_url, follow=True) - self.assertEqual(response.status_code, 200) + page_2_root_children = page_2_root.get_children() + if page_2_root_children.count() > 0: + poll_child_node = page_2_root_children.get() + delete_url = "{}?ids={}&collection_id={}".format( + reverse('admin:djangocms_moderation_moderationrequesttreenode_delete'), + ",".join([str(poll_child_node.pk)]), + self.collection.pk, + ) + response = self.client.post(delete_url, follow=True) + self.assertEqual(response.status_code, 200) # Load the changelist and check that the page loads without an error changelist_url = reverse('admin:djangocms_moderation_moderationrequesttreenode_changelist') @@ -1274,19 +1369,34 @@ def test_moderation_workflow_node_deletion_5(self): # Check the data # Only the roots (page_1_version and page_2_version) should remain - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 3 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 3 - ) + if cms_version < "4.1.0": + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 2 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 2 + ) + else: + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 3 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 3 + ) self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 2) root_1 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_1_version).get() root_2 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_2_version).get() - self.assertEqual(root_1.get_children().count(), 1) - self.assertEqual(root_2.get_children().count(), 0) - self.assertEqual(root_1.get_children().get().moderation_request.version, self.poll_version) + + if cms_version < "4.1.0": + self.assertEqual(root_1.get_children().count(), 0) + self.assertEqual(root_2.get_children().count(), 0) + else: + self.assertEqual(root_1.get_children().count(), 1) + self.assertEqual(root_1.get_children().get().moderation_request.version, self.poll_version) + self.assertEqual(root_2.get_children().count(), 0) From 185b43a41f0bbee3a9fbc22277106f41bb0cd2de Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 06:27:41 +0000 Subject: [PATCH 15/26] fix: flake8 issue --- tests/test_views.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index 984d9f08..7c05c417 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -239,7 +239,7 @@ def test_add_pages_moderated_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking + # version-locking override `_add_nested_children` add locked state checking self.assertEqual(stored_collection.count(), 1) else: self.assertEqual(stored_collection.count(), 3) @@ -258,8 +258,8 @@ def test_add_pages_moderated_children_to_collection(self): ) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking - #poll1_version is locked, so will not be added to collection + # version-locking override `_add_nested_children` add locked state checking + # poll1_version is locked, so will not be added to collection self.assertEqual(mr1.count(), 0) self.assertEqual(mr2.count(), 0) else: @@ -317,8 +317,8 @@ def test_add_pages_moderated_duplicated_children_to_collection(self): ) mr = stored_collection.filter(version=poll_version) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking - #poll1_version is locked, so will not be added to collection + # version-locking override `_add_nested_children` add locked state checking + # poll1_version is locked, so will not be added to collection self.assertEqual(mr.count(), 0) else: self.assertEqual(mr.count(), 1) @@ -368,11 +368,11 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking + # version-locking override `_add_nested_children` add locked state checking self.assertEqual(stored_collection.count(), 1) else: self.assertEqual(stored_collection.count(), 2) - + self.assertEqual(stored_collection.filter(version=page_version).count(), 1) self.assertEqual( ModerationRequestTreeNode.objects.filter( @@ -382,8 +382,8 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( ) mr1 = stored_collection.filter(version=poll1_version) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking - #poll1_version is locked, so will not be added to collection + # version-locking override `_add_nested_children` add locked state checking + # poll1_version is locked, so will not be added to collection self.assertEqual(mr1.count(), 0) else: self.assertEqual(mr1.count(), 1) @@ -454,7 +454,7 @@ def test_add_pages_moderated_traversed_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking + # version-locking override `_add_nested_children` add locked state checking self.assertEqual(stored_collection.count(), 1) else: self.assertEqual(stored_collection.count(), 4) @@ -469,8 +469,8 @@ def test_add_pages_moderated_traversed_children_to_collection(self): mr = stored_collection.filter(version=poll_version) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking - #poll1_version is locked, so will not be added to collection + # version-locking override `_add_nested_children` add locked state checking + # poll1_version is locked, so will not be added to collection self.assertEqual(mr.count(), 0) self.assertEqual( stored_collection.filter(version=poll_child_1_version).count(), 0 @@ -1034,7 +1034,7 @@ def _add_pages_to_collection(self): # The tree structure for page_1_version is correct root_1 = ModerationRequestTreeNode.get_root_nodes().get(moderation_request__version=self.page_1_version) if cms_version < "4.1.0": - #version-locking override `_add_nested_children` add locked state checking + # version-locking override `_add_nested_children` add locked state checking self.assertEqual(mr.count(), 2) self.assertEqual( ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), @@ -1392,7 +1392,7 @@ def test_moderation_workflow_node_deletion_5(self): moderation_request__version=self.page_1_version).get() root_2 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_2_version).get() - + if cms_version < "4.1.0": self.assertEqual(root_1.get_children().count(), 0) self.assertEqual(root_2.get_children().count(), 0) From 2e4c97e9e5da07d4cba1eb4168e83d0785190463 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 06:52:08 +0000 Subject: [PATCH 16/26] fix: pre-commit-config related errors --- .pre-commit-config.yaml | 2 +- tests/test_views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index accaa9e3..54562a19 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,6 +44,6 @@ repos: - id: isort - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.1.0 hooks: - id: codespell diff --git a/tests/test_views.py b/tests/test_views.py index 7c05c417..d4cd9464 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -602,7 +602,7 @@ def test_collection_with_redirect_url_query_redirect_sanitisation(self): """ Reflected XSS Protection by ensuring that harmful characters are encoded - When a collection is succesful a redirect occurs back to the grouper in versioning, + When a collection is successful a redirect occurs back to the grouper in versioning, this functionality should continue to function even when sanitised! """ user = self.get_superuser() From 877085dacb9b805bbf801a151073d9dfd9507714 Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 06:52:36 +0000 Subject: [PATCH 17/26] fix: pre-commit-config related errors --- .pre-commit-config.yaml | 6 +++++- djangocms_moderation/conf.py | 2 +- djangocms_moderation/migrations/0001_initial.py | 4 ++-- djangocms_moderation/migrations/0011_auto_20181008_1328.py | 2 -- djangocms_moderation/migrations/0014_auto_20190315_1723.py | 7 ++++++- .../migrations/0016_moderationrequesttreenode.py | 6 +++++- .../static/djangocms_moderation/js/libs/diffview.js | 2 +- 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54562a19..c0963489 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: rev: '1.4.0' hooks: - id: django-upgrade - args: [--target-version, "4.2"] + args: [--target-version, "4.0"] - repo: https://github.com/PyCQA/flake8 rev: 7.0.0 @@ -47,3 +47,7 @@ repos: rev: v2.1.0 hooks: - id: codespell + exclude: > + (?x)^( + .*\.(js|po) + )$ diff --git a/djangocms_moderation/conf.py b/djangocms_moderation/conf.py index e7008be7..26a3e5c5 100644 --- a/djangocms_moderation/conf.py +++ b/djangocms_moderation/conf.py @@ -9,7 +9,7 @@ ) CORE_COMPLIANCE_NUMBER_BACKENDS = ( - (UUID_BACKEND, _("Unique alpha-numeric string")), + (UUID_BACKEND, _("Unique alpha-npreumeric string")), (SEQUENTIAL_NUMBER_BACKEND, _("Sequential number")), ( SEQUENTIAL_NUMBER_WITH_IDENTIFIER_PREFIX_BACKEND, diff --git a/djangocms_moderation/migrations/0001_initial.py b/djangocms_moderation/migrations/0001_initial.py index 0efd9497..c714385e 100644 --- a/djangocms_moderation/migrations/0001_initial.py +++ b/djangocms_moderation/migrations/0001_initial.py @@ -311,7 +311,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, default="", - help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", + help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", # noqa: E501 max_length=128, verbose_name="identifier", ), @@ -320,7 +320,7 @@ class Migration(migrations.Migration): "requires_compliance_number", models.BooleanField( default=False, - help_text="Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below", + help_text="Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below", # noqa: E501 verbose_name="requires compliance number?", ), ), diff --git a/djangocms_moderation/migrations/0011_auto_20181008_1328.py b/djangocms_moderation/migrations/0011_auto_20181008_1328.py index db52ec3a..cca7ac2b 100644 --- a/djangocms_moderation/migrations/0011_auto_20181008_1328.py +++ b/djangocms_moderation/migrations/0011_auto_20181008_1328.py @@ -1,6 +1,4 @@ # Generated by Django 1.11.13 on 2018-10-08 12:28 -from django.conf import settings -from django.db import migrations from django.db import migrations, models import django.db.models.deletion diff --git a/djangocms_moderation/migrations/0014_auto_20190315_1723.py b/djangocms_moderation/migrations/0014_auto_20190315_1723.py index d420dc9c..6cc014da 100644 --- a/djangocms_moderation/migrations/0014_auto_20190315_1723.py +++ b/djangocms_moderation/migrations/0014_auto_20190315_1723.py @@ -11,6 +11,11 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='moderationcollection', - options={'permissions': (('can_change_author', 'Can change collection author'), ('cancel_moderationcollection', 'Can cancel collection')), 'verbose_name': 'collection'}, + options={'permissions': ( + ('can_change_author', 'Can change collection author'), + ('cancel_moderationcollection', 'Can cancel collection') + ), + 'verbose_name': 'collection' + }, ), ] diff --git a/djangocms_moderation/migrations/0016_moderationrequesttreenode.py b/djangocms_moderation/migrations/0016_moderationrequesttreenode.py index 592c7f89..6f37e42c 100644 --- a/djangocms_moderation/migrations/0016_moderationrequesttreenode.py +++ b/djangocms_moderation/migrations/0016_moderationrequesttreenode.py @@ -17,7 +17,11 @@ class Migration(migrations.Migration): ('path', models.CharField(max_length=255, unique=True)), ('depth', models.PositiveIntegerField()), ('numchild', models.PositiveIntegerField(default=0)), - ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.ModerationRequest', verbose_name='moderation_request')), + ('moderation_request', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='djangocms_moderation.ModerationRequest', + verbose_name='moderation_request') + ), # noqa: E124 ], options={ 'ordering': ('id',), diff --git a/djangocms_moderation/static/djangocms_moderation/js/libs/diffview.js b/djangocms_moderation/static/djangocms_moderation/js/libs/diffview.js index 65e2e411..999f8f6f 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/libs/diffview.js +++ b/djangocms_moderation/static/djangocms_moderation/js/libs/diffview.js @@ -177,7 +177,7 @@ var diffview = { var botrows = []; for (var i = 0; i < rowcnt; i++) { - // jump ahead if we've alredy provided leading context or if this is the first range + // jump ahead if we've already provided leading context or if this is the first range if ( contextSize && opcodes.length > 1 && From 66dc28a2d9aeab3fc4f499756c367a00b7010c1e Mon Sep 17 00:00:00 2001 From: Josh Peng Yu Date: Fri, 15 Mar 2024 07:12:28 +0000 Subject: [PATCH 18/26] fix: typo --- djangocms_moderation/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_moderation/conf.py b/djangocms_moderation/conf.py index 26a3e5c5..e051e5ef 100644 --- a/djangocms_moderation/conf.py +++ b/djangocms_moderation/conf.py @@ -9,7 +9,7 @@ ) CORE_COMPLIANCE_NUMBER_BACKENDS = ( - (UUID_BACKEND, _("Unique alpha-npreumeric string")), + (UUID_BACKEND, _("Unique alphanumeric string")), (SEQUENTIAL_NUMBER_BACKEND, _("Sequential number")), ( SEQUENTIAL_NUMBER_WITH_IDENTIFIER_PREFIX_BACKEND, From 9b3b5052dfa2626f282751378faef6d0fc41dc9c Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 08:24:17 +0100 Subject: [PATCH 19/26] Fix isort precommit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bcd3de1a..d725ccad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort From b0b9fbb264262608315d741997ae72468a7a3d73 Mon Sep 17 00:00:00 2001 From: Raffaella <45825990+raffaellasuardini@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:01:57 +0100 Subject: [PATCH 20/26] feat: added issue template (#261) --- .github/ISSUE_TEMPLATE/---bug-report.md | 65 +++++++++++++++++++ .../ISSUE_TEMPLATE/---documentation-report.md | 43 ++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/---bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/---documentation-report.md diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md new file mode 100644 index 00000000..1d5d21b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -0,0 +1,65 @@ +--- +name: "\U0001F41E Bug report" +about: Something isn't working as expected? Here is the right place to report. +title: "[BUG]" +labels: '' +assignees: '' + +--- + + + +## Description + + + +## Steps to reproduce + + + +## Expected behaviour + + + +## Actual behaviour + + + +## Screenshots + + + +## Additional information (CMS/Python/Django versions) + + + +## Do you want to help fix this issue? + + + +* [ ] Yes, I want to help fix this issue and I will join #workgroup-pr-review on [Slack](https://www.django-cms.org/slack) to confirm with the community that a PR is welcome. +* [ ] No, I only want to report the issue. diff --git a/.github/ISSUE_TEMPLATE/---documentation-report.md b/.github/ISSUE_TEMPLATE/---documentation-report.md new file mode 100644 index 00000000..2b599f7a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---documentation-report.md @@ -0,0 +1,43 @@ +--- +name: "\U0001F4D8 Documentation report" +about: "Something isn't described correctly in the documentation or needs to be updated? + Here is the right place to report." +title: "[DOC]" +labels: 'component: documentation' +assignees: '' + +--- + + + +## Description + + + +## Screenshots + + + +## Additional information (CMS/Python/Django versions) + + + +## Do you want to help fix this documentation issue? + + + +* [ ] Yes, I want to help fix this issue and I will join #workgroup-documentation on [Slack](https://www.django-cms.org/slack) to confirm with the team that a PR is welcome. +* [ ] No, I only want to report the issue. From 1bb3c1600f0d4bbe08350e956bf6cbc075a707d8 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 15:14:35 +0100 Subject: [PATCH 21/26] fix: Squash migrations to remove intermediate state with auto field = 0 (#259) --- .../0001_squashed_0017_auto_20220831_0727.py | 213 ++++++++++++++++++ .../migrations/0007_auto_20181002_1725.py | 2 +- .../migrations/0010_auto_20181008_1317.py | 2 +- 3 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py diff --git a/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py new file mode 100644 index 00000000..7ff51e57 --- /dev/null +++ b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py @@ -0,0 +1,213 @@ +# Generated by Django 3.2.25 on 2024-03-11 10:45 + +import cms.models.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + replaces = [('djangocms_moderation', '0001_initial'), ('djangocms_moderation', '0002_auto_20180905_1152'), ('djangocms_moderation', '0003_auto_20180903_1206'), ('djangocms_moderation', '0004_auto_20180907_1206'), ('djangocms_moderation', '0005_auto_20180919_1348'), ('djangocms_moderation', '0006_auto_20181001_1840'), ('djangocms_moderation', '0007_auto_20181002_1725'), ('djangocms_moderation', '0008_auto_20181002_1833'), ('djangocms_moderation', '0009_auto_20181005_1534'), ('djangocms_moderation', '0010_auto_20181008_1317'), ('djangocms_moderation', '0011_auto_20181008_1328'), ('djangocms_moderation', '0012_auto_20181016_1319'), ('djangocms_moderation', '0013_auto_20181122_1110'), ('djangocms_moderation', '0014_auto_20190313_1638'), ('djangocms_moderation', '0014_auto_20190315_1723'), ('djangocms_moderation', '0016_moderationrequesttreenode'), ('djangocms_moderation', '0017_auto_20220831_0727')] + + initial = True + + dependencies = [ + ('cms', '0028_remove_page_placeholders'), + ('djangocms_versioning', '0010_version_proxies'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0008_alter_user_username_max_length'), + ('contenttypes', '0002_remove_content_type_name'), + ('cms', '0020_old_tree_cleanup'), + ] + + operations = [ + migrations.CreateModel( + name='ConfirmationPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='name')), + ('content_type', models.CharField(choices=[('plain', 'Plain'), ('form', 'Form')], default='form', max_length=50, verbose_name='Content Type')), + ('template', models.CharField(choices=[('djangocms_moderation/moderation_confirmation.html', 'Default')], default='djangocms_moderation/moderation_confirmation.html', max_length=100, verbose_name='Template')), + ('content', cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, slotname='confirmation_content', to='cms.placeholder')), + ], + options={ + 'verbose_name': 'Confirmation Page', + 'verbose_name_plural': 'Confirmation Pages', + }, + ), + migrations.CreateModel( + name='ModerationCollection', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='collection name')), + ('status', models.CharField(choices=[('COLLECTING', 'Collecting'), ('IN_REVIEW', 'In Review'), ('ARCHIVED', 'Archived'), ('CANCELLED', 'Cancelled')], db_index=True, default='COLLECTING', max_length=10)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_modified', models.DateTimeField(auto_now=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ], + ), + migrations.CreateModel( + name='ModerationRequest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(choices=[('en', 'English'), ('de', 'German')], max_length=20, verbose_name='language')), + ('is_active', models.BooleanField(db_index=True, default=True, verbose_name='is active')), + ('date_sent', models.DateTimeField(auto_now_add=True, verbose_name='date sent')), + ('compliance_number', models.CharField(blank=True, editable=False, max_length=32, null=True, unique=True, verbose_name='compliance number')), + ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_requests', to='djangocms_moderation.moderationcollection')), + ('version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_versioning.version', verbose_name='version')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ], + options={ + 'verbose_name': 'Request', + 'verbose_name_plural': 'Requests', + 'ordering': ['id'], + 'unique_together': {('collection', 'version')}, + }, + ), + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=120, unique=True, verbose_name='name')), + ('confirmation_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='djangocms_moderation.confirmationpage', verbose_name='confirmation page')), + ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='group')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + 'verbose_name': 'Role', + 'verbose_name_plural': 'Roles', + }, + ), + migrations.CreateModel( + name='Workflow', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=120, unique=True, verbose_name='name')), + ('is_default', models.BooleanField(default=False, verbose_name='is default')), + ('identifier', models.CharField(blank=True, default='', help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", max_length=128, verbose_name='identifier')), + ('requires_compliance_number', models.BooleanField(default=False, help_text='Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below', verbose_name='requires compliance number?')), + ('compliance_number_backend', models.CharField(choices=[('djangocms_moderation.backends.uuid4_backend', 'Unique alpha-numeric string'), ('djangocms_moderation.backends.sequential_number_backend', 'Sequential number'), ('djangocms_moderation.backends.sequential_number_with_identifier_prefix_backend', 'Sequential number with identifier prefix')], default='djangocms_moderation.backends.uuid4_backend', max_length=255, verbose_name='compliance number backend')), + ], + options={ + 'verbose_name': 'Workflow', + 'verbose_name_plural': 'Workflows', + }, + ), + migrations.CreateModel( + name='WorkflowStep', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_required', models.BooleanField(default=True, verbose_name='is mandatory')), + ('order', models.PositiveIntegerField()), + ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.role', verbose_name='role')), + ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='djangocms_moderation.workflow', verbose_name='workflow')), + ], + options={ + 'verbose_name': 'Step', + 'verbose_name_plural': 'Steps', + 'ordering': ('order',), + 'unique_together': {('role', 'workflow')}, + }, + ), + migrations.AddField( + model_name='moderationcollection', + name='workflow', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_collections', to='djangocms_moderation.workflow', verbose_name='workflow'), + ), + migrations.CreateModel( + name='RequestComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.TextField(blank=True, verbose_name='message')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationrequest')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='moderationcollection', + name='author', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='owner'), + ), + migrations.AlterModelOptions( + name='moderationcollection', + options={'permissions': (('can_change_author', 'Can change collection author'),), 'verbose_name': 'collection'}, + ), + migrations.CreateModel( + name='CollectionComment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.TextField(blank=True, verbose_name='message')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationcollection')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ConfirmationFormSubmission', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.TextField(blank=True, editable=False)), + ('submitted_at', models.DateTimeField(auto_now_add=True)), + ('by_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='by user')), + ('confirmation_page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='djangocms_moderation.confirmationpage', verbose_name='confirmation page')), + ('for_step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='djangocms_moderation.workflowstep', verbose_name='for step')), + ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='form_submissions', to='djangocms_moderation.moderationrequest', verbose_name='moderation request')), + ], + options={ + 'verbose_name': 'Confirmation Form Submission', + 'verbose_name_plural': 'Confirmation Form Submissions', + 'unique_together': {('moderation_request', 'for_step')}, + }, + ), + migrations.CreateModel( + name='ModerationRequestAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('action', models.CharField(choices=[('resubmitted', 'Resubmitted'), ('start', 'Started'), ('rejected', 'Rejected'), ('approved', 'Approved'), ('cancelled', 'Cancelled'), ('finished', 'Finished')], max_length=30, verbose_name='status')), + ('message', models.TextField(blank=True, verbose_name='message')), + ('date_taken', models.DateTimeField(auto_now_add=True, verbose_name='date taken')), + ('is_archived', models.BooleanField(default=False)), + ('by_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='by user')), + ('step_approved', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.workflowstep', verbose_name='step approved')), + ('to_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='djangocms_moderation.role', verbose_name='to role')), + ('to_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='to user')), + ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='actions', to='djangocms_moderation.moderationrequest', verbose_name='moderation_request')), + ], + options={ + 'verbose_name': 'Action', + 'verbose_name_plural': 'Actions', + 'ordering': ('date_taken',), + }, + ), + migrations.AlterField( + model_name='moderationcollection', + name='author', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='moderator'), + ), + migrations.AlterModelOptions( + name='moderationcollection', + options={'permissions': (('can_change_author', 'Can change collection author'), ('cancel_moderationcollection', 'Can cancel collection')), 'verbose_name': 'collection'}, + ), + migrations.CreateModel( + name='ModerationRequestTreeNode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('path', models.CharField(max_length=255, unique=True)), + ('depth', models.PositiveIntegerField()), + ('numchild', models.PositiveIntegerField(default=0)), + ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationrequest', verbose_name='moderation_request')), + ], + options={ + 'ordering': ('id',), + }, + ), + ] diff --git a/djangocms_moderation/migrations/0007_auto_20181002_1725.py b/djangocms_moderation/migrations/0007_auto_20181002_1725.py index 651be569..ba92c49c 100644 --- a/djangocms_moderation/migrations/0007_auto_20181002_1725.py +++ b/djangocms_moderation/migrations/0007_auto_20181002_1725.py @@ -13,4 +13,4 @@ class Migration(migrations.Migration): dependencies = [("djangocms_moderation", "0006_auto_20181001_1840")] - operations = [migrations.RunPython(moderationrequest_author)] + operations = [migrations.RunPython(moderationrequest_author, elidable=True)] diff --git a/djangocms_moderation/migrations/0010_auto_20181008_1317.py b/djangocms_moderation/migrations/0010_auto_20181008_1317.py index a6f69b54..87168449 100644 --- a/djangocms_moderation/migrations/0010_auto_20181008_1317.py +++ b/djangocms_moderation/migrations/0010_auto_20181008_1317.py @@ -33,6 +33,6 @@ class Migration(migrations.Migration): operations = [ migrations.RunPython( - forward_copy_moderation_requests, reverse_copy_moderation_requests + forward_copy_moderation_requests, reverse_copy_moderation_requests, elidable=True ) ] From 47304d4113952a51cb9b39649967a4fee9595eed Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 08:59:55 +0100 Subject: [PATCH 22/26] Reformat squashed migration --- .../0001_squashed_0017_auto_20220831_0727.py | 689 +++++++++++++++--- 1 file changed, 568 insertions(+), 121 deletions(-) diff --git a/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py index 7ff51e57..5bb811a7 100644 --- a/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py +++ b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py @@ -7,207 +7,654 @@ class Migration(migrations.Migration): - - replaces = [('djangocms_moderation', '0001_initial'), ('djangocms_moderation', '0002_auto_20180905_1152'), ('djangocms_moderation', '0003_auto_20180903_1206'), ('djangocms_moderation', '0004_auto_20180907_1206'), ('djangocms_moderation', '0005_auto_20180919_1348'), ('djangocms_moderation', '0006_auto_20181001_1840'), ('djangocms_moderation', '0007_auto_20181002_1725'), ('djangocms_moderation', '0008_auto_20181002_1833'), ('djangocms_moderation', '0009_auto_20181005_1534'), ('djangocms_moderation', '0010_auto_20181008_1317'), ('djangocms_moderation', '0011_auto_20181008_1328'), ('djangocms_moderation', '0012_auto_20181016_1319'), ('djangocms_moderation', '0013_auto_20181122_1110'), ('djangocms_moderation', '0014_auto_20190313_1638'), ('djangocms_moderation', '0014_auto_20190315_1723'), ('djangocms_moderation', '0016_moderationrequesttreenode'), ('djangocms_moderation', '0017_auto_20220831_0727')] + replaces = [ + ("djangocms_moderation", "0001_initial"), + ("djangocms_moderation", "0002_auto_20180905_1152"), + ("djangocms_moderation", "0003_auto_20180903_1206"), + ("djangocms_moderation", "0004_auto_20180907_1206"), + ("djangocms_moderation", "0005_auto_20180919_1348"), + ("djangocms_moderation", "0006_auto_20181001_1840"), + ("djangocms_moderation", "0007_auto_20181002_1725"), + ("djangocms_moderation", "0008_auto_20181002_1833"), + ("djangocms_moderation", "0009_auto_20181005_1534"), + ("djangocms_moderation", "0010_auto_20181008_1317"), + ("djangocms_moderation", "0011_auto_20181008_1328"), + ("djangocms_moderation", "0012_auto_20181016_1319"), + ("djangocms_moderation", "0013_auto_20181122_1110"), + ("djangocms_moderation", "0014_auto_20190313_1638"), + ("djangocms_moderation", "0014_auto_20190315_1723"), + ("djangocms_moderation", "0016_moderationrequesttreenode"), + ("djangocms_moderation", "0017_auto_20220831_0727"), + ] initial = True dependencies = [ - ('cms', '0028_remove_page_placeholders'), - ('djangocms_versioning', '0010_version_proxies'), + ("cms", "0028_remove_page_placeholders"), + ("djangocms_versioning", "0010_version_proxies"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('auth', '0008_alter_user_username_max_length'), - ('contenttypes', '0002_remove_content_type_name'), - ('cms', '0020_old_tree_cleanup'), + ("auth", "0008_alter_user_username_max_length"), + ("contenttypes", "0002_remove_content_type_name"), + ("cms", "0020_old_tree_cleanup"), ] operations = [ migrations.CreateModel( - name='ConfirmationPage', + name="ConfirmationPage", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50, verbose_name='name')), - ('content_type', models.CharField(choices=[('plain', 'Plain'), ('form', 'Form')], default='form', max_length=50, verbose_name='Content Type')), - ('template', models.CharField(choices=[('djangocms_moderation/moderation_confirmation.html', 'Default')], default='djangocms_moderation/moderation_confirmation.html', max_length=100, verbose_name='Template')), - ('content', cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, slotname='confirmation_content', to='cms.placeholder')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50, verbose_name="name")), + ( + "content_type", + models.CharField( + choices=[("plain", "Plain"), ("form", "Form")], + default="form", + max_length=50, + verbose_name="Content Type", + ), + ), + ( + "template", + models.CharField( + choices=[ + ( + "djangocms_moderation/moderation_confirmation.html", + "Default", + ) + ], + default="djangocms_moderation/moderation_confirmation.html", + max_length=100, + verbose_name="Template", + ), + ), + ( + "content", + cms.models.fields.PlaceholderField( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + slotname="confirmation_content", + to="cms.placeholder", + ), + ), ], options={ - 'verbose_name': 'Confirmation Page', - 'verbose_name_plural': 'Confirmation Pages', + "verbose_name": "Confirmation Page", + "verbose_name_plural": "Confirmation Pages", }, ), migrations.CreateModel( - name='ModerationCollection', + name="ModerationCollection", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=128, verbose_name='collection name')), - ('status', models.CharField(choices=[('COLLECTING', 'Collecting'), ('IN_REVIEW', 'In Review'), ('ARCHIVED', 'Archived'), ('CANCELLED', 'Cancelled')], db_index=True, default='COLLECTING', max_length=10)), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('date_modified', models.DateTimeField(auto_now=True)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=128, verbose_name="collection name"), + ), + ( + "status", + models.CharField( + choices=[ + ("COLLECTING", "Collecting"), + ("IN_REVIEW", "In Review"), + ("ARCHIVED", "Archived"), + ("CANCELLED", "Cancelled"), + ], + db_index=True, + default="COLLECTING", + max_length=10, + ), + ), + ("date_created", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="author", + ), + ), ], ), migrations.CreateModel( - name='ModerationRequest', + name="ModerationRequest", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('language', models.CharField(choices=[('en', 'English'), ('de', 'German')], max_length=20, verbose_name='language')), - ('is_active', models.BooleanField(db_index=True, default=True, verbose_name='is active')), - ('date_sent', models.DateTimeField(auto_now_add=True, verbose_name='date sent')), - ('compliance_number', models.CharField(blank=True, editable=False, max_length=32, null=True, unique=True, verbose_name='compliance number')), - ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_requests', to='djangocms_moderation.moderationcollection')), - ('version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_versioning.version', verbose_name='version')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language", + models.CharField( + choices=[("en", "English"), ("de", "German")], + max_length=20, + verbose_name="language", + ), + ), + ( + "is_active", + models.BooleanField( + db_index=True, default=True, verbose_name="is active" + ), + ), + ( + "date_sent", + models.DateTimeField(auto_now_add=True, verbose_name="date sent"), + ), + ( + "compliance_number", + models.CharField( + blank=True, + editable=False, + max_length=32, + null=True, + unique=True, + verbose_name="compliance number", + ), + ), + ( + "collection", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="moderation_requests", + to="djangocms_moderation.moderationcollection", + ), + ), + ( + "version", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_versioning.version", + verbose_name="version", + ), + ), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="author", + ), + ), ], options={ - 'verbose_name': 'Request', - 'verbose_name_plural': 'Requests', - 'ordering': ['id'], - 'unique_together': {('collection', 'version')}, + "verbose_name": "Request", + "verbose_name_plural": "Requests", + "ordering": ["id"], + "unique_together": {("collection", "version")}, }, ), migrations.CreateModel( - name='Role', + name="Role", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=120, unique=True, verbose_name='name')), - ('confirmation_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='djangocms_moderation.confirmationpage', verbose_name='confirmation page')), - ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.group', verbose_name='group')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=120, unique=True, verbose_name="name"), + ), + ( + "confirmation_page", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="djangocms_moderation.confirmationpage", + verbose_name="confirmation page", + ), + ), + ( + "group", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="auth.group", + verbose_name="group", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name="user", + ), + ), ], options={ - 'verbose_name': 'Role', - 'verbose_name_plural': 'Roles', + "verbose_name": "Role", + "verbose_name_plural": "Roles", }, ), migrations.CreateModel( - name='Workflow', + name="Workflow", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=120, unique=True, verbose_name='name')), - ('is_default', models.BooleanField(default=False, verbose_name='is default')), - ('identifier', models.CharField(blank=True, default='', help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", max_length=128, verbose_name='identifier')), - ('requires_compliance_number', models.BooleanField(default=False, help_text='Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below', verbose_name='requires compliance number?')), - ('compliance_number_backend', models.CharField(choices=[('djangocms_moderation.backends.uuid4_backend', 'Unique alpha-numeric string'), ('djangocms_moderation.backends.sequential_number_backend', 'Sequential number'), ('djangocms_moderation.backends.sequential_number_with_identifier_prefix_backend', 'Sequential number with identifier prefix')], default='djangocms_moderation.backends.uuid4_backend', max_length=255, verbose_name='compliance number backend')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=120, unique=True, verbose_name="name"), + ), + ( + "is_default", + models.BooleanField(default=False, verbose_name="is default"), + ), + ( + "identifier", + models.CharField( + blank=True, + default="", + help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", + max_length=128, + verbose_name="identifier", + ), + ), + ( + "requires_compliance_number", + models.BooleanField( + default=False, + help_text="Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below", + verbose_name="requires compliance number?", + ), + ), + ( + "compliance_number_backend", + models.CharField( + choices=[ + ( + "djangocms_moderation.backends.uuid4_backend", + "Unique alpha-numeric string", + ), + ( + "djangocms_moderation.backends.sequential_number_backend", + "Sequential number", + ), + ( + "djangocms_moderation.backends.sequential_number_with_identifier_prefix_backend", + "Sequential number with identifier prefix", + ), + ], + default="djangocms_moderation.backends.uuid4_backend", + max_length=255, + verbose_name="compliance number backend", + ), + ), ], options={ - 'verbose_name': 'Workflow', - 'verbose_name_plural': 'Workflows', + "verbose_name": "Workflow", + "verbose_name_plural": "Workflows", }, ), migrations.CreateModel( - name='WorkflowStep', + name="WorkflowStep", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_required', models.BooleanField(default=True, verbose_name='is mandatory')), - ('order', models.PositiveIntegerField()), - ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.role', verbose_name='role')), - ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='djangocms_moderation.workflow', verbose_name='workflow')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "is_required", + models.BooleanField(default=True, verbose_name="is mandatory"), + ), + ("order", models.PositiveIntegerField()), + ( + "role", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_moderation.role", + verbose_name="role", + ), + ), + ( + "workflow", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="steps", + to="djangocms_moderation.workflow", + verbose_name="workflow", + ), + ), ], options={ - 'verbose_name': 'Step', - 'verbose_name_plural': 'Steps', - 'ordering': ('order',), - 'unique_together': {('role', 'workflow')}, + "verbose_name": "Step", + "verbose_name_plural": "Steps", + "ordering": ("order",), + "unique_together": {("role", "workflow")}, }, ), migrations.AddField( - model_name='moderationcollection', - name='workflow', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='moderation_collections', to='djangocms_moderation.workflow', verbose_name='workflow'), + model_name="moderationcollection", + name="workflow", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="moderation_collections", + to="djangocms_moderation.workflow", + verbose_name="workflow", + ), ), migrations.CreateModel( - name='RequestComment', + name="RequestComment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('message', models.TextField(blank=True, verbose_name='message')), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationrequest')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("message", models.TextField(blank=True, verbose_name="message")), + ("date_created", models.DateTimeField(auto_now_add=True)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name="author", + ), + ), + ( + "moderation_request", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_moderation.moderationrequest", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.AlterField( - model_name='moderationcollection', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='owner'), + model_name="moderationcollection", + name="author", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="owner", + ), ), migrations.AlterModelOptions( - name='moderationcollection', - options={'permissions': (('can_change_author', 'Can change collection author'),), 'verbose_name': 'collection'}, + name="moderationcollection", + options={ + "permissions": (("can_change_author", "Can change collection author"),), + "verbose_name": "collection", + }, ), migrations.CreateModel( - name='CollectionComment', + name="CollectionComment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('message', models.TextField(blank=True, verbose_name='message')), - ('date_created', models.DateTimeField(auto_now_add=True)), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationcollection')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("message", models.TextField(blank=True, verbose_name="message")), + ("date_created", models.DateTimeField(auto_now_add=True)), + ( + "author", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name="author", + ), + ), + ( + "collection", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_moderation.moderationcollection", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='ConfirmationFormSubmission', + name="ConfirmationFormSubmission", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('data', models.TextField(blank=True, editable=False)), - ('submitted_at', models.DateTimeField(auto_now_add=True)), - ('by_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='by user')), - ('confirmation_page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='djangocms_moderation.confirmationpage', verbose_name='confirmation page')), - ('for_step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='djangocms_moderation.workflowstep', verbose_name='for step')), - ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='form_submissions', to='djangocms_moderation.moderationrequest', verbose_name='moderation request')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("data", models.TextField(blank=True, editable=False)), + ("submitted_at", models.DateTimeField(auto_now_add=True)), + ( + "by_user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="by user", + ), + ), + ( + "confirmation_page", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="djangocms_moderation.confirmationpage", + verbose_name="confirmation page", + ), + ), + ( + "for_step", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to="djangocms_moderation.workflowstep", + verbose_name="for step", + ), + ), + ( + "moderation_request", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="form_submissions", + to="djangocms_moderation.moderationrequest", + verbose_name="moderation request", + ), + ), ], options={ - 'verbose_name': 'Confirmation Form Submission', - 'verbose_name_plural': 'Confirmation Form Submissions', - 'unique_together': {('moderation_request', 'for_step')}, + "verbose_name": "Confirmation Form Submission", + "verbose_name_plural": "Confirmation Form Submissions", + "unique_together": {("moderation_request", "for_step")}, }, ), migrations.CreateModel( - name='ModerationRequestAction', + name="ModerationRequestAction", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('action', models.CharField(choices=[('resubmitted', 'Resubmitted'), ('start', 'Started'), ('rejected', 'Rejected'), ('approved', 'Approved'), ('cancelled', 'Cancelled'), ('finished', 'Finished')], max_length=30, verbose_name='status')), - ('message', models.TextField(blank=True, verbose_name='message')), - ('date_taken', models.DateTimeField(auto_now_add=True, verbose_name='date taken')), - ('is_archived', models.BooleanField(default=False)), - ('by_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='by user')), - ('step_approved', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.workflowstep', verbose_name='step approved')), - ('to_role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='djangocms_moderation.role', verbose_name='to role')), - ('to_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='to user')), - ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='actions', to='djangocms_moderation.moderationrequest', verbose_name='moderation_request')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "action", + models.CharField( + choices=[ + ("resubmitted", "Resubmitted"), + ("start", "Started"), + ("rejected", "Rejected"), + ("approved", "Approved"), + ("cancelled", "Cancelled"), + ("finished", "Finished"), + ], + max_length=30, + verbose_name="status", + ), + ), + ("message", models.TextField(blank=True, verbose_name="message")), + ( + "date_taken", + models.DateTimeField(auto_now_add=True, verbose_name="date taken"), + ), + ("is_archived", models.BooleanField(default=False)), + ( + "by_user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + verbose_name="by user", + ), + ), + ( + "step_approved", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_moderation.workflowstep", + verbose_name="step approved", + ), + ), + ( + "to_role", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to="djangocms_moderation.role", + verbose_name="to role", + ), + ), + ( + "to_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name="to user", + ), + ), + ( + "moderation_request", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="actions", + to="djangocms_moderation.moderationrequest", + verbose_name="moderation_request", + ), + ), ], options={ - 'verbose_name': 'Action', - 'verbose_name_plural': 'Actions', - 'ordering': ('date_taken',), + "verbose_name": "Action", + "verbose_name_plural": "Actions", + "ordering": ("date_taken",), }, ), migrations.AlterField( - model_name='moderationcollection', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='moderator'), + model_name="moderationcollection", + name="author", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + verbose_name="moderator", + ), ), migrations.AlterModelOptions( - name='moderationcollection', - options={'permissions': (('can_change_author', 'Can change collection author'), ('cancel_moderationcollection', 'Can cancel collection')), 'verbose_name': 'collection'}, + name="moderationcollection", + options={ + "permissions": ( + ("can_change_author", "Can change collection author"), + ("cancel_moderationcollection", "Can cancel collection"), + ), + "verbose_name": "collection", + }, ), migrations.CreateModel( - name='ModerationRequestTreeNode', + name="ModerationRequestTreeNode", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('path', models.CharField(max_length=255, unique=True)), - ('depth', models.PositiveIntegerField()), - ('numchild', models.PositiveIntegerField(default=0)), - ('moderation_request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='djangocms_moderation.moderationrequest', verbose_name='moderation_request')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("path", models.CharField(max_length=255, unique=True)), + ("depth", models.PositiveIntegerField()), + ("numchild", models.PositiveIntegerField(default=0)), + ( + "moderation_request", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="djangocms_moderation.moderationrequest", + verbose_name="moderation_request", + ), + ), ], options={ - 'ordering': ('id',), + "ordering": ("id",), }, ), ] From 18721321415ce7fbce3355aed2f899797b43cb0f Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 09:13:49 +0100 Subject: [PATCH 23/26] Fix codespell --- .../migrations/0001_squashed_0017_auto_20220831_0727.py | 7 +++++-- .../static/djangocms_moderation/js/burger.js | 2 +- setup.cfg | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py index 5bb811a7..df8856a5 100644 --- a/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py +++ b/djangocms_moderation/migrations/0001_squashed_0017_auto_20220831_0727.py @@ -286,7 +286,8 @@ class Migration(migrations.Migration): models.CharField( blank=True, default="", - help_text="Identifier is a 'free' field you could use for internal purposes. For example, it could be used as a workflow specific prefix of a compliance number", + help_text="Identifier is a 'free' field you could use for internal purposes. For example, " + "it could be used as a workflow specific prefix of a compliance number", max_length=128, verbose_name="identifier", ), @@ -295,7 +296,8 @@ class Migration(migrations.Migration): "requires_compliance_number", models.BooleanField( default=False, - help_text="Does the Compliance number need to be generated before the moderation request is approved? Please select the compliance number backend below", + help_text="Does the Compliance number need to be generated before the moderation request is " + "approved? Please select the compliance number backend below", verbose_name="requires compliance number?", ), ), @@ -306,6 +308,7 @@ class Migration(migrations.Migration): ( "djangocms_moderation.backends.uuid4_backend", "Unique alpha-numeric string", + ), ( "djangocms_moderation.backends.sequential_number_backend", diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index 85f6fa8d..3fbcdf9e 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -12,7 +12,7 @@ .on('click', function (e) { e.preventDefault(); - // action currently being targetted + // action currently being targeted let action = $(e.currentTarget); // get the form method being used? let formMethod = action.attr('class').indexOf('cms-form-get-method') === 1 ? 'POST' : 'GET'; diff --git a/setup.cfg b/setup.cfg index 56767f48..56cdaaf2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -46,3 +46,6 @@ exclude_lines = raise NotImplementedError if 0: if __name__ == .__main__.: + +[codespell] +ignore-words-list = alpha-numeric From 158548854e9502d27f954eb95beeda674fee4f79 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 11:49:56 +0100 Subject: [PATCH 24/26] Adjust tests for version_locking in both django CMS 4.0 and 4.1 --- djangocms_moderation/models.py | 14 ++++- tests/test_views.py | 109 +++++++++++++++++---------------- 2 files changed, 68 insertions(+), 55 deletions(-) diff --git a/djangocms_moderation/models.py b/djangocms_moderation/models.py index 1dbcc854..c143770f 100644 --- a/djangocms_moderation/models.py +++ b/djangocms_moderation/models.py @@ -22,6 +22,16 @@ from . import conf, constants, signals # isort:skip +try: + from djangocms_versioning.helpers import version_is_locked + + def version_is_unlocked_for_moderation(version, user): + return version_is_locked(version) is None +except ImportError: + def version_is_unlocked_for_moderation(version, user): + return version.created_by == user + + class ConfirmationPage(models.Model): CONTENT_TYPES = ( (constants.CONTENT_TYPE_PLAIN, _("Plain")), @@ -382,8 +392,8 @@ def _add_nested_children(self, version, parent_node): for child_version in get_moderated_children_from_placeholder( placeholder, version.versionable.grouping_values(parent) ): - # Don't add the version if it's already part of the collection or another users item - if version.created_by == child_version.created_by: + # Don't add the version if it's already part of the collection or locked by another user + if version_is_unlocked_for_moderation(child_version, version.created_by): moderation_request, _added_items = self.add_version( child_version, parent=parent_node, include_children=True ) diff --git a/tests/test_views.py b/tests/test_views.py index d4cd9464..ed8c8043 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -30,6 +30,15 @@ UserFactory, ) +try: + from djangocms_versioning.helpers import remove_version_lock +except ImportError: + try: + from djangocms_version_locking.helpers import remove_version_lock + except ImportError: + def remove_version_lock(version): + pass + class CollectionItemsViewAddingRequestsTestCase(CMSTestCase): def test_no_eligible_items_to_add_to_collection(self): @@ -216,6 +225,9 @@ def test_add_pages_moderated_children_to_collection(self): poll2_version = PollVersionFactory(created_by=user, content__language=language) PollPluginFactory(placeholder=placeholder, poll=poll1_version.content.poll) PollPluginFactory(placeholder=placeholder, poll=poll2_version.content.poll) + remove_version_lock(page_version) + remove_version_lock(poll1_version) + remove_version_lock(poll2_version) admin_endpoint = get_admin_url( name="cms_moderation_items_to_collection", language="en", args=() @@ -238,11 +250,7 @@ def test_add_pages_moderated_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - self.assertEqual(stored_collection.count(), 1) - else: - self.assertEqual(stored_collection.count(), 3) + self.assertEqual(stored_collection.count(), 3) mr = ModerationRequest.objects.filter( collection=collection, version=page_version ) @@ -288,6 +296,7 @@ def test_add_pages_moderated_duplicated_children_to_collection(self): poll_version = PollVersionFactory(created_by=user, content__language=language) PollPluginFactory(placeholder=placeholder, poll=poll_version.content.poll) PollPluginFactory(placeholder=placeholder, poll=poll_version.content.poll) + remove_version_lock(poll_version) admin_endpoint = get_admin_url( name="cms_moderation_items_to_collection", language="en", args=() @@ -316,18 +325,13 @@ def test_add_pages_moderated_duplicated_children_to_collection(self): 1, ) mr = stored_collection.filter(version=poll_version) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - # poll1_version is locked, so will not be added to collection - self.assertEqual(mr.count(), 0) - else: - self.assertEqual(mr.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_version) - ).count(), - 1, - ) + self.assertEqual(mr.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_version) + ).count(), + 1, + ) def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( self @@ -347,7 +351,9 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( poll2_version = PollVersionFactory(created_by=user2, content__language=language) PollPluginFactory(placeholder=placeholder, poll=poll1_version.content.poll) PollPluginFactory(placeholder=placeholder, poll=poll2_version.content.poll) - + remove_version_lock(page_version) + remove_version_lock(poll1_version) + # poll2_version remains is locked, so will not be added to collection admin_endpoint = get_admin_url( name="cms_moderation_items_to_collection", language="en", args=() ) @@ -367,11 +373,7 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - self.assertEqual(stored_collection.count(), 1) - else: - self.assertEqual(stored_collection.count(), 2) + self.assertEqual(stored_collection.count(), 2) self.assertEqual(stored_collection.filter(version=page_version).count(), 1) self.assertEqual( @@ -414,10 +416,12 @@ def test_add_pages_moderated_traversed_children_to_collection(self): language = page_version.content.language # Populate page placeholder = PlaceholderFactory(source=page_version.content) + remove_version_lock(page_version) poll_version = PollVersionFactory(created_by=user, content__language=language) poll_plugin = PollPluginFactory( placeholder=placeholder, poll=poll_version.content.poll ) + remove_version_lock(poll_version) # Populate page poll child layer 1 poll_child_1_version = PollVersionFactory( created_by=user, content__language=language @@ -425,6 +429,7 @@ def test_add_pages_moderated_traversed_children_to_collection(self): poll_child_1_plugin = PollPluginFactory( placeholder=poll_plugin.placeholder, poll=poll_child_1_version.content.poll ) + remove_version_lock(poll_child_1_version) # Populate page poll child layer 2 poll_child_2_version = PollVersionFactory( created_by=user, content__language=language @@ -433,6 +438,7 @@ def test_add_pages_moderated_traversed_children_to_collection(self): placeholder=poll_child_1_plugin.placeholder, poll=poll_child_2_version.content.poll, ) + remove_version_lock(poll_child_2_version) admin_endpoint = get_admin_url( name="cms_moderation_items_to_collection", language="en", args=() @@ -453,11 +459,7 @@ def test_add_pages_moderated_traversed_children_to_collection(self): self.assertEqual(302, response.status_code) self.assertEqual(admin_endpoint, response.url) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - self.assertEqual(stored_collection.count(), 1) - else: - self.assertEqual(stored_collection.count(), 4) + self.assertEqual(stored_collection.count(), 4) self.assertEqual(stored_collection.filter(version=page_version).count(), 1) self.assertEqual( @@ -752,11 +754,13 @@ def test_tree_nodes_are_created(self): PollPluginFactory( placeholder=placeholder, poll=poll_version.content.poll ) + remove_version_lock(poll_version) # Populate poll child poll_child_version = PollVersionFactory( created_by=user, content__language=language ) + remove_version_lock(poll_child_version) PollPluginFactory( placeholder=poll_version.content.placeholder, poll=poll_child_version.content.poll, @@ -766,6 +770,7 @@ def test_tree_nodes_are_created(self): poll_grandchild_version = PollVersionFactory( created_by=user, content__language=language ) + remove_version_lock(poll_grandchild_version) PollPluginFactory( placeholder=poll_child_version.content.placeholder, poll=poll_grandchild_version.content.poll, @@ -795,35 +800,29 @@ def test_tree_nodes_are_created(self): moderation_request__collection_id=collection.pk ) - # The correct amount of nodes exist - if cms_version < "4.1.0": - self.assertEqual(nodes.count(), 1) - else: - self.assertEqual(nodes.count(), 6) + # The correct number of nodes exists + self.assertEqual(nodes.count(), 6) # Now assert the tree structure... # Check root refers to correct version & has correct number of children root = ModerationRequestTreeNode.get_root_nodes().get() self.assertEqual(root.moderation_request.version, page_version) - if cms_version < "4.1.0": - self.assertEqual(root.get_children().count(), 0) - else: - self.assertEqual(root.get_children().count(), 2) - # Check first child of root has correct tree - poll_node = root.get_children().get(moderation_request__version=poll_version) - self.assertEqual(poll_node.get_children().count(), 1) - poll_child_node = poll_node.get_children().get() - self.assertEqual(poll_child_node.moderation_request.version, poll_child_version) - self.assertEqual(poll_child_node.get_children().count(), 1) - poll_grandchild_node = poll_child_node.get_children().get() - self.assertEqual(poll_grandchild_node.moderation_request.version, poll_grandchild_version) - # Check second child of root has correct tree - poll_child_node2 = root.get_children().get(moderation_request__version=poll_child_version) - self.assertNotEqual(poll_child_node, poll_child_node2) - self.assertEqual(poll_child_node2.moderation_request.version, poll_child_version) - self.assertEqual(poll_child_node2.get_children().count(), 1) - poll_grandchild_node2 = poll_child_node2.get_children().get() - self.assertNotEqual(poll_grandchild_node, poll_grandchild_node2) - self.assertEqual(poll_grandchild_node2.moderation_request.version, poll_grandchild_version) + self.assertEqual(root.get_children().count(), 2) + # Check first child of root has correct tree + poll_node = root.get_children().get(moderation_request__version=poll_version) + self.assertEqual(poll_node.get_children().count(), 1) + poll_child_node = poll_node.get_children().get() + self.assertEqual(poll_child_node.moderation_request.version, poll_child_version) + self.assertEqual(poll_child_node.get_children().count(), 1) + poll_grandchild_node = poll_child_node.get_children().get() + self.assertEqual(poll_grandchild_node.moderation_request.version, poll_grandchild_version) + # Check second child of root has correct tree + poll_child_node2 = root.get_children().get(moderation_request__version=poll_child_version) + self.assertNotEqual(poll_child_node, poll_child_node2) + self.assertEqual(poll_child_node2.moderation_request.version, poll_child_version) + self.assertEqual(poll_child_node2.get_children().count(), 1) + poll_grandchild_node2 = poll_child_node2.get_children().get() + self.assertNotEqual(poll_grandchild_node, poll_grandchild_node2) + self.assertEqual(poll_grandchild_node2.moderation_request.version, poll_grandchild_version) class SubmitCollectionForModerationViewTest(BaseViewTestCase): @@ -996,11 +995,15 @@ def _set_up_initial_page_data(self): self.poll_child_version = PollVersionFactory(created_by=self.user, content__language=language) PollPluginFactory( placeholder=self.poll_version.content.placeholder, poll=self.poll_child_version.content.poll) + remove_version_lock(self.page_1_version) + remove_version_lock(self.poll_version) + remove_version_lock(self.poll_child_version) # Page 2 self.page_2_version = PageVersionFactory(created_by=self.user, content__language=language) page_2_placeholder = PlaceholderFactory(source=self.page_2_version.content) PollPluginFactory(placeholder=page_2_placeholder, poll=self.poll_child_version.content.poll) + remove_version_lock(self.page_2_version) def _add_pages_to_collection(self): """ From 88a68ddb4e33e759f786a7181429008912af21ff Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 11:54:14 +0100 Subject: [PATCH 25/26] Test adjustments for django CMS 4.0 --- tests/test_views.py | 267 ++++++++++++++++---------------------------- 1 file changed, 97 insertions(+), 170 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index ed8c8043..17fd56d3 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -5,7 +5,6 @@ from django.test import TransactionTestCase from django.urls import reverse -from cms import __version__ as cms_version from cms.test_utils.testcases import CMSTestCase from cms.utils.urlutils import add_url_parameters, admin_reverse @@ -30,6 +29,7 @@ UserFactory, ) + try: from djangocms_versioning.helpers import remove_version_lock except ImportError: @@ -265,20 +265,14 @@ def test_add_pages_moderated_children_to_collection(self): collection=collection, version=poll2_version ) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - # poll1_version is locked, so will not be added to collection - self.assertEqual(mr1.count(), 0) - self.assertEqual(mr2.count(), 0) - else: - self.assertEqual(mr1.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request=mr1.first()).count(), 1 - ) - self.assertEqual(mr2.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request=mr2.first()).count(), 1 - ) + self.assertEqual(mr1.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request=mr1.first()).count(), 1 + ) + self.assertEqual(mr2.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request=mr2.first()).count(), 1 + ) def test_add_pages_moderated_duplicated_children_to_collection(self): """ @@ -383,18 +377,13 @@ def test_add_pages_moderated_duplicated_children_to_collection_for_author_only( 1, ) mr1 = stored_collection.filter(version=poll1_version) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - # poll1_version is locked, so will not be added to collection - self.assertEqual(mr1.count(), 0) - else: - self.assertEqual(mr1.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll1_version) - ).count(), - 1, - ) + self.assertEqual(mr1.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll1_version) + ).count(), + 1, + ) self.assertEqual(stored_collection.filter(version=poll2_version).count(), 0) self.assertEqual( @@ -470,42 +459,31 @@ def test_add_pages_moderated_traversed_children_to_collection(self): ) mr = stored_collection.filter(version=poll_version) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - # poll1_version is locked, so will not be added to collection - self.assertEqual(mr.count(), 0) - self.assertEqual( - stored_collection.filter(version=poll_child_1_version).count(), 0 - ) - self.assertEqual( - stored_collection.filter(version=poll_child_2_version).count(), 0 - ) - else: - self.assertEqual(mr.count(), 1) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_version) - ).count(), - 1, - ) - self.assertEqual( - stored_collection.filter(version=poll_child_1_version).count(), 1 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_child_1_version) - ).count(), - 1, - ) - self.assertEqual( - stored_collection.filter(version=poll_child_2_version).count(), 1 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter( - moderation_request=stored_collection.get(version=poll_child_2_version) - ).count(), - 1, - ) + self.assertEqual(mr.count(), 1) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_version) + ).count(), + 1, + ) + self.assertEqual( + stored_collection.filter(version=poll_child_1_version).count(), 1 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_child_1_version) + ).count(), + 1, + ) + self.assertEqual( + stored_collection.filter(version=poll_child_2_version).count(), 1 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter( + moderation_request=stored_collection.get(version=poll_child_2_version) + ).count(), + 1, + ) def test_adding_non_page_item_doesnt_trigger_nested_collection_mechanism(self): user = self.get_superuser() @@ -1036,39 +1014,29 @@ def _add_pages_to_collection(self): mr = ModerationRequest.objects.filter(collection=self.collection) # The tree structure for page_1_version is correct root_1 = ModerationRequestTreeNode.get_root_nodes().get(moderation_request__version=self.page_1_version) - if cms_version < "4.1.0": - # version-locking override `_add_nested_children` add locked state checking - self.assertEqual(mr.count(), 2) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 2 - ) - self.assertEqual(root_1.get_children().count(), 0) - - else: - self.assertEqual(mr.count(), 4) - # The correct amount of tree nodes has been created - # Poll is repeated twice and will therefore have an additional node - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 5 - ) - self.assertEqual(root_1.get_children().count(), 1) - - child_1 = root_1.get_children().get() - self.assertEqual(child_1.moderation_request.version, self.poll_version) - self.assertEqual(child_1.get_children().count(), 1) - grandchild = child_1.get_children().get() - self.assertEqual( - grandchild.moderation_request.version, self.poll_child_version) - - # The tree structure for page_2_version is correct - root_2 = ModerationRequestTreeNode.get_root_nodes().get( - moderation_request__version=self.page_2_version) - self.assertEqual(root_2.get_children().count(), 1) - child_2 = root_2.get_children().get() - self.assertEqual(child_2.moderation_request.version, self.poll_child_version) - self.assertEqual(grandchild.moderation_request, child_2.moderation_request) + self.assertEqual(mr.count(), 4) + # The correct amount of tree nodes has been created + # Poll is repeated twice and will therefore have an additional node + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 5 + ) + self.assertEqual(root_1.get_children().count(), 1) + + child_1 = root_1.get_children().get() + self.assertEqual(child_1.moderation_request.version, self.poll_version) + self.assertEqual(child_1.get_children().count(), 1) + grandchild = child_1.get_children().get() + self.assertEqual( + grandchild.moderation_request.version, self.poll_child_version) + + # The tree structure for page_2_version is correct + root_2 = ModerationRequestTreeNode.get_root_nodes().get( + moderation_request__version=self.page_2_version) + self.assertEqual(root_2.get_children().count(), 1) + child_2 = root_2.get_children().get() + self.assertEqual(child_2.moderation_request.version, self.poll_child_version) + self.assertEqual(grandchild.moderation_request, child_2.moderation_request) def test_moderation_workflow_node_deletion_1(self): """ @@ -1112,33 +1080,19 @@ def test_moderation_workflow_node_deletion_1(self): # The whole of the page_2_version tree should have been removed. # Additionally, poll_child_version should have been removed from # the page_1_version tree. - if cms_version < "4.1.0": - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 1 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 1 - ) - self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) - root = ModerationRequestTreeNode.get_root_nodes().get() - self.assertEqual(root.moderation_request.version, self.page_1_version) - self.assertEqual(root.get_children().count(), 0) - else: - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 2 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 2 - ) - self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) - root = ModerationRequestTreeNode.get_root_nodes().get() - self.assertEqual(root.moderation_request.version, self.page_1_version) - self.assertEqual(root.get_children().count(), 1) - self.assertEqual(root.get_children().get().moderation_request.version, self.poll_version) + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 2 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 2 + ) + self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 1) + root = ModerationRequestTreeNode.get_root_nodes().get() + self.assertEqual(root.moderation_request.version, self.page_1_version) + self.assertEqual(root.get_children().count(), 1) + self.assertEqual(root.get_children().get().moderation_request.version, self.poll_version) def test_moderation_workflow_node_deletion_2(self): """ @@ -1299,33 +1253,20 @@ def test_moderation_workflow_node_deletion_4(self): # Check the data # Only the roots (page_1_version and page_2_version) should remain - if cms_version < "4.1.0": - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 2 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 2 - ) - else: - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 3 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 3 - ) + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 3 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 3 + ) self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 2) root_1 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_1_version).get() root_2 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_2_version).get() - if cms_version < "4.1.0": - self.assertEqual(root_1.get_children().count(), 0) - else: - self.assertEqual(root_1.get_children().count(), 1) + self.assertEqual(root_1.get_children().count(), 1) self.assertEqual(root_2.get_children().count(), 0) def test_moderation_workflow_node_deletion_5(self): @@ -1372,34 +1313,20 @@ def test_moderation_workflow_node_deletion_5(self): # Check the data # Only the roots (page_1_version and page_2_version) should remain - if cms_version < "4.1.0": - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 2 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 2 - ) - else: - self.assertEqual( - ModerationRequest.objects.filter(collection=self.collection).count(), - 3 - ) - self.assertEqual( - ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), - 3 - ) + self.assertEqual( + ModerationRequest.objects.filter(collection=self.collection).count(), + 3 + ) + self.assertEqual( + ModerationRequestTreeNode.objects.filter(moderation_request__collection=self.collection).count(), + 3 + ) self.assertEqual(ModerationRequestTreeNode.get_root_nodes().count(), 2) root_1 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_1_version).get() root_2 = ModerationRequestTreeNode.get_root_nodes().filter( moderation_request__version=self.page_2_version).get() - if cms_version < "4.1.0": - self.assertEqual(root_1.get_children().count(), 0) - self.assertEqual(root_2.get_children().count(), 0) - else: - self.assertEqual(root_1.get_children().count(), 1) - self.assertEqual(root_1.get_children().get().moderation_request.version, self.poll_version) - self.assertEqual(root_2.get_children().count(), 0) + self.assertEqual(root_1.get_children().count(), 1) + self.assertEqual(root_1.get_children().get().moderation_request.version, self.poll_version) + self.assertEqual(root_2.get_children().count(), 0) From 21e755a0525c960cd4e9fed66c9549fb440957a3 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Fri, 15 Mar 2024 12:15:14 +0100 Subject: [PATCH 26/26] Add tests that locked versions do not get included in collections --- tests/test_views.py | 92 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/tests/test_views.py b/tests/test_views.py index 17fd56d3..b6e86c6c 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -31,13 +31,9 @@ try: - from djangocms_versioning.helpers import remove_version_lock + from djangocms_versioning.helpers import remove_version_lock, version_is_locked except ImportError: - try: - from djangocms_version_locking.helpers import remove_version_lock - except ImportError: - def remove_version_lock(version): - pass + from djangocms_version_locking.helpers import remove_version_lock, version_is_locked class CollectionItemsViewAddingRequestsTestCase(CMSTestCase): @@ -626,6 +622,90 @@ def test_collection_with_redirect_url_query_redirect_sanitisation(self): ) +class ModerationCollectionTestCase(CMSTestCase): + def setUp(self): + self.language = "en" + self.user_1 = self.get_superuser() + self.user_2 = UserFactory() + self.collection = ModerationCollectionFactory(author=self.user_1) + self.page_version = PageVersionFactory(created_by=self.user_1) + self.placeholder = PlaceholderFactory(source=self.page_version.content) + self.poll_version = PollVersionFactory(created_by=self.user_2, content__language=self.language) + + def test_add_version_with_locked_plugins(self): + """ + Locked plugins should not be allowed to be added to a collection + """ + PollPluginFactory(placeholder=self.placeholder, poll=self.poll_version.content.poll) + + admin_endpoint = get_admin_url( + name="cms_moderation_items_to_collection", language="en", args=() + ) + + url = add_url_parameters( + admin_endpoint, + return_to_url="http://example.com", + version_ids=self.page_version.pk, + collection_id=self.collection.pk, + ) + + # Poll should be locked by default + poll_is_locked = version_is_locked(self.poll_version) + self.assertTrue(poll_is_locked) + + with self.login_user_context(self.user_1): + self.client.post( + path=url, + data={"collection": self.collection.pk, "versions": [self.page_version.pk, self.poll_version.pk]}, + follow=False, + ) + + # Get all moderation request objects for the collection + moderation_requests = ModerationRequest.objects.filter(collection=self.collection) + + self.assertEqual(moderation_requests.count(), 1) + self.assertTrue(moderation_requests.filter(version=self.page_version).exists()) + self.assertFalse(moderation_requests.filter(version=self.poll_version).exists()) + + def test_add_version_with_unlocked_child(self): + """ + Only plugins that are unlocked should be added to collection + """ + + PollPluginFactory(placeholder=self.placeholder, poll=self.poll_version.content.poll) + + admin_endpoint = get_admin_url( + name="cms_moderation_items_to_collection", language="en", args=() + ) + + url = add_url_parameters( + admin_endpoint, + return_to_url="http://example.com", + version_ids=self.page_version.pk, + collection_id=self.collection.pk, + ) + + # Poll should be locked by default + poll_is_locked = version_is_locked(self.poll_version) + self.assertTrue(poll_is_locked) + + # Unlock the poll version + remove_version_lock(self.poll_version) + + with self.login_user_context(self.user_1): + self.client.post( + path=url, + data={"collection": self.collection.pk, "versions": [self.page_version.pk, self.poll_version.pk]}, + follow=False, + ) + + # Get all moderation request objects for the collection + moderation_requests = ModerationRequest.objects.filter(collection=self.collection) + self.assertEqual(moderation_requests.count(), 2) + self.assertTrue(moderation_requests.filter(version=self.page_version).exists()) + self.assertTrue(moderation_requests.filter(version=self.poll_version).exists()) + + class CollectionItemsViewTest(CMSTestCase): def setUp(self): self.client.force_login(self.get_superuser())