From e3a703fa384837fd7b84f22e7534c763bd16e510 Mon Sep 17 00:00:00 2001 From: Aiky30 Date: Tue, 18 Jan 2022 14:02:05 +0000 Subject: [PATCH 01/91] Release 2.0.0 (#200) --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8f224355..b12c8458 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.0.0 (2022-01-18) +=================== * Python 3.8, 3.9 support added * Django 3.0, 3.1 and 3.2 support added * Python 3.5 and 3.6 support removed diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 51479893..c5156840 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "1.0.28" +__version__ = "2.0.0" default_app_config = "djangocms_moderation.apps.ModerationConfig" From 141018def5facd98bd18c0f2b0098f93ee67c784 Mon Sep 17 00:00:00 2001 From: Aiky30 Date: Tue, 18 Jan 2022 14:47:25 +0000 Subject: [PATCH 02/91] fix: Refactor flawed add to collection XSS redirect sanitisation added in 1.0.26 (#199) --- CHANGELOG.rst | 1 + djangocms_moderation/views.py | 8 ++++++-- tests/test_views.py | 30 +++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b12c8458..1e7a220d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Refactor flawed add to collection XSS redirect sanitisation added in 1.0.26 2.0.0 (2022-01-18) =================== diff --git a/djangocms_moderation/views.py b/djangocms_moderation/views.py index 95d4e839..03f899e3 100644 --- a/djangocms_moderation/views.py +++ b/djangocms_moderation/views.py @@ -1,10 +1,12 @@ +from urllib.parse import quote + from django.contrib import admin, messages from django.db import transaction from django.http import Http404, HttpResponseRedirect 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, urlquote +from django.utils.http import is_safe_url from django.utils.translation import gettext_lazy as _, ngettext from django.views.generic import FormView @@ -86,7 +88,9 @@ def _get_success_redirect(self): allowed_hosts=self.request.get_host(), require_https=self.request.is_secure(), ) - return_to_url = urlquote(return_to_url) + # Protect against refracted XSS attacks + # Allow : in http://, ?=& for GET parameters + return_to_url = quote(return_to_url, safe='/:?=&') if not url_is_safe: return_to_url = self.request.path return HttpResponseRedirect(return_to_url) diff --git a/tests/test_views.py b/tests/test_views.py index d496d48d..b7ecee19 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -6,7 +6,7 @@ from django.urls import reverse from cms.test_utils.testcases import CMSTestCase -from cms.utils.urlutils import add_url_parameters +from cms.utils.urlutils import add_url_parameters, admin_reverse from djangocms_versioning.test_utils.factories import PageVersionFactory @@ -550,15 +550,20 @@ def test_adding_page_not_by_the_author_doesnt_trigger_nested_collection_mechanis ModerationRequestTreeNode.objects.filter(moderation_request=mr1.first()).count(), 0 ) - def test_collection_with_redirect_url_query_string_parameter_sanitisation(self): + 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, + this functionality should continue to function even when sanitised! + """ user = self.get_superuser() collection = ModerationCollectionFactory(author=user) page1_version = PageVersionFactory(created_by=user) page2_version = PageVersionFactory(created_by=user) - - redirect_to_url = f"""{reverse( - "admin:djangocms_moderation_moderationcollection_changelist" - )}?=""" + opts = page1_version.versionable.version_model_proxy._meta + redirect_to_url = admin_reverse(f"{opts.app_label}_{opts.model_name}_changelist") + redirect_to_url += f"?page={page1_version.content.page.id}&" url = add_url_parameters( get_admin_url( @@ -578,8 +583,19 @@ def test_collection_with_redirect_url_query_string_parameter_sanitisation(self): follow=False, ) + messages = [message.message for message in list(get_messages(response.wsgi_request))] + self.assertEqual(response.status_code, 302) - self.assertIn("%3F%3D%3Cscript%3Ealert%28%27attack%21%27%29%3C/script%3E", response.url) + self.assertIn("%3Cscript%3Ealert%28%27attack%21%27%29%3C/script%3E", response.url) + self.assertIn(f"?page={page1_version.content.page.id}", response.url) + self.assertIn( + "2 items successfully added to moderation collection", + messages, + ) + self.assertNotIn( + "Perhaps it was deleted", + messages, + ) class CollectionItemsViewTest(CMSTestCase): From 88945aca0d6986a497fee10841f5c3c156f3b6f9 Mon Sep 17 00:00:00 2001 From: Aiky30 Date: Thu, 3 Feb 2022 09:53:19 +0000 Subject: [PATCH 03/91] fix: Add to collection should automatically add deeply nested versionable objects (#205) --- CHANGELOG.rst | 1 + djangocms_moderation/helpers.py | 46 ++++++++++----- tests/test_helpers.py | 57 +++++++++++++++++++ tests/utils/factories.py | 27 ++++----- tests/utils/moderated_polls/cms_plugins.py | 22 ++++++- tests/utils/moderated_polls/factories.py | 53 +++++++++++++++++ tests/utils/moderated_polls/models.py | 42 ++++++++++++++ .../templates/polls/deeply_nested_poll.html | 1 + .../templates/polls/nested_poll.html | 1 + 9 files changed, 223 insertions(+), 27 deletions(-) create mode 100644 tests/utils/moderated_polls/factories.py create mode 100644 tests/utils/moderated_polls/templates/polls/deeply_nested_poll.html create mode 100644 tests/utils/moderated_polls/templates/polls/nested_poll.html diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e7a220d..d4004673 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Add to collection should automatically add deeply nested draft versioned objects #205 * fix: Refactor flawed add to collection XSS redirect sanitisation added in 1.0.26 2.0.0 (2022-01-18) diff --git a/djangocms_moderation/helpers.py b/djangocms_moderation/helpers.py index c3f38e62..2405d73d 100644 --- a/djangocms_moderation/helpers.py +++ b/djangocms_moderation/helpers.py @@ -6,6 +6,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from cms.models import CMSPlugin from cms.utils.plugins import downcast_plugins from djangocms_versioning import versionables @@ -157,21 +158,40 @@ def _get_moderatable_version(versionable, grouper, parent_version_filters): return +def _get_nested_moderated_children_from_placeholder_plugin(instance, placeholder, parent_version_filters): + """ + Find all nested versionable objects, traverses through all attached models until it finds + any models that are versioned. + """ + for field in instance._meta.get_fields(): + if not field.is_relation or field.auto_created: + continue + + candidate = getattr(instance, field.name) + + # Break early if the field is None, a placeholder, or is a CMSPlugin instance + # We do this to save unnecessary processing + if not candidate or candidate == placeholder or isinstance(candidate, CMSPlugin): + continue + + try: + versionable = versionables.for_grouper(candidate) + except KeyError: + yield from _get_nested_moderated_children_from_placeholder_plugin( + candidate, placeholder, parent_version_filters + ) + continue + + version = _get_moderatable_version( + versionable, candidate, parent_version_filters + ) + if version: + yield version + + def get_moderated_children_from_placeholder(placeholder, parent_version_filters): """ Get all moderated children version objects from a placeholder """ for plugin in downcast_plugins(placeholder.get_plugins()): - for field in plugin._meta.get_fields(): - if not field.is_relation or field.auto_created: - continue - candidate = getattr(plugin, field.name) - try: - versionable = versionables.for_grouper(candidate) - except KeyError: - continue - version = _get_moderatable_version( - versionable, candidate, parent_version_filters - ) - if version: - yield version + yield from _get_nested_moderated_children_from_placeholder_plugin(plugin, placeholder, parent_version_filters) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 7c6e89e9..bd989141 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -33,6 +33,10 @@ PollPluginFactory, PollVersionFactory, ) +from .utils.moderated_polls.factories import ( + DeeplyNestedPollPluginFactory, + NestedPollPluginFactory, +) @skip("Confirmation page feature doesn't support 1.0.x yet") @@ -234,3 +238,56 @@ def test_get_moderated_children_from_placeholder_gets_correct_versions(self): self.assertEqual(page_1_moderated_children, [pg_1_poll_version]) self.assertEqual(page_2_moderated_children, [pg_2_poll_version]) + + def test_get_moderated_children_from_placeholder_gets_nested_models(self): + """ + Versionable models that are nested inside a custom plugin model + can be found when adding to a collection. + """ + pg_version = PageVersionFactory(created_by=self.user) + language = pg_version.content.language + + # Populate page + placeholder = PlaceholderFactory(source=pg_version.content) + # Moderated plugin + poll_version = PollVersionFactory( + created_by=self.user, content__language=language + ) + # Nested versioned poll + NestedPollPluginFactory(placeholder=placeholder, nested_poll__poll=poll_version.content.poll) + + moderated_children = list( + get_moderated_children_from_placeholder( + placeholder, {"language": pg_version.content.language} + ) + ) + + self.assertEqual(moderated_children, [poll_version]) + + def test_get_moderated_children_from_placeholder_gets_deeply_nested_models(self): + """ + Versionable models that are deeply nested inside a custom plugin model + can be found when adding to a collection. + """ + pg_version = PageVersionFactory(created_by=self.user) + language = pg_version.content.language + + # Populate page + placeholder = PlaceholderFactory(source=pg_version.content) + # Moderated plugin + poll_version = PollVersionFactory( + created_by=self.user, content__language=language + ) + # Deeply nested versioned poll + DeeplyNestedPollPluginFactory( + placeholder=placeholder, + deeply_nested_poll__nested_poll__poll=poll_version.content.poll + ) + + moderated_children = list( + get_moderated_children_from_placeholder( + placeholder, {"language": pg_version.content.language} + ) + ) + + self.assertEqual(moderated_children, [poll_version]) diff --git a/tests/utils/factories.py b/tests/utils/factories.py index 74a44fe4..811f0955 100644 --- a/tests/utils/factories.py +++ b/tests/utils/factories.py @@ -10,6 +10,7 @@ AbstractVersionFactory, PageVersionFactory, ) +from factory.django import DjangoModelFactory from factory.fuzzy import FuzzyChoice, FuzzyInteger, FuzzyText from djangocms_moderation.models import ( @@ -45,7 +46,7 @@ def get_plugin_language(plugin): # also be None unless set manually -class PlaceholderFactory(factory.django.DjangoModelFactory): +class PlaceholderFactory(DjangoModelFactory): default_width = FuzzyInteger(0, 25) slot = FuzzyText(length=2, chars=string.digits) # NOTE: When using this factory you will probably want to set @@ -55,14 +56,14 @@ class Meta: model = Placeholder -class PollFactory(factory.django.DjangoModelFactory): +class PollFactory(DjangoModelFactory): name = FuzzyText(length=6) class Meta: model = Poll -class PollContentFactory(factory.django.DjangoModelFactory): +class PollContentFactory(DjangoModelFactory): poll = factory.SubFactory(PollFactory) language = FuzzyChoice(["en", "fr", "it"]) text = FuzzyText(length=24) @@ -71,7 +72,7 @@ class Meta: model = PollContent -class PollPluginFactory(factory.django.DjangoModelFactory): +class PollPluginFactory(DjangoModelFactory): language = factory.LazyAttribute(get_plugin_language) placeholder = factory.SubFactory(PlaceholderFactory) parent = None @@ -93,14 +94,14 @@ class Meta: # None Moderated Poll App factories -class NoneModeratedPollFactory(factory.django.DjangoModelFactory): +class NoneModeratedPollFactory(DjangoModelFactory): name = FuzzyText(length=6) class Meta: model = NoneModeratedPoll -class NoneModeratedPollContentFactory(factory.django.DjangoModelFactory): +class NoneModeratedPollContentFactory(DjangoModelFactory): poll = factory.SubFactory(NoneModeratedPollFactory) language = FuzzyChoice(["en", "fr", "it"]) text = FuzzyText(length=24) @@ -116,7 +117,7 @@ class Meta: model = Version -class NoneModeratedPollPluginFactory(factory.django.DjangoModelFactory): +class NoneModeratedPollPluginFactory(DjangoModelFactory): language = factory.LazyAttribute(get_plugin_language) placeholder = factory.SubFactory(PlaceholderFactory) parent = None @@ -128,7 +129,7 @@ class Meta: model = NoneModeratedPollPlugin -class UserFactory(factory.django.DjangoModelFactory): +class UserFactory(DjangoModelFactory): username = FuzzyText(length=12) first_name = factory.Faker("first_name") last_name = factory.Faker("last_name") @@ -145,14 +146,14 @@ def _create(cls, model_class, *args, **kwargs): return manager.create_user(*args, **kwargs) -class WorkflowFactory(factory.django.DjangoModelFactory): +class WorkflowFactory(DjangoModelFactory): name = FuzzyText(length=12) class Meta: model = Workflow -class ModerationCollectionFactory(factory.django.DjangoModelFactory): +class ModerationCollectionFactory(DjangoModelFactory): name = FuzzyText(length=12) author = factory.SubFactory(UserFactory) workflow = factory.SubFactory(WorkflowFactory) @@ -161,7 +162,7 @@ class Meta: model = ModerationCollection -class ModerationRequestFactory(factory.django.DjangoModelFactory): +class ModerationRequestFactory(DjangoModelFactory): collection = factory.SubFactory(ModerationCollectionFactory) version = factory.SubFactory(PageVersionFactory) language = 'en' @@ -171,7 +172,7 @@ class Meta: model = ModerationRequest -class RootModerationRequestTreeNodeFactory(factory.django.DjangoModelFactory): +class RootModerationRequestTreeNodeFactory(DjangoModelFactory): moderation_request = factory.SubFactory(ModerationRequestFactory) class Meta: @@ -183,7 +184,7 @@ def _create(cls, model_class, *args, **kwargs): return model_class.add_root(*args, **kwargs) -class ChildModerationRequestTreeNodeFactory(factory.django.DjangoModelFactory): +class ChildModerationRequestTreeNodeFactory(DjangoModelFactory): moderation_request = factory.SubFactory(ModerationRequestFactory) parent = factory.SubFactory(RootModerationRequestTreeNodeFactory) diff --git a/tests/utils/moderated_polls/cms_plugins.py b/tests/utils/moderated_polls/cms_plugins.py index 4603205e..a5cf18ed 100644 --- a/tests/utils/moderated_polls/cms_plugins.py +++ b/tests/utils/moderated_polls/cms_plugins.py @@ -1,7 +1,11 @@ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool -from .models import PollPlugin as Poll +from .models import ( + DeeplyNestedPollPlugin as DeeplyNestedPoll, + NestedPollPlugin as NestedPoll, + PollPlugin as Poll, +) @plugin_pool.register_plugin @@ -10,3 +14,19 @@ class PollPlugin(CMSPluginBase): name = "Poll" allow_children = True render_template = "polls/poll.html" + + +@plugin_pool.register_plugin +class NestedPollPlugin(CMSPluginBase): + model = NestedPoll + name = "NestedPoll" + allow_children = True + render_template = "polls/nested_poll.html" + + +@plugin_pool.register_plugin +class DeeplyNestedPollPlugin(CMSPluginBase): + model = DeeplyNestedPoll + name = "DeeplyNestedPoll" + allow_children = True + render_template = "polls/deeply_nested_poll.html" diff --git a/tests/utils/moderated_polls/factories.py b/tests/utils/moderated_polls/factories.py new file mode 100644 index 00000000..7f555fba --- /dev/null +++ b/tests/utils/moderated_polls/factories.py @@ -0,0 +1,53 @@ +import factory +from factory.django import DjangoModelFactory + +from ..factories import ( + PlaceholderFactory, + PollFactory, + get_plugin_language, + get_plugin_position, +) +from .models import ( + DeeplyNestedPoll, + DeeplyNestedPollPlugin, + NestedPoll, + NestedPollPlugin, +) + + +class NestedPollFactory(DjangoModelFactory): + poll = factory.SubFactory(PollFactory) + + class Meta: + model = NestedPoll + + +class NestedPollPluginFactory(DjangoModelFactory): + language = factory.LazyAttribute(get_plugin_language) + placeholder = factory.SubFactory(PlaceholderFactory) + parent = None + position = factory.LazyAttribute(get_plugin_position) + plugin_type = "NestedPollPlugin" + nested_poll = factory.SubFactory(NestedPollFactory) + + class Meta: + model = NestedPollPlugin + + +class DeeplyNestedPollFactory(DjangoModelFactory): + nested_poll = factory.SubFactory(NestedPollFactory) + + class Meta: + model = DeeplyNestedPoll + + +class DeeplyNestedPollPluginFactory(DjangoModelFactory): + language = factory.LazyAttribute(get_plugin_language) + placeholder = factory.SubFactory(PlaceholderFactory) + parent = None + position = factory.LazyAttribute(get_plugin_position) + plugin_type = "DeeplyNestedPollPlugin" + deeply_nested_poll = factory.SubFactory(DeeplyNestedPollFactory) + + class Meta: + model = DeeplyNestedPollPlugin diff --git a/tests/utils/moderated_polls/models.py b/tests/utils/moderated_polls/models.py index 32437bb6..7ae35cc9 100644 --- a/tests/utils/moderated_polls/models.py +++ b/tests/utils/moderated_polls/models.py @@ -61,3 +61,45 @@ class PollPlugin(CMSPlugin): def __str__(self): return str(self.poll) + + +class NestedPoll(models.Model): + poll = models.ForeignKey(Poll, on_delete=models.CASCADE) + + def __str__(self): + return self.poll + + +class NestedPollPlugin(CMSPlugin): + cmsplugin_ptr = models.OneToOneField( + CMSPlugin, + on_delete=models.CASCADE, + related_name="%(app_label)s_%(class)s", + parent_link=True, + ) + + nested_poll = models.ForeignKey(NestedPoll, on_delete=models.CASCADE) + + def __str__(self): + return str(self.nested_poll) + + +class DeeplyNestedPoll(models.Model): + nested_poll = models.ForeignKey(NestedPoll, on_delete=models.CASCADE) + + def __str__(self): + return self.nested_poll + + +class DeeplyNestedPollPlugin(CMSPlugin): + cmsplugin_ptr = models.OneToOneField( + CMSPlugin, + on_delete=models.CASCADE, + related_name="%(app_label)s_%(class)s", + parent_link=True, + ) + + deeply_nested_poll = models.ForeignKey(DeeplyNestedPoll, on_delete=models.CASCADE) + + def __str__(self): + return str(self.deeply_nested_poll) diff --git a/tests/utils/moderated_polls/templates/polls/deeply_nested_poll.html b/tests/utils/moderated_polls/templates/polls/deeply_nested_poll.html new file mode 100644 index 00000000..ce2d8874 --- /dev/null +++ b/tests/utils/moderated_polls/templates/polls/deeply_nested_poll.html @@ -0,0 +1 @@ +{% load polls_tags %}{% render_poll instance.deeply_nested_poll.nested_poll.poll %} diff --git a/tests/utils/moderated_polls/templates/polls/nested_poll.html b/tests/utils/moderated_polls/templates/polls/nested_poll.html new file mode 100644 index 00000000..7d62f3ef --- /dev/null +++ b/tests/utils/moderated_polls/templates/polls/nested_poll.html @@ -0,0 +1 @@ +{% load polls_tags %}{% render_poll instance.nested_poll.poll %} From 2b91e09ff2dd7a92ff979748b676ad3a29cbd146 Mon Sep 17 00:00:00 2001 From: Simon Krull Date: Sun, 13 Feb 2022 14:00:06 +0100 Subject: [PATCH 04/91] TASK: adjust setup metadata (#209) * TASK: adjust setup metadata * BUGFIX: add missing , --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0835848d..3a490fe6 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,9 @@ install_requires=INSTALL_REQUIREMENTS, author="Divio AG", author_email="info@divio.ch", - url="http://github.com/divio/djangocms-moderation", + maintainer='Django CMS Association and contributors', + maintainer_email='info@django-cms.org', + url="http://github.com/django-cms/djangocms-moderation", license="BSD", test_suite="tests.settings.run", ) From 6b8c63993b82e4b741c085fe70e961507f445666 Mon Sep 17 00:00:00 2001 From: Aiky30 Date: Wed, 16 Mar 2022 14:34:20 +0000 Subject: [PATCH 05/91] fix: Avoid errors thrown when nested plugins are M2M fields (#210) --- CHANGELOG.rst | 1 + djangocms_moderation/helpers.py | 5 +++ tests/test_helpers.py | 31 +++++++++++++++++++ tests/utils/moderated_polls/cms_plugins.py | 9 ++++++ tests/utils/moderated_polls/factories.py | 23 ++++++++++++++ tests/utils/moderated_polls/models.py | 14 +++++++++ .../templates/polls/many_polls.html | 1 + 7 files changed, 84 insertions(+) create mode 100644 tests/utils/moderated_polls/templates/polls/many_polls.html diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d4004673..408bdc1d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Avoid errors thrown when nested plugins are M2M fields * fix: Add to collection should automatically add deeply nested draft versioned objects #205 * fix: Refactor flawed add to collection XSS redirect sanitisation added in 1.0.26 diff --git a/djangocms_moderation/helpers.py b/djangocms_moderation/helpers.py index 2405d73d..25249302 100644 --- a/djangocms_moderation/helpers.py +++ b/djangocms_moderation/helpers.py @@ -163,6 +163,11 @@ def _get_nested_moderated_children_from_placeholder_plugin(instance, placeholder Find all nested versionable objects, traverses through all attached models until it finds any models that are versioned. """ + # Catch Many to many fields that don't have _meta + # FIXME: Handle nested M2M instances + if not hasattr(instance, "_meta"): + return + for field in instance._meta.get_fields(): if not field.is_relation or field.auto_created: continue diff --git a/tests/test_helpers.py b/tests/test_helpers.py index bd989141..ebdacf10 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -35,6 +35,7 @@ ) from .utils.moderated_polls.factories import ( DeeplyNestedPollPluginFactory, + ManytoManyPollPluginFactory, NestedPollPluginFactory, ) @@ -264,6 +265,36 @@ def test_get_moderated_children_from_placeholder_gets_nested_models(self): self.assertEqual(moderated_children, [poll_version]) + def test_get_moderated_children_from_placeholder_gets_plugin_with_m2m_fields(self): + """ + FIXME: Currently only checks that a M2M nested poll entry does not cause an error. + """ + pg_version = PageVersionFactory(created_by=self.user) + language = pg_version.content.language + placeholder = PlaceholderFactory(source=pg_version.content) + poll_1_version = PollVersionFactory( + created_by=self.user, content__language=language + ) + poll_2_version = PollVersionFactory( + created_by=self.user, content__language=language + ) + ManytoManyPollPluginFactory(placeholder=placeholder, polls=[ + poll_1_version.content.poll, + poll_2_version.content.poll, + ]) + + moderated_children = list( + get_moderated_children_from_placeholder( + placeholder, {"language": pg_version.content.language} + ) + ) + + # FIXME: + # This test is only covering that the M2M field doesn't cause an error. + # It should see that the nested polls are found + # self.assertEqual(moderated_children, [poll_1_version, poll_2_version]) + self.assertEqual(moderated_children, []) + def test_get_moderated_children_from_placeholder_gets_deeply_nested_models(self): """ Versionable models that are deeply nested inside a custom plugin model diff --git a/tests/utils/moderated_polls/cms_plugins.py b/tests/utils/moderated_polls/cms_plugins.py index a5cf18ed..0e5cd10b 100644 --- a/tests/utils/moderated_polls/cms_plugins.py +++ b/tests/utils/moderated_polls/cms_plugins.py @@ -3,6 +3,7 @@ from .models import ( DeeplyNestedPollPlugin as DeeplyNestedPoll, + ManytoManyPollPlugin as ManytoManyPoll, NestedPollPlugin as NestedPoll, PollPlugin as Poll, ) @@ -30,3 +31,11 @@ class DeeplyNestedPollPlugin(CMSPluginBase): name = "DeeplyNestedPoll" allow_children = True render_template = "polls/deeply_nested_poll.html" + + +@plugin_pool.register_plugin +class ManytoManyPollPlugin(CMSPluginBase): + model = ManytoManyPoll + name = "ManytoManyPoll" + allow_children = True + render_template = "polls/many_polls.html" diff --git a/tests/utils/moderated_polls/factories.py b/tests/utils/moderated_polls/factories.py index 7f555fba..4819f262 100644 --- a/tests/utils/moderated_polls/factories.py +++ b/tests/utils/moderated_polls/factories.py @@ -10,6 +10,7 @@ from .models import ( DeeplyNestedPoll, DeeplyNestedPollPlugin, + ManytoManyPollPlugin, NestedPoll, NestedPollPlugin, ) @@ -22,6 +23,28 @@ class Meta: model = NestedPoll +class ManytoManyPollPluginFactory(DjangoModelFactory): + language = factory.LazyAttribute(get_plugin_language) + placeholder = factory.SubFactory(PlaceholderFactory) + parent = None + position = factory.LazyAttribute(get_plugin_position) + plugin_type = "ManytoManyPollPlugin" + + class Meta: + model = ManytoManyPollPlugin + + @factory.post_generation + def polls(self, create, extracted, **kwargs): + if not create: + # Simple build, do nothing. + return + + if extracted: + # A list of groups were passed in, use them + for poll in extracted: + self.polls.add(poll) + + class NestedPollPluginFactory(DjangoModelFactory): language = factory.LazyAttribute(get_plugin_language) placeholder = factory.SubFactory(PlaceholderFactory) diff --git a/tests/utils/moderated_polls/models.py b/tests/utils/moderated_polls/models.py index 7ae35cc9..81147b54 100644 --- a/tests/utils/moderated_polls/models.py +++ b/tests/utils/moderated_polls/models.py @@ -103,3 +103,17 @@ class DeeplyNestedPollPlugin(CMSPlugin): def __str__(self): return str(self.deeply_nested_poll) + + +class ManytoManyPollPlugin(CMSPlugin): + cmsplugin_ptr = models.OneToOneField( + CMSPlugin, + on_delete=models.CASCADE, + related_name="%(app_label)s_%(class)s", + parent_link=True, + ) + + polls = models.ManyToManyField(Poll) + + def __str__(self): + return str(self.polls.first()) diff --git a/tests/utils/moderated_polls/templates/polls/many_polls.html b/tests/utils/moderated_polls/templates/polls/many_polls.html new file mode 100644 index 00000000..d419d98b --- /dev/null +++ b/tests/utils/moderated_polls/templates/polls/many_polls.html @@ -0,0 +1 @@ +{% load polls_tags %}{% render_poll instance.polls.first %} From a2be186e592f606300fa24ac2e688fcd8676fa28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Apr 2022 03:12:21 +0100 Subject: [PATCH 06/91] Bump minimist from 1.2.3 to 1.2.6 (#212) Bumps [minimist](https://github.com/substack/minimist) from 1.2.3 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.3...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 483fbbf9..a9ab3ae4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5742,9 +5742,9 @@ } }, "minimist": { - "version": "1.2.3", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.3.tgz", - "integrity": "sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw==", + "version": "1.2.6", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mixin-deep": { diff --git a/package.json b/package.json index af5a7748..20f8cd45 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "gulp-util": "3.0.5", "imports-loader": "^0.7.1", "karma-sourcemap-loader": "^0.3.7", - "minimist": "1.2.3", + "minimist": "1.2.6", "webpack": "^3.0.0" }, "dependencies": { From 3df17cbb2f1a5d8e1f39a159b9cd3aae9fbc9416 Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Thu, 23 Jun 2022 16:21:08 +0100 Subject: [PATCH 07/91] feat: Added burger menu to moderation request tree admin. (#216) Added JS and CSS to consolidate the separate moderation request list_actions into a burger menu. --- CHANGELOG.rst | 1 + djangocms_moderation/admin.py | 6 +- .../djangocms_moderation/css/burger.css | 172 ++++++++++++++++ .../static/djangocms_moderation/js/burger.js | 193 ++++++++++++++++++ tests/requirements/requirements_base.txt | 1 + tests/settings.py | 4 +- tests/test_admin.py | 13 ++ tests/test_app_registration.py | 2 +- 8 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 djangocms_moderation/static/djangocms_moderation/css/burger.css create mode 100644 djangocms_moderation/static/djangocms_moderation/js/burger.js diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 408bdc1d..a8626ea0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* feat: Added burgermenu for ModerationRequestTreeAdmin icons * fix: Avoid errors thrown when nested plugins are M2M fields * fix: Add to collection should automatically add deeply nested draft versioned objects #205 * fix: Refactor flawed add to collection XSS redirect sanitisation added in 1.0.26 diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index 6e66ca7e..c2e76928 100644 --- a/djangocms_moderation/admin.py +++ b/djangocms_moderation/admin.py @@ -114,8 +114,10 @@ class ModerationRequestTreeAdmin(TreeAdmin): more than once, i.e. they are present in more than one parent. """ class Media: - js = ("djangocms_moderation/js/actions.js",) - css = {"all": ("djangocms_moderation/css/actions.css",)} + js = ("djangocms_moderation/js/actions.js", "djangocms_moderation/js/burger.js") + css = { + "all": ("djangocms_moderation/css/actions.css", "djangocms_moderation/css/burger.css") + } actions = [ # filtered out in `self.get_actions` delete_selected, diff --git a/djangocms_moderation/static/djangocms_moderation/css/burger.css b/djangocms_moderation/static/djangocms_moderation/css/burger.css new file mode 100644 index 00000000..c38ffa86 --- /dev/null +++ b/djangocms_moderation/static/djangocms_moderation/css/burger.css @@ -0,0 +1,172 @@ +/*------------------------------------- +Classes for Action btn & Burger menu +---------------------------------------*/ + +a.btn.cms-moderation-action-btn { + position: relative; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + padding: 0 !important; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + width: 34px; + height: 34px; + margin-top: -12px !important; + position: relative; + bottom: -6px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + cursor: pointer; +} +a.btn.cms-moderation-action-btn img { + width: 20px; + height: 20px; +} + +a.btn.cms-moderation-action-btn.inactive { + opacity: 0.3; + filter: alpha(opacity=30); +} + + +/* disable clicking for inactive buttons */ +.btn.cms-moderation-action-btn.inactive { + pointer-events: none; + background-color: #e1e1e1 !important; +} + +.btn.cms-moderation-action-btn.inactive img { + opacity: 0.5; +} + +/* set size and spacing between for the action icons */ +a.btn.cms-moderation-action-btn img { + width: 20px; + height: 20px; + margin-right: 4px; +} + + +/*------------------------------------- +This governs the drop-down behaviour +extending the pagetree classes provided by CMS +---------------------------------------*/ + +.cms-actions-dropdown-menu { + display: none; + position: absolute; + top: 30px; + right: -1px; + z-index: 1000; + min-width: 180px; + margin: 0; + padding: 0 !important; + border-radius: 5px; + background: #fff; + box-shadow: 0 0 10px rgba(0,0,0,.25); + -webkit-transform: translateZ(0); + transform: translateZ(0); +} + +/* Dropdown menu shadow */ +.cms-actions-dropdown-menu::before { + content: ""; + position: absolute; + left: 100%; + z-index: -1; + width: 10px; + height: 10px; + margin-left: -5px; + background-color: #fff; + box-shadow: 0 0 10px rgba(0,0,0,.25); + -webkit-transform: rotate(45deg) translateZ(0); + transform: rotate(45deg) translateZ(0); + } + +.cms-actions-dropdown-menu.open { + display: block; + width: 200px; +} + +.cms-actions-dropdown-menu.closed { + display: none; +} + +.cms-actions-dropdown-menu-arrow-right-top::before { + top: 16px; +} + +/* add shadow on burger menu trigger */ +a.btn.cms-moderation-action-btn:hover, a.btn.cms-moderation-action-btn.open { + box-shadow: inset 0 3px 5px rgba(0,0,0,.125); +} + +/* style for each option row */ +ul.cms-actions-dropdown-menu-inner { + margin: 0; + padding: 0 !important; + border-radius: 5px; + background-color: #fff; +} + +ul.cms-actions-dropdown-menu-inner li { + border: 1px solid transparent; + border-radius: 5px; + padding: 2px 6px; + list-style-type: none; +} +ul.cms-actions-dropdown-menu-inner li:hover { + border: 1px solid #ccc; + background-color: #0bf; +} + +a.cms-actions-dropdown-menu-item-anchor { + display: block; + line-height: 1.5; + text-align: left; + text-decoration: none; + padding: 10px 15px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +/* Explicitly defining anchor states to overwrite djangocms-admin styles! */ +a.cms-actions-dropdown-menu-item-anchor, +a.cms-actions-dropdown-menu-item-anchor:visited, +a.cms-actions-dropdown-menu-item-anchor:link, +a.cms-actions-dropdown-menu-item-anchor:link:visited +{ + color: #666 !important; +} +a.cms-actions-dropdown-menu-item-anchor:hover, +a.cms-actions-dropdown-menu-item-anchor:active, +a.cms-actions-dropdown-menu-item-anchor:link:hover, +a.cms-actions-dropdown-menu-item-anchor:link:active +{ + color: #fff !important; + background: #0bf; +} + +/* set the size of the option icon */ +a.cms-actions-dropdown-menu-item-anchor img { + width: 20px; + height: 20px; +} +/* align the option text with it's icon */ +a.cms-actions-dropdown-menu-item-anchor span { + line-height: 1rem; + vertical-align: 20%; + margin-left: 10px; +} +/* disable any inactive option */ +a.cms-actions-dropdown-menu-item-anchor.inactive { + cursor: not-allowed; + pointer-events: none; + opacity: 0.3; + filter: alpha(opacity=30); +} diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js new file mode 100644 index 00000000..5dd7eb2e --- /dev/null +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -0,0 +1,193 @@ +(function ($) { + if (!$) { + return; + } + + $(function () { + // INFO: it is not possible to put a form inside a form, so the moderation actions have to create their own form + // on click. + $(` .cms-moderation-action-btn, + .js-moderation-action, + .cms-actions-dropdown-menu-item-anchor`) + .on('click', function (e) { + e.preventDefault(); + + // action currently being targetted + let action = $(e.currentTarget); + // get the form method being used? + let formMethod = action.attr('class').indexOf('cms-form-get-method') === 1 ? 'POST' : 'GET'; + let csrfToken = formMethod === 'GET' ? '' : ''; + let fakeForm = $( + '
' + csrfToken + + '
' + ); + let body = window.top.document.body; + let keepSideFrame = action.attr('class').indexOf('js-versioning-keep-sideframe') !== -1; + + // always break out of the sideframe, cause it was never meant to open cms views inside it + try { + if (!keepSideFrame) { + window.top.CMS.API.Sideframe.close(); + } + } catch (err) { } + if (keepSideFrame) { + body = window.document.body; + } + + fakeForm.appendTo(body).submit(); + }); + + $('.js-versioning-close-sideframe').on('click', function () { + try { + window.top.CMS.API.Sideframe.close(); + } catch (e) { } + }); + }); + + // Hide django messages after timeout occurs to prevent content overlap + $('document').ready(function () { + // Targeting first item returned (there's only ever one messagelist per template): + let messageList = document.getElementsByClassName('messagelist')[0]; + let interval = 20; + let timeout = 500; + + if (messageList !== undefined) { + for (let item of messageList.children) { + item.style.opacity = 1; + setTimeout(() => { + let fader = setInterval(() => { + item.style.opacity -= 0.05; + if (item.style.opacity < 0) { + item.style.display = 'none'; + clearInterval(fader); + } + + }, interval); + }, timeout); + } + } + }); + + let closeBurgerMenu = function closeBurgerMenu() { + $('.cms-actions-dropdown-menu').removeClass('open'); + $('.cms-actions-dropdown-menu').addClass('closed'); + $('.cms-moderation-action-btn').removeClass('open'); + $('.cms-moderation-action-btn').addClass('closed'); + }; + + let toggleBurgerMenu = function toggleBurgerMenu(burgerMenuAnchor, optionsContainer) { + let bm = $(burgerMenuAnchor); + let op = $(optionsContainer); + let closed = bm.hasClass('closed'); + + closeBurgerMenu(); + + if (closed) { + bm.removeClass('closed').addClass('open'); + op.removeClass('closed').addClass('open'); + } else { + bm.addClass('closed').removeClass('open'); + op.addClass('closed').removeClass('open'); + } + + let pos = bm.offset(); + let leftOffset = 200; + + op.css('left', pos.left - leftOffset); + op.css('top', pos.top); + }; + + // Create burger menu: + $(function () { + let burger_menu_icon; + + burger_menu_icon = '/static/djangocms_versioning/svg/menu.svg'; + + let createBurgerMenu = function createBurgerMenu(row) { + + let actions = $(row).children('.field-list_display_actions'); + + if (!actions.length) { + /* skip any rows without actions to avoid errors */ + return; + } + + /* create burger menu anchor icon */ + let anchor = document.createElement('a'); + let icon = document.createElement('img'); + + icon.setAttribute('src', burger_menu_icon); + anchor.setAttribute('class', 'btn cms-moderation-action-btn closed'); + anchor.setAttribute('title', 'Actions'); + anchor.appendChild(icon); + + /* create options container */ + let optionsContainer = document.createElement('div'); + let ul = document.createElement('ul'); + + /* 'cms-actions-dropdown-menu' class is the main selector for the menu, + 'cms-actions-dropdown-menu-arrow-right-top' keeps the menu arrow in position. */ + optionsContainer.setAttribute( + 'class', + 'cms-actions-dropdown-menu cms-actions-dropdown-menu-arrow-right-top'); + ul.setAttribute('class', 'cms-actions-dropdown-menu-inner'); + + /* get the existing actions and move them into the options container */ + $(actions[0]).children('.cms-moderation-action-btn').each(function (index, item) { + + let li = document.createElement('li'); + /* create an anchor from the item */ + let li_anchor = document.createElement('a'); + + li_anchor.setAttribute('class', 'cms-actions-dropdown-menu-item-anchor'); + li_anchor.setAttribute('href', $(item).attr('href')); + + if ($(item).hasClass('cms-form-get-method')) { + // Ensure the fake-form selector is propagated to the new anchor + li_anchor.classList.add('cms-form-get-method'); + } + /* get the span which contains the img */ + let value = $(item).children('span')[0]; + + /* move the icon image */ + li_anchor.appendChild($(value).children('img')[0]); + + /* create the button text and construct the button */ + let span = document.createElement('span'); + + span.appendChild( + document.createTextNode(item.title) + ); + + li_anchor.appendChild(span); + li.appendChild(li_anchor); + ul.appendChild(li); + + /* destroy original replaced buttons */ + actions[0].removeChild(item); + }); + + /* add the options to the drop-down */ + optionsContainer.appendChild(ul); + actions[0].appendChild(anchor); + document.body.appendChild(optionsContainer); + + /* listen for burger menu clicks */ + anchor.addEventListener('click', function (ev) { + ev.stopPropagation(); + toggleBurgerMenu(anchor, optionsContainer); + }); + + /* close burger menu if clicking outside */ + $(window).click(function () { + closeBurgerMenu(); + }); + }; + + $('#result_list').find('tr').each(function (index, item) { + createBurgerMenu(item); + }); + }); +})((typeof django !== 'undefined' && django.jQuery) || (typeof CMS !== 'undefined' && CMS.$) || false); diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index b7cd9390..c59a7250 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -18,3 +18,4 @@ https://github.com/django-cms/django-cms/tarball/develop-4#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/master#egg=djangocms-versioning https://github.com/FidelityInternational/djangocms-version-locking/tarball/master#egg=djangocms-version-locking +https://github.com/django-cms/djangocms-alias/tarball/master#egg=djangocms-alias diff --git a/tests/settings.py b/tests/settings.py index 67561ba3..c016c9f2 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -5,13 +5,12 @@ "tests.utils.app_2", "djangocms_versioning", "djangocms_version_locking", - # the following 4 apps are related "aldryn_forms", "filer", "easy_thumbnails", "captcha", - + "djangocms_alias", "djangocms_text_ckeditor", "tests.utils.moderated_polls", "tests.utils.versioned_none_moderated_app", @@ -22,6 +21,7 @@ "auth": None, "cms": None, "menus": None, + "djangocms_alias": None, "djangocms_versioning": None, "djangocms_version_locking": None, "filer": None, diff --git a/tests/test_admin.py b/tests/test_admin.py index b72e1eae..dad3a0cd 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -352,6 +352,7 @@ def test_tree_admin_list_links_to_moderation_request_change_view(self): ), self.mr1.pk, ) + self.assertHTMLEqual(result, expected) @@ -394,6 +395,7 @@ def setUp(self): self.mr_tree_admin = ModerationRequestTreeAdmin( ModerationRequest, admin.AdminSite() ) + self.mra = ModerationRequestAdmin(ModerationRequest, admin.AdminSite()) self.mca = ModerationCollectionAdmin(ModerationCollection, admin.AdminSite()) @@ -412,3 +414,14 @@ def test_tree_admin_change_list_shows_additional_configured_fields(self): self.assertIn("get_poll_additional_changelist_field", self.mr_tree_admin.get_list_display(mock_request)) + + def test_tree_admin_burger_menu_present(self): + redirect_url = reverse('admin:djangocms_moderation_moderationrequest_changelist') + url = "{}?moderation_request__collection__id={}".format( + redirect_url, + self.collection.id + ) + with self.login_user_context(self.user): + response = self.client.get(url) + + self.assertContains(response, '/static/djangocms_moderation/js/burger.js') diff --git a/tests/test_app_registration.py b/tests/test_app_registration.py index f29a1a53..1b2423dc 100644 --- a/tests/test_app_registration.py +++ b/tests/test_app_registration.py @@ -87,7 +87,7 @@ def test_config_with_two_apps(self): for model in self.moderated_models: self.assertIn(model, registered_model) - self.assertEqual(len(registered_model), 6) + self.assertEqual(len(registered_model), 7) class CMSConfigCheck(CMSTestCase): From 324afaa696dc7f96eb0a5dc660289d33dbb1415e Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Thu, 23 Jun 2022 16:46:01 +0100 Subject: [PATCH 08/91] Release 2.1.0 --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a8626ea0..b9042957 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.0 (2022-06-23) +================== * feat: Added burgermenu for ModerationRequestTreeAdmin icons * fix: Avoid errors thrown when nested plugins are M2M fields * fix: Add to collection should automatically add deeply nested draft versioned objects #205 diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index c5156840..b4c6565c 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.0.0" +__version__ = "2.1.0" default_app_config = "djangocms_moderation.apps.ModerationConfig" From 4c450dfa75afe456acccbb9f6ce5c2371309686a Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 10:14:46 +0100 Subject: [PATCH 09/91] fix: Updated moderation_request_change_list to inject the correct static location --- CHANGELOG.rst | 1 + .../static/djangocms_moderation/js/burger.js | 7 +++++++ .../static/djangocms_moderation/svg/menu.svg | 2 ++ .../moderation_request_change_list.html | 6 +++++- 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 djangocms_moderation/static/djangocms_moderation/svg/menu.svg diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b9042957..e778513b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Updated moderation_request_change_list to pass static url for svg asset in burger menu 2.1.0 (2022-06-23) ================== diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index 5dd7eb2e..d46bb93c 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -103,6 +103,13 @@ $(function () { let burger_menu_icon; + if (typeof moderation_static_url_prefix === 'undefined') { + burger_menu_icon = '/static/djangocms_versioning/svg/menu.svg'; + } else { + // eslint-disable-next-line no-undef + burger_menu_icon = `${moderation_static_url_prefix}svg/menu.svg`; + } + burger_menu_icon = '/static/djangocms_versioning/svg/menu.svg'; let createBurgerMenu = function createBurgerMenu(row) { diff --git a/djangocms_moderation/static/djangocms_moderation/svg/menu.svg b/djangocms_moderation/static/djangocms_moderation/svg/menu.svg new file mode 100644 index 00000000..30e00516 --- /dev/null +++ b/djangocms_moderation/static/djangocms_moderation/svg/menu.svg @@ -0,0 +1,2 @@ + + diff --git a/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html b/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html index 5cc7f11c..da65fed5 100644 --- a/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html +++ b/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html @@ -1,7 +1,11 @@ {% extends "admin/djangocms_moderation/treebeard/tree_change_list.html" %} -{% load i18n cms_static %} +{% load i18n cms_static static %} {% block extrahead %} + {# INFO: moderation_static_url_prefix variable is used to inject static_url into actions.js #} + {{ block.super }} {# INFO: we need to add styles here instead of "extrastyle" to avoid From a22e3394aeeae6d8b7689a123d1e4bd7f06b0a9d Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 10:34:19 +0100 Subject: [PATCH 10/91] Release 2.1.1 --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e778513b..2701d1d8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.1 (2022-06-24) +================== * fix: Updated moderation_request_change_list to pass static url for svg asset in burger menu 2.1.0 (2022-06-23) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index b4c6565c..f6d0ddfe 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.0" +__version__ = "2.1.1" default_app_config = "djangocms_moderation.apps.ModerationConfig" From 27cc7f2a4fd937f64af0f4ec8a64bdbe88d69a2c Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 11:25:17 +0100 Subject: [PATCH 11/91] fix: Burger menu js altered to prioritise the correct static url --- CHANGELOG.rst | 1 + djangocms_moderation/static/djangocms_moderation/js/burger.js | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2701d1d8..3c3ea3b9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Alter burger menu js to prioritise injected static url 2.1.1 (2022-06-24) ================== diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index d46bb93c..a30e5e1a 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -104,14 +104,12 @@ let burger_menu_icon; if (typeof moderation_static_url_prefix === 'undefined') { - burger_menu_icon = '/static/djangocms_versioning/svg/menu.svg'; + burger_menu_icon = '/static/djangocms_moderation/svg/menu.svg'; } else { // eslint-disable-next-line no-undef burger_menu_icon = `${moderation_static_url_prefix}svg/menu.svg`; } - burger_menu_icon = '/static/djangocms_versioning/svg/menu.svg'; - let createBurgerMenu = function createBurgerMenu(row) { let actions = $(row).children('.field-list_display_actions'); From e589ac1c95354f57d319eae044dc4fc3382cc7d5 Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 11:41:53 +0100 Subject: [PATCH 12/91] Release 2.1.2 --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3c3ea3b9..d0c0afb9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.2 (2022-06-24) +================== * fix: Alter burger menu js to prioritise injected static url 2.1.1 (2022-06-24) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index f6d0ddfe..640946ae 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.1" +__version__ = "2.1.2" default_app_config = "djangocms_moderation.apps.ModerationConfig" From e14e28402703e93c285f341a12d295c190abbc6f Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 13:43:16 +0100 Subject: [PATCH 13/91] Fix: ModerationRequestAdmin retain modal html classes --- CHANGELOG.rst | 1 + .../static/djangocms_moderation/css/burger.css | 5 +++++ .../static/djangocms_moderation/js/burger.js | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d0c0afb9..69c199ed 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Retain classes which define whether a link should open in a modal or not 2.1.2 (2022-06-24) ================== diff --git a/djangocms_moderation/static/djangocms_moderation/css/burger.css b/djangocms_moderation/static/djangocms_moderation/css/burger.css index c38ffa86..61750a05 100644 --- a/djangocms_moderation/static/djangocms_moderation/css/burger.css +++ b/djangocms_moderation/static/djangocms_moderation/css/burger.css @@ -170,3 +170,8 @@ a.cms-actions-dropdown-menu-item-anchor.inactive { opacity: 0.3; filter: alpha(opacity=30); } +/* Override the related-widget-wrapper-link css from the cms, only if it's within the ModerationRequestAdmin */ +a.cms-actions-dropdown-menu-item-anchor.related-widget-wrapper-link { + width: auto !important; + height: auto !important; +} diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index a30e5e1a..cec27d9b 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -153,6 +153,10 @@ // Ensure the fake-form selector is propagated to the new anchor li_anchor.classList.add('cms-form-get-method'); } + if ($(item).hasClass('related-widget-wrapper-link')) { + // Ensure we retain the class which defines whether an item opens in a modal + li_anchor.classList.add('related-widget-wrapper-link'); + } /* get the span which contains the img */ let value = $(item).children('span')[0]; From fbe7a6a8281514b1345d60245d2980a27d719781 Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Fri, 24 Jun 2022 13:49:34 +0100 Subject: [PATCH 14/91] Release 2.1.3 --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 69c199ed..b5c61d59 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.3 (2022-06-24) +================== * fix: Retain classes which define whether a link should open in a modal or not 2.1.2 (2022-06-24) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 640946ae..de397662 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.2" +__version__ = "2.1.3" default_app_config = "djangocms_moderation.apps.ModerationConfig" From e4958786074d9c3abff21fc7058a444ba5db2c33 Mon Sep 17 00:00:00 2001 From: Andrew Aikman Date: Fri, 8 Jul 2022 13:30:49 +0100 Subject: [PATCH 15/91] fix: Broken markup and js scripts in the ModerationRequest changelist view (#224) --- CHANGELOG.rst | 1 + djangocms_moderation/admin.py | 10 +++++++--- .../moderation_request_change_list.html | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b5c61d59..37e66b51 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Broken markup and js scripts in the ModerationRequest changelist views 2.1.3 (2022-06-24) ================== diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index c2e76928..460bb8ee 100644 --- a/djangocms_moderation/admin.py +++ b/djangocms_moderation/admin.py @@ -114,7 +114,11 @@ class ModerationRequestTreeAdmin(TreeAdmin): more than once, i.e. they are present in more than one parent. """ class Media: - js = ("djangocms_moderation/js/actions.js", "djangocms_moderation/js/burger.js") + js = ( + "admin/js/jquery.init.js", + "djangocms_moderation/js/actions.js", + "djangocms_moderation/js/burger.js", + ) css = { "all": ("djangocms_moderation/css/actions.css", "djangocms_moderation/css/burger.css") } @@ -464,7 +468,7 @@ def _traverse_moderation_nodes(node_item): class ModerationRequestAdmin(admin.ModelAdmin): class Media: - js = ('djangocms_moderation/js/actions.js',) + js = ('admin/js/jquery.init.js', 'djangocms_moderation/js/actions.js',) inlines = [ModerationRequestActionInline] @@ -1000,7 +1004,7 @@ class WorkflowAdmin(admin.ModelAdmin): class ModerationCollectionAdmin(admin.ModelAdmin): class Media: - js = ("djangocms_moderation/js/actions.js",) + js = ("admin/js/jquery.init.js", "djangocms_moderation/js/actions.js",) css = {"all": ("djangocms_moderation/css/actions.css",)} actions = None # remove `delete_selected` for now, it will be handled later diff --git a/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html b/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html index da65fed5..7681dcab 100644 --- a/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html +++ b/djangocms_moderation/templates/djangocms_moderation/moderation_request_change_list.html @@ -4,14 +4,14 @@ {% block extrahead %} {# INFO: moderation_static_url_prefix variable is used to inject static_url into actions.js #} {{ block.super }} - {# - INFO: we need to add styles here instead of "extrastyle" to avoid + {% comment "INFO" %} + We need to add styles here instead of "extrastyle" to avoid conflicts with adminstyle. We are adding cms.base.css to gain the icon support, e.g. `` - #} + {% endcomment %} {% endblock extrahead %} From 8bc91a9c8bad9a77a3de68f81aa5c05452c71f4f Mon Sep 17 00:00:00 2001 From: Andrew Aikman Date: Fri, 8 Jul 2022 14:00:07 +0100 Subject: [PATCH 16/91] Release 2.1.4 (#225) --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 37e66b51..7a402344 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.4 (2022-07-08) +================== * fix: Broken markup and js scripts in the ModerationRequest changelist views 2.1.3 (2022-06-24) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index de397662..7251dc00 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.3" +__version__ = "2.1.4" default_app_config = "djangocms_moderation.apps.ModerationConfig" From fc01b3af48a1faeb7d8d7f2b0b0d3b9988ab811a Mon Sep 17 00:00:00 2001 From: Andrew Aikman Date: Mon, 11 Jul 2022 11:58:47 +0100 Subject: [PATCH 17/91] fix: Broken markup in the ModerationCollection changelist view, and missing attributes in the burger menu (#226) --- CHANGELOG.rst | 1 + .../static/djangocms_moderation/js/burger.js | 10 ++++++++++ .../moderationcollection/change_list.html | 8 ++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7a402344..c077d8c9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* fix: Broken markup in the ModerationCollection changelist view, and missing attributes in the burger menu 2.1.4 (2022-07-08) ================== diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index cec27d9b..85f6fa8d 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -145,9 +145,19 @@ let li = document.createElement('li'); /* create an anchor from the item */ let li_anchor = document.createElement('a'); + const itemId = $(item).attr('id'); + const itemTarget = $(item).attr('target'); li_anchor.setAttribute('class', 'cms-actions-dropdown-menu-item-anchor'); li_anchor.setAttribute('href', $(item).attr('href')); + // Copy the id attribute if it is set + if (itemId !== undefined) { + li_anchor.setAttribute('id', itemId); + } + // Copy the target attribute if it is set + if (itemTarget !== undefined) { + li_anchor.setAttribute('target', itemTarget); + } if ($(item).hasClass('cms-form-get-method')) { // Ensure the fake-form selector is propagated to the new anchor diff --git a/djangocms_moderation/templates/admin/djangocms_moderation/moderationcollection/change_list.html b/djangocms_moderation/templates/admin/djangocms_moderation/moderationcollection/change_list.html index f934f9f8..5c2b68f0 100644 --- a/djangocms_moderation/templates/admin/djangocms_moderation/moderationcollection/change_list.html +++ b/djangocms_moderation/templates/admin/djangocms_moderation/moderationcollection/change_list.html @@ -2,10 +2,10 @@ {% load i18n cms_static %} {% block extrahead %} {{ block.super }} - {# - INFO: we need to add styles here instead of "extrastyle" to avoid + {% comment "INFO" %} + We need to add styles here instead of "extrastyle" to avoid conflicts with adminstyle. We are adding cms.base.css to gain the icon support, e.g. `` - #} + {% endcomment %} -{% endblock extrahead %} \ No newline at end of file +{% endblock extrahead %} From 597316bd9d931221a757b0eb0c5200ebdb41b77c Mon Sep 17 00:00:00 2001 From: Andrew Aikman Date: Mon, 11 Jul 2022 13:46:57 +0100 Subject: [PATCH 18/91] Release 2.1.5 (#227) --- CHANGELOG.rst | 3 +++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c077d8c9..78626741 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog Unreleased ========== + +2.1.5 (2022-07-11) +================== * fix: Broken markup in the ModerationCollection changelist view, and missing attributes in the burger menu 2.1.4 (2022-07-08) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 7251dc00..4cfbecb1 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.4" +__version__ = "2.1.5" default_app_config = "djangocms_moderation.apps.ModerationConfig" From ae36f30ac75785cee47154071fd92d0f00ac413d Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Wed, 7 Sep 2022 14:26:13 +0100 Subject: [PATCH 19/91] Fix: Language max length too small (#229) Language max length assumes all language code will be 5 or less characters, this does not apply to some east Asian language codes, which require a higher limit. As the language field uses choices, and these are configured in settings, I have increased the max_length 25 to include some wiggle room for future language code updates. --- .../migrations/0017_auto_20220831_0727.py | 19 +++++++++++++++++++ djangocms_moderation/models.py | 2 +- tests/requirements/requirements_base.txt | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 djangocms_moderation/migrations/0017_auto_20220831_0727.py diff --git a/djangocms_moderation/migrations/0017_auto_20220831_0727.py b/djangocms_moderation/migrations/0017_auto_20220831_0727.py new file mode 100644 index 00000000..df1d65d6 --- /dev/null +++ b/djangocms_moderation/migrations/0017_auto_20220831_0727.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.24 on 2022-08-31 07:27 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('djangocms_moderation', '0016_moderationrequesttreenode'), + ] + + operations = [ + migrations.AlterField( + model_name='moderationrequest', + name='language', + field=models.CharField(choices=settings.LANGUAGES, max_length=20, verbose_name='language'), + ) + ] diff --git a/djangocms_moderation/models.py b/djangocms_moderation/models.py index 25c2afd6..1e92dccc 100644 --- a/djangocms_moderation/models.py +++ b/djangocms_moderation/models.py @@ -416,7 +416,7 @@ class ModerationRequest(models.Model): to=Version, verbose_name=_("version"), on_delete=models.CASCADE ) language = models.CharField( - verbose_name=_("language"), max_length=5, choices=settings.LANGUAGES + verbose_name=_("language"), max_length=20, choices=settings.LANGUAGES ) is_active = models.BooleanField( verbose_name=_("is active"), default=True, db_index=True diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index c59a7250..f78fe3e8 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -14,7 +14,7 @@ pyflakes>=2.1.1 python-dateutil>=2.4 # Unreleased django-cms 4.0 compatible packages -https://github.com/django-cms/django-cms/tarball/develop-4#egg=django-cms +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/master#egg=djangocms-versioning https://github.com/FidelityInternational/djangocms-version-locking/tarball/master#egg=djangocms-version-locking From 79aaae4558faf9c80292b493b36e35ff3cd28577 Mon Sep 17 00:00:00 2001 From: Adam Murray Date: Thu, 8 Sep 2022 09:27:15 +0100 Subject: [PATCH 20/91] Release 2.1.6 (#230) --- CHANGELOG.rst | 4 ++++ djangocms_moderation/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 78626741..e7c2e8a8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,10 @@ Changelog Unreleased ========== +2.1.6 (2022-09-07) +================== +* fix: Language max_length too short for certain language codes + 2.1.5 (2022-07-11) ================== * fix: Broken markup in the ModerationCollection changelist view, and missing attributes in the burger menu diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 4cfbecb1..1e525f4c 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1,3 +1,3 @@ -__version__ = "2.1.5" +__version__ = "2.1.6" default_app_config = "djangocms_moderation.apps.ModerationConfig" From 66ea4c5df3287cf8c7e83c08e3fef4959c8d61af Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 23 Jan 2023 23:26:45 +0100 Subject: [PATCH 21/91] Create transifex.yaml --- .tx/transifex.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .tx/transifex.yaml diff --git a/.tx/transifex.yaml b/.tx/transifex.yaml new file mode 100644 index 00000000..2370ac4c --- /dev/null +++ b/.tx/transifex.yaml @@ -0,0 +1,7 @@ +git: + filters: + - filter_type: file + file_format: PO + source_file: djangocms_moderation/locale/en/LC_MESSAGES/django.po + source_language: en + translation_files_expression: 'djangocms_moderation/locale//LC_MESSAGES/django.po' From 0cfb9ed68c46a9097ce1275f7f2752bd436d9971 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 23 Jan 2023 23:32:03 +0100 Subject: [PATCH 22/91] Add localization --- .../locale/en/LC_MESSAGES/django.po | 914 ++++++++++++++++++ 1 file changed, 914 insertions(+) create mode 100644 djangocms_moderation/locale/en/LC_MESSAGES/django.po diff --git a/djangocms_moderation/locale/en/LC_MESSAGES/django.po b/djangocms_moderation/locale/en/LC_MESSAGES/django.po new file mode 100644 index 00000000..26ebb0bf --- /dev/null +++ b/djangocms_moderation/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,914 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-23 23:31+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: admin.py:67 models.py:650 +#: templates/djangocms_moderation/comment_list.html:10 +msgid "Action" +msgstr "" + +#: admin.py:68 models.py:651 +msgid "Actions" +msgstr "" + +#: admin.py:78 +#, python-brace-format +msgid "By {user}" +msgstr "" + +#: admin.py:80 +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:25 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:26 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:25 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:25 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:25 +#: templates/djangocms_moderation/items_to_collection.html:47 +#: templates/djangocms_moderation/moderation_request_change_list.html:23 +msgid "Status" +msgstr "" + +#: admin.py:99 +msgid "Form Submission" +msgstr "" + +#: admin.py:187 admin.py:1048 +msgid "actions" +msgstr "" + +#: admin.py:222 +msgid "ID" +msgstr "" + +#: admin.py:228 +msgid "Content type" +msgstr "" + +#: admin.py:232 +msgid "Title" +msgstr "" + +#: admin.py:236 +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:22 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:23 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:22 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:22 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:22 +#: templates/djangocms_moderation/comment_list.html:9 +#: templates/djangocms_moderation/items_to_collection.html:44 +msgid "Author" +msgstr "" + +#: admin.py:257 +msgid "Preview" +msgstr "" + +#: admin.py:267 +msgid "Reviewer" +msgstr "" + +#: admin.py:276 +msgid "Ready for publishing" +msgstr "" + +#: admin.py:278 +msgid "Pending author rework" +msgstr "" + +#: admin.py:282 +#, python-format +msgid "Pending %(role)s approval" +msgstr "" + +#: admin.py:291 +#, python-format +msgid "%(action)s by %(name)s" +msgstr "" + +#: admin.py:293 +msgid "Ready for submission" +msgstr "" + +#: admin.py:457 +#, python-format +msgid "%(count)d request successfully deleted" +msgid_plural "%(count)d requests successfully deleted" +msgstr[0] "" +msgstr[1] "" + +#: admin.py:599 +#, python-format +msgid "%(count)d request successfully resubmitted for review" +msgid_plural "%(count)d requests successfully resubmitted for review" +msgstr[0] "" +msgstr[1] "" + +#: admin.py:646 +#, python-format +msgid "%(count)d request successfully published" +msgid_plural "%(count)d requests successfully published" +msgstr[0] "" +msgstr[1] "" + +#: admin.py:705 +#, python-format +msgid "%(count)d request successfully submitted for rework" +msgid_plural "%(count)d requests successfully submitted for rework" +msgstr[0] "" +msgstr[1] "" + +#: admin.py:797 +#, python-format +msgid "%(count)d request successfully approved" +msgid_plural "%(count)d requests successfully approved" +msgstr[0] "" +msgstr[1] "" + +#: admin.py:863 +#: templates/admin/djangocms_moderation/collectioncomment/change_form.html:10 +msgid "Collection comments" +msgstr "" + +#: admin.py:913 +msgid "User" +msgstr "" + +#: admin.py:947 +msgid "Request comments" +msgstr "" + +#: admin.py:1039 +msgid "reviewers" +msgstr "" + +#: admin.py:1057 +msgid "Modify collection" +msgstr "" + +#: admin.py:1184 models.py:441 +msgid "Request" +msgstr "" + +#: admin.py:1189 +msgid "By User" +msgstr "" + +#: admin.py:1197 +msgid "Question" +msgstr "" + +#: admin.py:1197 +msgid "Answer" +msgstr "" + +#: admin.py:1202 +msgid "Form Data" +msgstr "" + +#: admin_actions.py:36 +msgid "Resubmit changes for review" +msgstr "" + +#: admin_actions.py:53 +msgid "Submit for rework" +msgstr "" + +#: admin_actions.py:66 +msgid "Approve" +msgstr "" + +#: admin_actions.py:82 +msgid "Remove selected" +msgstr "" + +#: admin_actions.py:99 +msgid "Publish selected requests" +msgstr "" + +#: admin_actions.py:156 +msgid "No suitable items found to add to moderation collection" +msgstr "" + +#: admin_actions.py:161 +msgid "Add to moderation collection" +msgstr "" + +#: admin_actions.py:165 +msgid "Add items to a collection to unpublish" +msgstr "" + +#: apps.py:7 +msgid "django CMS Moderation" +msgstr "" + +#: cms_toolbars.py:84 monkeypatch.py:56 +msgid "Submit for moderation" +msgstr "" + +#: cms_toolbars.py:109 +#: templates/djangocms_moderation/moderation_request_change_list.html:48 +msgid "Moderation collections" +msgstr "" + +#: conf.py:12 +msgid "Unique alpha-numeric string" +msgstr "" + +#: conf.py:13 +msgid "Sequential number" +msgstr "" + +#: conf.py:16 +msgid "Sequential number with identifier prefix" +msgstr "" + +#: conf.py:40 +msgid "Default" +msgstr "" + +#: constants.py:18 +msgid "Current page" +msgstr "" + +#: constants.py:19 +msgid "Page children (immediate)" +msgstr "" + +#: constants.py:20 +msgid "Page and children (immediate)" +msgstr "" + +#: constants.py:21 +msgid "Page descendants" +msgstr "" + +#: constants.py:22 +msgid "Page and descendants" +msgstr "" + +#: constants.py:33 +msgid "Resubmitted" +msgstr "" + +#: constants.py:34 +msgid "Started" +msgstr "" + +#: constants.py:35 +msgid "Rejected" +msgstr "" + +#: constants.py:36 +msgid "Approved" +msgstr "" + +#: constants.py:37 constants.py:54 +msgid "Cancelled" +msgstr "" + +#: constants.py:38 +msgid "Finished" +msgstr "" + +#: constants.py:51 +msgid "Collecting" +msgstr "" + +#: constants.py:52 +msgid "In Review" +msgstr "" + +#: constants.py:53 +msgid "Archived" +msgstr "" + +#: contrib/moderation_forms/cms_plugins.py:14 +#: contrib/moderation_forms/models.py:9 +msgid "Moderation Form" +msgstr "" + +#: contrib/moderation_forms/models.py:10 +msgid "Moderation Forms" +msgstr "" + +#: emails.py:16 +#: templates/djangocms_moderation/emails/moderation-request/approved.txt:8 +msgid "Approved moderation requests" +msgstr "" + +#: emails.py:17 +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:11 +msgid "Rejected moderation requests" +msgstr "" + +#: emails.py:18 +msgid "Request for moderation deleted" +msgstr "" + +#: emails.py:86 +msgid "Review requested" +msgstr "" + +#: filters.py:18 forms.py:64 models.py:213 +msgid "moderator" +msgstr "" + +#: filters.py:36 +msgid "reviewer" +msgstr "" + +#: filters.py:77 +msgid "All" +msgstr "" + +#: forms.py:67 +msgid "comment" +msgstr "" + +#: forms.py:100 forms.py:198 +#, python-brace-format +msgid "Any {role}" +msgstr "" + +#: forms.py:172 +msgid "" +"Your item is either locked, not enabled for moderation,or is part of another " +"active moderation request" +msgid_plural "" +"Your items are either locked, not enabled for moderation,or are part of " +"another active moderation request" +msgstr[0] "" +msgstr[1] "" + +#: forms.py:184 +msgid "Select review group" +msgstr "" + +#: forms.py:205 +msgid "This collection can't be submitted for a review" +msgstr "" + +#: forms.py:222 +msgid "This collection can't be cancelled" +msgstr "" + +#: forms.py:266 +msgid "You can only change your own comments" +msgstr "" + +#: helpers.py:112 +#, python-format +msgid "In collection \"%(collection_name)s (%(collection_id)s)\"" +msgstr "" + +#: helpers.py:117 +#, python-format +msgid "In moderation \"%(collection_name)s (%(collection_id)s)\"" +msgstr "" + +#: models.py:27 +msgid "Plain" +msgstr "" + +#: models.py:28 +msgid "Form" +msgstr "" + +#: models.py:31 models.py:72 models.py:115 +msgid "name" +msgstr "" + +#: models.py:34 +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:20 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:21 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:20 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:20 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:20 +#: templates/djangocms_moderation/items_to_collection.html:42 +msgid "Content Type" +msgstr "" + +#: models.py:40 +msgid "Template" +msgstr "" + +#: models.py:47 +msgid "Confirmation Page" +msgstr "" + +#: models.py:48 +msgid "Confirmation Pages" +msgstr "" + +#: models.py:77 +msgid "user" +msgstr "" + +#: models.py:80 +msgid "group" +msgstr "" + +#: models.py:84 models.py:740 +msgid "confirmation page" +msgstr "" + +#: models.py:92 +msgid "Role" +msgstr "" + +#: models.py:93 +msgid "Roles" +msgstr "" + +#: models.py:100 +msgid "Can't pick both user and group. Only one." +msgstr "" + +#: models.py:116 +msgid "is default" +msgstr "" + +#: models.py:118 +msgid "identifier" +msgstr "" + +#: models.py:123 +msgid "" +"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" +msgstr "" + +#: models.py:129 +msgid "requires compliance number?" +msgstr "" + +#: models.py:132 +msgid "" +"Does the Compliance number need to be generated before the moderation " +"request is approved? Please select the compliance number backend below" +msgstr "" + +#: models.py:138 +msgid "compliance number backend" +msgstr "" + +#: models.py:145 +#: templates/djangocms_moderation/moderation_request_change_list.html:24 +msgid "Workflow" +msgstr "" + +#: models.py:146 +msgid "Workflows" +msgstr "" + +#: models.py:161 +msgid "Can't have two default workflows, only one is allowed." +msgstr "" + +#: models.py:171 +msgid "role" +msgstr "" + +#: models.py:173 +msgid "is mandatory" +msgstr "" + +#: models.py:176 models.py:217 +msgid "workflow" +msgstr "" + +#: models.py:185 +msgid "Step" +msgstr "" + +#: models.py:186 +msgid "Steps" +msgstr "" + +#: models.py:210 +msgid "collection name" +msgstr "" + +#: models.py:232 +msgid "collection" +msgstr "" + +#: models.py:234 +msgid "Can change collection author" +msgstr "" + +#: models.py:235 +msgid "Can cancel collection" +msgstr "" + +#: models.py:318 +msgid "Cancelled collection" +msgstr "" + +#: models.py:398 models.py:635 +msgid "moderation_request" +msgstr "" + +#: models.py:416 +msgid "version" +msgstr "" + +#: models.py:419 +msgid "language" +msgstr "" + +#: models.py:422 +msgid "is active" +msgstr "" + +#: models.py:424 +msgid "date sent" +msgstr "" + +#: models.py:426 +msgid "compliance number" +msgstr "" + +#: models.py:435 models.py:695 +msgid "author" +msgstr "" + +#: models.py:442 +#: templates/admin/djangocms_moderation/moderationrequest/change_form.html:10 +#: templates/admin/djangocms_moderation/requestcomment/change_form.html:10 +#: templates/admin/djangocms_moderation/requestcomment/change_list.html:10 +#: templates/djangocms_moderation/moderation_request_change_list.html:49 +msgid "Requests" +msgstr "" + +#: models.py:598 +msgid "status" +msgstr "" + +#: models.py:603 models.py:732 +msgid "by user" +msgstr "" + +#: models.py:609 +msgid "to user" +msgstr "" + +#: models.py:617 +msgid "to role" +msgstr "" + +#: models.py:627 +msgid "step approved" +msgstr "" + +#: models.py:632 models.py:693 +msgid "message" +msgstr "" + +#: models.py:640 +msgid "date taken" +msgstr "" + +#: models.py:720 +msgid "moderation request" +msgstr "" + +#: models.py:726 +msgid "for step" +msgstr "" + +#: models.py:749 +msgid "Confirmation Form Submission" +msgstr "" + +#: models.py:750 +msgid "Confirmation Form Submissions" +msgstr "" + +#: monkeypatch.py:134 monkeypatch.py:144 +msgid "Cannot archive a version in an active moderation collection" +msgstr "" + +#: monkeypatch.py:139 +msgid "Cannot revert when draft version is in an active moderation collection" +msgstr "" + +#: monkeypatch.py:148 +msgid "Version is in an active moderation collection" +msgstr "" + +#: monkeypatch.py:152 +msgid "Cannot edit a version in an active moderation collection" +msgstr "" + +#: templates/admin/djangocms_moderation/collectioncomment/change_form.html:7 +#: templates/admin/djangocms_moderation/collectioncomment/change_list.html:16 +#: templates/admin/djangocms_moderation/moderationrequest/change_form.html:7 +#: templates/admin/djangocms_moderation/requestcomment/change_form.html:7 +#: templates/admin/djangocms_moderation/requestcomment/change_list.html:7 +#: templates/djangocms_moderation/moderation_request_change_list.html:46 +msgid "Home" +msgstr "" + +#: templates/admin/djangocms_moderation/collectioncomment/change_form.html:9 +#: templates/admin/djangocms_moderation/collectioncomment/change_list.html:18 +#: templates/admin/djangocms_moderation/moderationrequest/change_form.html:9 +#: templates/admin/djangocms_moderation/requestcomment/change_form.html:9 +#: templates/admin/djangocms_moderation/requestcomment/change_list.html:9 +msgid "Collections" +msgstr "" + +#: templates/admin/djangocms_moderation/collectioncomment/change_form.html:11 +#: templates/admin/djangocms_moderation/moderationrequest/change_form.html:11 +#: templates/admin/djangocms_moderation/requestcomment/change_form.html:12 +#, python-format +msgid "Add %(name)s" +msgstr "" + +#: templates/admin/djangocms_moderation/collectioncomment/change_list.html:7 +#, python-format +msgid "" +"\n" +" %(collection_name)s comments\n" +" " +msgstr "" + +#: templates/admin/djangocms_moderation/collectioncomment/change_list.html:19 +#: templates/admin/djangocms_moderation/requestcomment/change_form.html:11 +#: templates/admin/djangocms_moderation/requestcomment/change_list.html:11 +msgid "Comments" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:3 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:3 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:3 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:3 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:3 +msgid "Delete Confirmation" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:14 +msgid "Are you sure you want to approve these items for publishing?" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:19 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:20 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:19 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:19 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:19 +msgid "Id" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:21 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:22 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:21 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:21 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:21 +#: templates/djangocms_moderation/items_to_collection.html:43 +msgid "Identifier" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:23 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:24 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:23 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:23 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:23 +#: templates/djangocms_moderation/items_to_collection.html:45 +msgid "Edit Date" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:24 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:25 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:24 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:24 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:24 +#: templates/djangocms_moderation/items_to_collection.html:46 +msgid "Version #" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:47 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:48 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:47 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:47 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:47 +msgid "Yes, I\\" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/approve_confirmation.html:51 +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:52 +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:51 +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:51 +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:51 +msgid "No, take me back" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/delete_confirmation.html:15 +msgid "Are you sure you want to remove these items from this collection?" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/publish_confirmation.html:14 +msgid "Are you sure you want to publish these items?" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/resubmit_confirmation.html:14 +msgid " Are you sure you want to re-submit these items for review?" +msgstr "" + +#: templates/admin/djangocms_moderation/moderationrequest/rework_confirmation.html:14 +msgid "Are you sure you want to submit these items back for rework?" +msgstr "" + +#: templates/djangocms_moderation/cancel_collection.html:12 +msgid "Are you sure you'd like to cancel this collection?" +msgstr "" + +#: templates/djangocms_moderation/cancel_collection.html:16 +msgid "Cancel" +msgstr "" + +#: templates/djangocms_moderation/cancel_collection.html:18 +msgid "Confirm" +msgstr "" + +#: templates/djangocms_moderation/comment_icon.html:2 +msgid "View Comments" +msgstr "" + +#: templates/djangocms_moderation/comment_list.html:8 +#: templates/djangocms_moderation/emails/moderation-request/approved.txt:20 +#: templates/djangocms_moderation/emails/moderation-request/cancelled.txt:20 +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:23 +#: templates/djangocms_moderation/emails/moderation-request/request.txt:20 +msgid "Comment" +msgstr "" + +#: templates/djangocms_moderation/comment_list.html:11 +msgid "Date" +msgstr "" + +#: templates/djangocms_moderation/comment_list.html:21 +msgid "no comment provided" +msgstr "" + +#: templates/djangocms_moderation/confirmation_page.html:9 +#: templates/djangocms_moderation/select_workflow_form.html:27 +msgid "Next" +msgstr "" + +#: templates/djangocms_moderation/edit_icon.html:2 +msgid "Edit Collection Settings" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/approved.txt:2 +#, python-format +msgid "" +"\n" +"Hello %(collection_author)s, %(by_user)s has approved some moderation\n" +"requests in the collection %(collection_name)s.\n" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/approved.txt:25 +#: templates/djangocms_moderation/emails/moderation-request/cancelled.txt:25 +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:28 +#: templates/djangocms_moderation/emails/moderation-request/request.txt:25 +#: templates/djangocms_moderation/moderation_request_change_list.html:22 +msgid "Job ID" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/approved.txt:29 +#: templates/djangocms_moderation/emails/moderation-request/cancelled.txt:29 +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:32 +#: templates/djangocms_moderation/emails/moderation-request/request.txt:29 +msgid "Admin Url" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/cancelled.txt:2 +#, python-format +msgid "" +"\n" +"Hello %(collection_author)s, the following moderation requests in the " +"collection\n" +"%(collection_name)s have been cancelled\n" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/cancelled.txt:8 +msgid "Cancelled moderation requests" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:2 +#, python-format +msgid "" +"\n" +"Hello %(collection_author)s, %(by_user)s has rejected some moderation\n" +"requests in the collection %(collection_name)s.\n" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/rejected.txt:7 +msgid "" +"You can resubmit the necessary changes for another review, or remove the " +"moderation requests from the collection." +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/request.txt:2 +#, python-format +msgid "" +"\n" +"Hello, %(by_user)s has requested your approval for\n" +"changes in the collection %(collection_name)s.\n" +msgstr "" + +#: templates/djangocms_moderation/emails/moderation-request/request.txt:8 +msgid "Items included in this request" +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:11 +msgid "Add items to collection" +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:13 +#, python-format +msgid "" +"\n" +" Add '%(content_name)s' to collection\n" +" " +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:18 +msgid "No items to add" +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:36 +msgid "The selected collection currently contains the following items:" +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:66 +msgid "This collection contains no items yet" +msgstr "" + +#: templates/djangocms_moderation/items_to_collection.html:70 +#: templates/djangocms_moderation/request_form.html:37 +msgid "Submit" +msgstr "" + +#: templates/djangocms_moderation/moderation_request_change_list.html:25 +msgid "Owner" +msgstr "" + +#: templates/djangocms_moderation/moderation_request_change_list.html:34 +msgid "Cancel this collection" +msgstr "" + +#: templates/djangocms_moderation/moderation_request_change_list.html:39 +#: views.py:221 +msgid "Submit collection for review" +msgstr "" + +#: templates/djangocms_moderation/request_form.html:7 +#: templates/djangocms_moderation/select_workflow_form.html:7 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "" +msgstr[1] "" + +#: templates/djangocms_moderation/request_form.html:26 +msgid "Completed Forms" +msgstr "" + +#: templates/djangocms_moderation/request_icon.html:2 +msgid "View Requests" +msgstr "" + +#: views.py:74 +#, python-format +msgid "%(count)d item successfully added to moderation collection" +msgid_plural "%(count)d items successfully added to moderation collection" +msgstr[0] "" +msgstr[1] "" + +#: views.py:206 +msgid "Your collection has been submitted for review" +msgstr "" + +#: views.py:250 +msgid "Your collection has been cancelled" +msgstr "" + +#: views.py:259 +msgid "Cancel collection" +msgstr "" From 8219789713db7f03a2744d50590221e6dc50aa45 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 23 Jan 2023 23:50:52 +0100 Subject: [PATCH 23/91] Update requirements_base.txt --- tests/requirements/requirements_base.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index f78fe3e8..43c3c297 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -16,6 +16,6 @@ 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/master#egg=djangocms-versioning -https://github.com/FidelityInternational/djangocms-version-locking/tarball/master#egg=djangocms-version-locking +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/master#egg=djangocms-alias From 051e8da8569a58643c15db104bb2d8e118defbb1 Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Thu, 1 Jun 2023 12:23:04 +0100 Subject: [PATCH 24/91] Fix: Treebeard support improved (#241) * fix: Simplify admin template to extend treebeard and thus allow treebeard to do what it wants. * chore: Update CHANGELOG.rst --- CHANGELOG.rst | 2 ++ README.rst | 4 +++- .../treebeard/tree_change_list.html | 13 ++----------- setup.py | 2 +- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e7c2e8a8..78b3f434 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,8 @@ Changelog Unreleased ========== +* fix: Treebeard support improved by inheriting a treebeard template + 2.1.6 (2022-09-07) ================== * fix: Language max_length too short for certain language codes diff --git a/README.rst b/README.rst index 9e13b946..e2d425b2 100644 --- a/README.rst +++ b/README.rst @@ -11,13 +11,15 @@ Requirements django CMS Moderation requires that you have a django CMS 4.0 (or higher) project already running and set up. +djangocms-versioning is also required along with django-fsm which should be installed with versioning. + To install ========== Run:: - pip install git+git://github.com/divio/djangocms-moderation@develop#egg=djangocms-moderation + pip install git+git://github.com/django-cms/djangocms-moderation@master#egg=djangocms-moderation Add the following to your project's ``INSTALLED_APPS``: diff --git a/djangocms_moderation/templates/admin/djangocms_moderation/treebeard/tree_change_list.html b/djangocms_moderation/templates/admin/djangocms_moderation/treebeard/tree_change_list.html index 401ee10b..d78b39bf 100644 --- a/djangocms_moderation/templates/admin/djangocms_moderation/treebeard/tree_change_list.html +++ b/djangocms_moderation/templates/admin/djangocms_moderation/treebeard/tree_change_list.html @@ -1,15 +1,6 @@ {# Used for MP and NS trees #} -{% extends "admin/change_list.html" %} -{% load admin_list admin_tree i18n %} - -{% block extrastyle %} - {{ block.super }} - {% treebeard_css %} -{% endblock %} - -{% block extrahead %} - {{ block.super }} -{% endblock %} +{% extends "admin/tree_change_list.html" %} +{% load admin_list admin_tree %} {% block result_list %} {% if action_form and actions_on_top and cl.full_result_count %} diff --git a/setup.py b/setup.py index 3a490fe6..7b32e437 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ author_email="info@divio.ch", maintainer='Django CMS Association and contributors', maintainer_email='info@django-cms.org', - url="http://github.com/django-cms/djangocms-moderation", + url="https://github.com/django-cms/djangocms-moderation", license="BSD", test_suite="tests.settings.run", ) From 409c4bc9ff18ca4337f2380bd7a66d3046fb8668 Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Thu, 1 Jun 2023 16:12:52 +0100 Subject: [PATCH 25/91] Fix: Problems from alias failing tests (#243) * chore: Add dependency links that install from source * chore: Move alias to install version with django-cms 4.0 support * chore: Drop python 3.7 & fix tox --- setup.py | 5 +++++ tests/requirements/requirements_base.txt | 2 +- tox.ini | 17 +++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 7b32e437..ddb3bae5 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,10 @@ "django-sekizai>=0.7", "django-admin-sortable2>=0.6.4", ] +DEPENDENCY_LINKS = [ + "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", +] setup( name="djangocms-moderation", @@ -25,6 +29,7 @@ "Topic :: Software Development", ], install_requires=INSTALL_REQUIREMENTS, + dependency_links=DEPENDENCY_LINKS, author="Divio AG", author_email="info@divio.ch", maintainer='Django CMS Association and contributors', diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 43c3c297..0c3d55fa 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -18,4 +18,4 @@ 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/master#egg=djangocms-alias +https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias diff --git a/tox.ini b/tox.ini index 24c8a857..3012f5a8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,20 @@ [tox] envlist = - dj32-flake8 - dj32-isort - py{37,38,39}-dj{22,32}-sqlite-cms4 + flake8 + isort + py{38,39}-dj{22,32}-sqlite-cms4 skip_missing_interpreters=True [testenv] deps = - dj22: -r{toxinidir}/tests/requirements/django_22.txt + dj22: -r{toxinidir}/tests/requirements/dj22_cms40.txt dj22: Django>=2.2,<2.3 - dj32: -r{toxinidir}/tests/requirements/django_32.txt + dj32: -r{toxinidir}/tests/requirements/dj32_cms40.txt dj32: Django>=3.2,<3.3 basepython = - py37: python3.7 py38: python3.8 py39: python3.9 @@ -26,9 +25,11 @@ commands = {env:COMMAND:coverage} report [testenv:flake8] -commands = flake8 basepython = python3.9 +commands = flake8 +deps = flake8 [testenv:isort] -commands = isort --recursive --check-only --diff {toxinidir} basepython = python3.9 +commands = isort --check-only --diff {toxinidir} +deps = isort From 0080d64e77178b665fff0235817e6503b6b70161 Mon Sep 17 00:00:00 2001 From: Mark Walker Date: Wed, 24 Jan 2024 22:19:39 +0000 Subject: [PATCH 26/91] chore: Switch docs to furo & add pre-commit (#244) --- .github/dependabot.yml | 11 +++ .github/workflows/docs.yml | 35 ++++++++ .pre-commit-config.yaml | 49 +++++++++++ .readthedocs.yaml | 20 +++++ docs/.gitignore | 2 + docs/conf.py | 13 ++- docs/requirements.in | 9 ++ docs/requirements.txt | 136 ++++++++++++++++++++++++++++++ setup.cfg | 1 + setup.py | 2 +- tests/requirements/dj32_cms40.txt | 2 +- 11 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 docs/.gitignore create mode 100644 docs/requirements.in create mode 100644 docs/requirements.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..14f01789 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..b005cb83 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,35 @@ +name: Docs + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + docs: + runs-on: ubuntu-latest + name: docs + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: 'pip' + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - run: python -m pip install -r docs/requirements.txt + - run: python setup.py install + - run: codespell -w *.rst + - run: codespell -w --skip docs/spelling_wordlist docs + - name: Build docs + run: | + cd docs + sphinx-build -b dirhtml -n -d build/doctrees . build/dirhtml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..bcd3de1a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,49 @@ +ci: + autofix_commit_msg: | + ci: auto fixes from pre-commit hooks + + for more information, see https://pre-commit.ci + autofix_prs: false + autoupdate_commit_msg: 'ci: pre-commit autoupdate' + autoupdate_schedule: monthly + +repos: + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: ["--py37-plus"] + + - repo: https://github.com/adamchainz/django-upgrade + rev: '1.4.0' + hooks: + - id: django-upgrade + args: [--target-version, "2.2"] + + - repo: https://github.com/PyCQA/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + + - repo: https://github.com/asottile/yesqa + rev: v1.3.0 + hooks: + - id: yesqa + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: check-merge-conflict + - id: debug-statements + - id: mixed-line-ending + - id: trailing-whitespace + + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + + - repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + - id: codespell diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..e1bd672f --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + configuration: docs/conf.py + fail_on_warning: false + +formats: + - epub + - pdf + +python: + install: + - requirements: docs/requirements.txt diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..85c55ebc --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +.env +.venv diff --git a/docs/conf.py b/docs/conf.py index 834c2434..1273cc40 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,7 +62,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -78,7 +78,16 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "alabaster" +try: + import furo + + html_theme = 'furo' + html_theme_options = { + "navigation_with_keys": True, + } +except ImportError: + html_theme = 'default' + # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/requirements.in b/docs/requirements.in new file mode 100644 index 00000000..c9231608 --- /dev/null +++ b/docs/requirements.in @@ -0,0 +1,9 @@ +furo +Sphinx>=4.2.0 +sphinx-copybutton +sphinxext-opengraph +sphinxcontrib-spelling +sphinx-autobuild +codespell +pip-tools +docstrfmt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..537a0614 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,136 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile +# +alabaster==0.7.16 + # via sphinx +babel==2.14.0 + # via sphinx +beautifulsoup4==4.12.3 + # via furo +black==23.12.1 + # via docstrfmt +build==1.0.3 + # via pip-tools +certifi==2023.11.17 + # via requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via + # black + # docstrfmt + # pip-tools +codespell==2.2.6 + # via -r requirements.in +colorama==0.4.6 + # via sphinx-autobuild +docstrfmt==1.6.1 + # via -r requirements.in +docutils==0.20.1 + # via + # docstrfmt + # sphinx +furo==2023.9.10 + # via -r requirements.in +idna==3.6 + # via requests +imagesize==1.4.1 + # via sphinx +jinja2==3.1.3 + # via sphinx +libcst==1.1.0 + # via docstrfmt +livereload==2.6.3 + # via sphinx-autobuild +markupsafe==2.1.4 + # via jinja2 +mypy-extensions==1.0.0 + # via + # black + # typing-inspect +packaging==23.2 + # via + # black + # build + # sphinx +pathspec==0.12.1 + # via black +pip-tools==7.3.0 + # via -r requirements.in +platformdirs==4.1.0 + # via + # black + # docstrfmt +pyenchant==3.2.2 + # via sphinxcontrib-spelling +pygments==2.17.2 + # via + # furo + # sphinx +pyproject-hooks==1.0.0 + # via build +pyyaml==6.0.1 + # via libcst +requests==2.31.0 + # via sphinx +six==1.16.0 + # via livereload +snowballstemmer==2.2.0 + # via sphinx +soupsieve==2.5 + # via beautifulsoup4 +sphinx==7.2.6 + # via + # -r requirements.in + # docstrfmt + # furo + # sphinx-autobuild + # sphinx-basic-ng + # sphinx-copybutton + # sphinxcontrib-spelling + # sphinxext-opengraph +sphinx-autobuild==2021.3.14 + # via -r requirements.in +sphinx-basic-ng==1.0.0b2 + # via furo +sphinx-copybutton==0.5.2 + # via -r requirements.in +sphinxcontrib-applehelp==1.0.8 + # via sphinx +sphinxcontrib-devhelp==1.0.6 + # via sphinx +sphinxcontrib-htmlhelp==2.0.5 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.7 + # via sphinx +sphinxcontrib-serializinghtml==1.1.10 + # via sphinx +sphinxcontrib-spelling==8.0.0 + # via -r requirements.in +sphinxext-opengraph==0.9.1 + # via -r requirements.in +tabulate==0.9.0 + # via docstrfmt +toml==0.10.2 + # via docstrfmt +tornado==6.4 + # via livereload +typing-extensions==4.9.0 + # via + # libcst + # typing-inspect +typing-inspect==0.9.0 + # via libcst +urllib3==2.1.0 + # via requests +wheel==0.42.0 + # via pip-tools + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools diff --git a/setup.cfg b/setup.cfg index 5a79ae38..56767f48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,7 @@ exclude = **/migrations/, build/, .tox/, + docs/conf.py, node_modules/, [isort] diff --git a/setup.py b/setup.py index ddb3bae5..86e47922 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ INSTALL_REQUIREMENTS = [ - "Django>=2.2,<4.0", + "Django>=2.2", "django-cms", "django-sekizai>=0.7", "django-admin-sortable2>=0.6.4", diff --git a/tests/requirements/dj32_cms40.txt b/tests/requirements/dj32_cms40.txt index d19b191e..6dd211b1 100644 --- a/tests/requirements/dj32_cms40.txt +++ b/tests/requirements/dj32_cms40.txt @@ -2,5 +2,5 @@ Django>=3.2,<4.0 -django-admin-sortable2 +django-admin-sortable2<2 django_polymorphic From bc368f0a900fc94281686746c9a959c5bbe4beff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:22:11 +0000 Subject: [PATCH 27/91] Bump codecov/codecov-action from 1 to 3 (#249) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 704336cc..fd56b6a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,4 +36,4 @@ jobs: run: coverage run setup.py test - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 From 9d6219e759d9f6d8fd00e08b657d231bef59141f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:22:45 +0000 Subject: [PATCH 28/91] Bump actions/setup-node from 1 to 4 (#248) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 1 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v1...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/frontend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 4d88962e..3cbc5c0e 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install From c19dbbbfcf9b914a777e2258657bcade8c730c91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:23:02 +0000 Subject: [PATCH 29/91] Bump actions/setup-python from 2 to 5 (#247) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6760a90a..c28df9f8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install flake8 @@ -27,7 +27,7 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.9 - run: python -m pip install isort diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd56b6a5..b1ed70bb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 539c0dfa83bc560f279f6c34cbed9e97cbc81b3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:24:14 +0000 Subject: [PATCH 30/91] Bump actions/checkout from 1 to 4 (#246) Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v1...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/frontend.yml | 2 +- .github/workflows/lint.yml | 4 ++-- .github/workflows/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 3cbc5c0e..cfdcaed4 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -10,7 +10,7 @@ jobs: node-version: [6.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c28df9f8..fe6c95e4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b1ed70bb..5bdd54e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 From c96f9a48077b47ed796e7eef050653fce448e89f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:48:24 +0000 Subject: [PATCH 31/91] Bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5bdd54e5..fa839f07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,4 +36,4 @@ jobs: run: coverage run setup.py test - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 From 6683dc8565cfac8794c500d50c3ceb942ecced2a Mon Sep 17 00:00:00 2001 From: raffaella Date: Mon, 11 Mar 2024 18:11:33 +0100 Subject: [PATCH 32/91] feat: added pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4f76fcfa --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +## Description + + + +## Related resources + + + +* #... +* #... + +## Checklist + + + +* [ ] I have added or modified the tests when changing logic +* [ ] I have followed [the conventional commits guidelines](https://www.conventionalcommits.org/) to add meaningful information into the changelog +* [ ] I have read the [contribution guidelines ](https://github.com/django-cms/django-cms/blob/develop/CONTRIBUTING.rst) From fd77632e3c256d4aeba99903c57380d8a246f7cf Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 10:47:06 +0100 Subject: [PATCH 33/91] 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 34/91] 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 35/91] 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 36/91] 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 37/91] 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 38/91] 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 39/91] 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 238be3df46c368c245b181f902caf36186ae18be 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 40/91] 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 ba0bed9020e6b04ad25c5792f77b9053c0bb5d5b Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 15:14:35 +0100 Subject: [PATCH 41/91] 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 56811e0e926a9656bc5e427f18d90575a863efba Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 12 Mar 2024 16:58:09 +0100 Subject: [PATCH 42/91] 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 43/91] 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 44/91] 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 45/91] 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 46/91] 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 47/91] 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 48/91] 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 49/91] 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 50/91] 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 51/91] 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 52/91] 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 53/91] 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 54/91] 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 55/91] 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 56/91] 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 57/91] 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 58/91] 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 59/91] 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 60/91] 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()) From 05d91643d2313ef53d79d37692cb58acf7631fa3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 19:28:16 +0000 Subject: [PATCH 61/91] ci: pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.31.0 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v2.31.0...v3.15.2) - [github.com/adamchainz/django-upgrade: 1.4.0 → 1.16.0](https://github.com/adamchainz/django-upgrade/compare/1.4.0...1.16.0) - [github.com/codespell-project/codespell: v2.1.0 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.1.0...v2.2.6) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c0963489..5b6424bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,13 +9,13 @@ ci: repos: - repo: https://github.com/asottile/pyupgrade - rev: v2.31.0 + rev: v3.15.2 hooks: - id: pyupgrade args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade - rev: '1.4.0' + rev: '1.16.0' hooks: - id: django-upgrade args: [--target-version, "4.0"] @@ -44,7 +44,7 @@ repos: - id: isort - repo: https://github.com/codespell-project/codespell - rev: v2.1.0 + rev: v2.2.6 hooks: - id: codespell exclude: > From 456ba0f4660cbed26dd6cdafa88e1b1c69b23e71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 04:35:20 +0000 Subject: [PATCH 62/91] chore(deps): bump idna from 3.6 to 3.7 in /docs Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7) --- updated-dependencies: - dependency-name: idna dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 537a0614..059e7e23 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -35,7 +35,7 @@ docutils==0.20.1 # sphinx furo==2023.9.10 # via -r requirements.in -idna==3.6 +idna==3.7 # via requests imagesize==1.4.1 # via sphinx From 7709938681696f557ae83b66e67591e0ed353c65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 21:02:13 +0000 Subject: [PATCH 63/91] chore(deps): bump jinja2 from 3.1.3 to 3.1.4 in /docs Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4) --- updated-dependencies: - dependency-name: jinja2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 059e7e23..f0933e55 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -39,7 +39,7 @@ idna==3.7 # via requests imagesize==1.4.1 # via sphinx -jinja2==3.1.3 +jinja2==3.1.4 # via sphinx libcst==1.1.0 # via docstrfmt From 1244819fb306a8f215c53b0b3d6f004eb39641a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=82=85=E7=91=9E=E6=9B=BC?= Date: Thu, 16 May 2024 21:44:13 +0800 Subject: [PATCH 64/91] Release/2.2.0 (#265) * bump version to 2.2.0 * bump version to 2.2.0 changelog update * fix testcases issue for djangocms-versioning 2.0.0 * remove testcase logic for versioning 2.0.0, as its not suggest to use * update release date. * fix pre-commit errors * eclude json files for pre-commit code spell check * fix pre-commit errors, add json files --- .pre-commit-config.yaml | 2 +- CHANGELOG.rst | 7 +++-- djangocms_moderation/__init__.py | 2 +- djangocms_moderation/compact.py | 9 ++++++ tests/requirements/dj42_cms40.txt | 2 +- tests/requirements/dj42_cms41.txt | 2 +- tests/requirements/dj50_cms41.txt | 2 +- tests/test_forms.py | 18 +++++------ tests/test_models.py | 30 +++++++++---------- tests/test_monkeypatch.py | 16 +++------- tests/test_views.py | 8 ++--- tests/utils/app_1/__init__.py | 1 - tests/utils/app_2/__init__.py | 1 - tests/utils/base.py | 11 +++++++ tests/utils/moderated_polls/__init__.py | 1 - .../versioned_none_moderated_app/__init__.py | 3 -- 16 files changed, 62 insertions(+), 53 deletions(-) create mode 100644 djangocms_moderation/compact.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b6424bc..8245a032 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,5 +49,5 @@ repos: - id: codespell exclude: > (?x)^( - .*\.(js|po) + .*\.(js|po|json) )$ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 78b3f434..01692029 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,9 +2,12 @@ Changelog ========= -Unreleased +2.2.0 (2024-05-16) ========== - +* Python 3.8, 3.9 support removed +* Python 3.10, 3.11 and 3.12 support added +* Django 2.2 support removed +* Django 4.2 support added * fix: Treebeard support improved by inheriting a treebeard template 2.1.6 (2022-09-07) diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index edc60b35..8a124bf6 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1 +1 @@ -__version__ = "2.1.6" +__version__ = "2.2.0" diff --git a/djangocms_moderation/compact.py b/djangocms_moderation/compact.py new file mode 100644 index 00000000..a010273a --- /dev/null +++ b/djangocms_moderation/compact.py @@ -0,0 +1,9 @@ +from django import get_version + +from packaging.version import Version + + +DJANGO_VERSION = get_version() + + +DJANGO_4_1 = Version(DJANGO_VERSION) < Version('4.2') diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt index deb73e0f..1f5e7b9c 100644 --- a/tests/requirements/dj42_cms40.txt +++ b/tests/requirements/dj42_cms40.txt @@ -7,5 +7,5 @@ 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/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/FidelityInternational/djangocms-version-locking/tarball/master#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 index d2ef85cc..824d76f5 100644 --- a/tests/requirements/dj42_cms41.txt +++ b/tests/requirements/dj42_cms41.txt @@ -3,5 +3,5 @@ Django>=4.2,<5.0 django-cms>=4.1,<4.2 -djangocms-versioning>=2.0.0 +djangocms-versioning>=2.0.1 djangocms-alias>=2.0.0 diff --git a/tests/requirements/dj50_cms41.txt b/tests/requirements/dj50_cms41.txt index 53ea1d28..8461ac03 100644 --- a/tests/requirements/dj50_cms41.txt +++ b/tests/requirements/dj50_cms41.txt @@ -3,5 +3,5 @@ Django>=5.0,<5.1 django-cms>=4.1,<4.2 -djangocms-versioning>=2.0.0 +djangocms-versioning>=2.0.1 djangocms-alias>=2.0.0 diff --git a/tests/test_forms.py b/tests/test_forms.py index 4439508a..256a8a83 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -16,10 +16,10 @@ ) from djangocms_moderation.models import ModerationCollection, ModerationRequest -from .utils.base import BaseTestCase +from .utils.base import AssertQueryMixin, BaseTestCase -class UpdateModerationRequestFormTest(BaseTestCase): +class UpdateModerationRequestFormTest(AssertQueryMixin, BaseTestCase): def test_form_init_approved_action(self): form = UpdateModerationRequestForm( action=constants.ACTION_APPROVED, @@ -31,7 +31,7 @@ def test_form_init_approved_action(self): ) field_moderator = form.fields["moderator"] self.assertEqual(field_moderator.empty_label, "Any Role 2") - self.assertQuerysetEqual( + self.assertQuerySetEqual( field_moderator.queryset, User.objects.filter(pk__in=[self.user2.pk]), transform=lambda x: x, @@ -48,7 +48,7 @@ def test_form_init_cancelled_action(self): active_request=self.moderation_request1, ) field_moderator = form.fields["moderator"] - self.assertQuerysetEqual(field_moderator.queryset, User.objects.none()) + self.assertQuerySetEqual(field_moderator.queryset, User.objects.none()) self.assertIsInstance(field_moderator.widget, HiddenInput) def test_form_init_rejected_action(self): @@ -61,7 +61,7 @@ def test_form_init_rejected_action(self): active_request=self.moderation_request1, ) field_moderator = form.fields["moderator"] - self.assertQuerysetEqual(field_moderator.queryset, User.objects.none()) + self.assertQuerySetEqual(field_moderator.queryset, User.objects.none()) self.assertIsInstance(field_moderator.widget, HiddenInput) def test_form_save(self): @@ -143,7 +143,7 @@ def test_action_user_can_change_own_comment(self): self.assertTrue(form.is_valid()) -class CollectionItemsFormTestCase(BaseTestCase): +class CollectionItemsFormTestCase(AssertQueryMixin, BaseTestCase): def test_add_items_to_collection(self): pg1_version = PageVersionFactory(created_by=self.user) pg2_version = PageVersionFactory(created_by=self.user) @@ -155,7 +155,7 @@ def test_add_items_to_collection(self): form = CollectionItemsForm(data=data, user=self.user) self.assertTrue(form.is_valid()) versions = form.clean_versions() - self.assertQuerysetEqual( + self.assertQuerySetEqual( versions, Version.objects.filter(pk__in=[pg1_version.pk, pg2_version.pk]), transform=lambda x: x, @@ -172,7 +172,7 @@ def test_attempt_add_with_item_already_in_collection(self): form = CollectionItemsForm(data=data, user=self.user) self.assertTrue(form.is_valid()) versions = form.clean_versions() - self.assertQuerysetEqual( + self.assertQuerySetEqual( versions, Version.objects.filter(pk__in=[pg_version.pk]), transform=lambda x: x, @@ -253,7 +253,7 @@ def test_collection_choice_should_be_limited_to_current_user_and_collecting_stat if not form.is_valid(): self.assertIn("collection", form.errors) - self.assertQuerysetEqual( + self.assertQuerySetEqual( form.fields["collection"].queryset, ModerationCollection.objects.filter( pk__in=[collection1.pk, collection2.pk] diff --git a/tests/test_models.py b/tests/test_models.py index 53d48d5d..979a011a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -20,10 +20,10 @@ ) from .utils import factories -from .utils.base import BaseTestCase +from .utils.base import AssertQueryMixin, BaseTestCase -class RoleTest(BaseTestCase): +class RoleTest(AssertQueryMixin, BaseTestCase): def test_user_and_group_validation_error(self): role = Role.objects.create(name="New Role 1", user=self.user, group=self.group) self.assertRaisesMessage( @@ -43,7 +43,7 @@ def test_user_is_assigned(self): def test_get_users_queryset(self): # with user role = Role.objects.create(name="New Role 1", user=self.user) - self.assertQuerysetEqual( + self.assertQuerySetEqual( role.get_users_queryset(), User.objects.filter(pk=self.user.pk), transform=lambda x: x, @@ -51,7 +51,7 @@ def test_get_users_queryset(self): ) # with group role = Role.objects.create(name="New Role 2", group=self.group) - self.assertQuerysetEqual( + self.assertQuerySetEqual( role.get_users_queryset(), User.objects.filter(pk__in=[self.user2.pk, self.user3.pk]), transform=lambda x: x, @@ -88,7 +88,7 @@ def test_get_next_required(self): self.assertIsNone(self.wf1st3.get_next_required()) -class ModerationRequestTest(BaseTestCase): +class ModerationRequestTest(AssertQueryMixin, BaseTestCase): def test_has_pending_step(self): self.assertTrue(self.moderation_request1.has_pending_step()) self.assertFalse(self.moderation_request2.has_pending_step()) @@ -128,7 +128,7 @@ def test_get_last_action(self): ) def test_get_pending_steps(self): - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request3.get_pending_steps(), WorkflowStep.objects.filter(pk__in=[self.wf3st2.pk]), transform=lambda x: x, @@ -142,7 +142,7 @@ def test_get_pending_steps(self): # ... so all the steps are now pending as we need to re-moderate the # resubmitted request - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request3.get_pending_steps(), WorkflowStep.objects.filter(workflow=self.wf3), transform=lambda x: x, @@ -150,13 +150,13 @@ def test_get_pending_steps(self): ) def test_get_pending_required_steps(self): - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request1.get_pending_required_steps(), WorkflowStep.objects.filter(pk__in=[self.wf1st1.pk, self.wf1st3.pk]), transform=lambda x: x, ordered=False, ) - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request3.get_pending_required_steps(), WorkflowStep.objects.none(), transform=lambda x: x, @@ -164,7 +164,7 @@ def test_get_pending_required_steps(self): ) # Lets test with archived action - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request2.get_pending_required_steps(), WorkflowStep.objects.none(), transform=lambda x: x, @@ -176,7 +176,7 @@ def test_get_pending_required_steps(self): last_action.is_archived = True last_action.save() - self.assertQuerysetEqual( + self.assertQuerySetEqual( self.moderation_request2.get_pending_required_steps(), WorkflowStep.objects.filter(pk=last_action.step_approved.pk), transform=lambda x: x, @@ -663,7 +663,7 @@ def test_cancel(self): self.assertEqual(actions[0].moderation_request, active_request) -class AddVersionTestCase(TestCase): +class AddVersionTestCase(AssertQueryMixin, TestCase): def setUp(self): self.collection = factories.ModerationCollectionFactory() @@ -717,13 +717,13 @@ def test_add_version_duplicate_with_same_parent(self): moderation_request, added_items = self.collection.add_version(version, parent) self.assertEqual(ModerationRequest.objects.all().count(), 2) - self.assertQuerysetEqual( + self.assertQuerySetEqual( ModerationRequest.objects.all(), [parent.moderation_request.pk, child.moderation_request.pk], transform=lambda o: o.pk ) self.assertEqual(ModerationRequestTreeNode.objects.all().count(), 2) - self.assertQuerysetEqual( + self.assertQuerySetEqual( ModerationRequestTreeNode.objects.all(), [parent.pk, child.pk], transform=lambda o: o.pk @@ -746,7 +746,7 @@ def test_add_version_duplicate_with_different_parent(self): moderation_request, added_items = self.collection.add_version(version, parent) self.assertEqual(ModerationRequest.objects.all().count(), 3) - self.assertQuerysetEqual( + self.assertQuerySetEqual( ModerationRequest.objects.all(), [root.moderation_request.pk, child.moderation_request.pk, parent.moderation_request.pk], transform=lambda o: o.pk diff --git a/tests/test_monkeypatch.py b/tests/test_monkeypatch.py index 3817b0b6..2c80deff 100644 --- a/tests/test_monkeypatch.py +++ b/tests/test_monkeypatch.py @@ -42,13 +42,10 @@ 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) - 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 + if versioning_version >= "2.0.2": self.assertEqual("", edit_link) - # self.assertIn("inactive", edit_link) + else: + self.assertIn("inactive", edit_link) @mock.patch("djangocms_moderation.monkeypatch.is_registered_for_moderation") @mock.patch("djangocms_moderation.monkeypatch.is_obj_review_locked") @@ -92,12 +89,7 @@ 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) - 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) + self.assertIn("inactive", archive_link) _mock.return_value = None archive_link = self.version_admin._get_archive_link(version, self.mock_request) diff --git a/tests/test_views.py b/tests/test_views.py index b6e86c6c..3c4ed742 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -17,7 +17,7 @@ ) from djangocms_moderation.utils import get_admin_url -from .utils.base import BaseViewTestCase +from .utils.base import AssertQueryMixin, BaseViewTestCase from .utils.factories import ( ChildModerationRequestTreeNodeFactory, ModerationCollectionFactory, @@ -706,7 +706,7 @@ def test_add_version_with_unlocked_child(self): self.assertTrue(moderation_requests.filter(version=self.poll_version).exists()) -class CollectionItemsViewTest(CMSTestCase): +class CollectionItemsViewTest(AssertQueryMixin, CMSTestCase): def setUp(self): self.client.force_login(self.get_superuser()) self.url = get_admin_url( @@ -746,7 +746,7 @@ def test_initial_form_values_when_collection_id_passed(self): self.assertEqual( response.context["form"].initial["collection"], str(collection.pk) ) - self.assertQuerysetEqual( + self.assertQuerySetEqual( response.context["form"].initial["versions"], [pg_version.pk, poll_version.pk], transform=lambda o: o.pk, @@ -762,7 +762,7 @@ def test_initial_form_values_when_collection_id_not_passed(self): self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context["form"].initial.keys()), 1) - self.assertQuerysetEqual( + self.assertQuerySetEqual( response.context["form"].initial["versions"], [pg_version.pk, poll_version.pk], transform=lambda o: o.pk, diff --git a/tests/utils/app_1/__init__.py b/tests/utils/app_1/__init__.py index 03f81427..e69de29b 100644 --- a/tests/utils/app_1/__init__.py +++ b/tests/utils/app_1/__init__.py @@ -1 +0,0 @@ -default_app_config = "tests.utils.app_1.apps.App1Config" diff --git a/tests/utils/app_2/__init__.py b/tests/utils/app_2/__init__.py index ff4e78c0..e69de29b 100644 --- a/tests/utils/app_2/__init__.py +++ b/tests/utils/app_2/__init__.py @@ -1 +0,0 @@ -default_app_config = "tests.utils.app_2.apps.App2Config" diff --git a/tests/utils/base.py b/tests/utils/base.py index 073308fb..ee9997a0 100644 --- a/tests/utils/base.py +++ b/tests/utils/base.py @@ -6,6 +6,7 @@ from djangocms_versioning.test_utils.factories import PageVersionFactory from djangocms_moderation import constants +from djangocms_moderation.compact import DJANGO_4_1 from djangocms_moderation.models import ( ModerationCollection, ModerationRequest, @@ -18,6 +19,16 @@ class MockRequest: GET = {} +class AssertQueryMixin: + """Mixin to append uppercase `assertQuerySetEqual` for TestCase class + if django version below 4.2 + """ + + if DJANGO_4_1: + def assertQuerySetEqual(self, *args, **kwargs): + return self.assertQuerysetEqual(*args, **kwargs) + + class BaseTestCase(CMSTestCase): @classmethod def setUpTestData(cls): diff --git a/tests/utils/moderated_polls/__init__.py b/tests/utils/moderated_polls/__init__.py index d70b54ee..e69de29b 100644 --- a/tests/utils/moderated_polls/__init__.py +++ b/tests/utils/moderated_polls/__init__.py @@ -1 +0,0 @@ -default_app_config = "tests.utils.moderated_polls.apps.PollsConfig" diff --git a/tests/utils/versioned_none_moderated_app/__init__.py b/tests/utils/versioned_none_moderated_app/__init__.py index dc0b47c0..e69de29b 100644 --- a/tests/utils/versioned_none_moderated_app/__init__.py +++ b/tests/utils/versioned_none_moderated_app/__init__.py @@ -1,3 +0,0 @@ -default_app_config = ( - "tests.utils.versioned_none_moderated_app.apps.VersionedNoneModeratedAppConfig" -) From 3eb737dc8a79ac3dac510b6da80ea409a9d76d6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:15:43 +0200 Subject: [PATCH 65/91] --- (#270) updated-dependencies: - dependency-name: requests dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index f0933e55..471fc60a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -74,7 +74,7 @@ pyproject-hooks==1.0.0 # via build pyyaml==6.0.1 # via libcst -requests==2.31.0 +requests==2.32.0 # via sphinx six==1.16.0 # via livereload From 288cab911d66a9b45730f2464cba53da1fbfba32 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:19:20 +0200 Subject: [PATCH 66/91] ci: pre-commit autoupdate (#266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: pre-commit autoupdate updates: - [github.com/adamchainz/django-upgrade: 1.16.0 → 1.18.0](https://github.com/adamchainz/django-upgrade/compare/1.16.0...1.18.0) - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) - [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0) * Update setup.cfg to fix code spell error * Update setup.cfg --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- .pre-commit-config.yaml | 6 +++--- setup.cfg | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8245a032..a094a773 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade - rev: '1.16.0' + rev: '1.18.0' hooks: - id: django-upgrade args: [--target-version, "4.0"] @@ -31,7 +31,7 @@ repos: - id: yesqa - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-merge-conflict - id: debug-statements @@ -44,7 +44,7 @@ repos: - id: isort - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell exclude: > diff --git a/setup.cfg b/setup.cfg index 56cdaaf2..f5268d24 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,4 +48,4 @@ exclude_lines = if __name__ == .__main__.: [codespell] -ignore-words-list = alpha-numeric +ignore-words-list = alpha-numeric,assertIn,THIRDPARTY From ff3876d37e5ad40d18beb94040a0acd534cedee1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 9 Jun 2024 05:51:21 +0200 Subject: [PATCH 67/91] chore(deps): bump tornado from 6.4 to 6.4.1 in /docs (#271) Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1) --- updated-dependencies: - dependency-name: tornado dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 471fc60a..b16c6f06 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -118,7 +118,7 @@ tabulate==0.9.0 # via docstrfmt toml==0.10.2 # via docstrfmt -tornado==6.4 +tornado==6.4.1 # via livereload typing-extensions==4.9.0 # via From 6c50255182e149d8ac3f53ac631511c18108175f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 07:52:04 +0200 Subject: [PATCH 68/91] chore(deps): bump urllib3 from 2.1.0 to 2.2.2 in /docs (#272) Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.1.0 to 2.2.2. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.1.0...2.2.2) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b16c6f06..6878bbbd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -126,7 +126,7 @@ typing-extensions==4.9.0 # typing-inspect typing-inspect==0.9.0 # via libcst -urllib3==2.1.0 +urllib3==2.2.2 # via requests wheel==0.42.0 # via pip-tools From e2e8ac25bf4f9b42f6a56d2a9fb32df3fd74d94a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 23:32:06 +0200 Subject: [PATCH 69/91] ci: pre-commit autoupdate (#274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.15.2 → v3.16.0](https://github.com/asottile/pyupgrade/compare/v3.15.2...v3.16.0) - [github.com/adamchainz/django-upgrade: 1.18.0 → 1.19.0](https://github.com/adamchainz/django-upgrade/compare/1.18.0...1.19.0) - [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a094a773..59db67e9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,19 +9,19 @@ ci: repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.16.0 hooks: - id: pyupgrade args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade - rev: '1.18.0' + rev: '1.19.0' hooks: - id: django-upgrade args: [--target-version, "4.0"] - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + rev: 7.1.0 hooks: - id: flake8 From 0f83bbe56485672d8d5dca19597f515bef80e097 Mon Sep 17 00:00:00 2001 From: Vipul Narang <61502917+vipulnarang95@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:01:45 +0530 Subject: [PATCH 70/91] added multiselect and add to collection in admin (#273) --- CHANGELOG.rst | 4 ++++ djangocms_moderation/cms_config.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 01692029..4662f0ce 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,10 @@ Changelog ========= +Unreleased +========== +* feat: Add multi select and add to moderation in admin for moderated_models + 2.2.0 (2024-05-16) ========== * Python 3.8, 3.9 support removed diff --git a/djangocms_moderation/cms_config.py b/djangocms_moderation/cms_config.py index 6dfba873..4c71d13e 100644 --- a/djangocms_moderation/cms_config.py +++ b/djangocms_moderation/cms_config.py @@ -1,8 +1,11 @@ +from django.contrib import admin from django.core.exceptions import ImproperlyConfigured from cms.app_base import CMSAppConfig, CMSAppExtension from cms.models import PageContent +from .admin_actions import add_items_to_collection + class ModerationExtension(CMSAppExtension): def __init__(self): @@ -16,6 +19,16 @@ def handle_moderation_request_changelist_actions(self, moderation_request_change def handle_moderation_request_changelist_fields(self, moderation_request_changelist_fields): self.moderation_request_changelist_fields.extend(moderation_request_changelist_fields) + def handle_admin_actions(self, moderated_models): + """ + Add items to collection to admin actions in model admin + """ + for model in moderated_models: + if admin.site.is_registered(model): + admin_instance = admin.site._registry[model] + admin_instance.actions = admin_instance.actions or [] + admin_instance.actions.append(add_items_to_collection) + def configure_app(self, cms_config): versioning_enabled = getattr(cms_config, "djangocms_versioning_enabled", False) moderated_models = getattr(cms_config, "moderated_models", []) @@ -24,6 +37,8 @@ def configure_app(self, cms_config): raise ImproperlyConfigured("Versioning needs to be enabled for Moderation") self.moderated_models.extend(moderated_models) + if moderated_models: + self.handle_admin_actions(moderated_models) if hasattr(cms_config, "moderation_request_changelist_actions"): self.handle_moderation_request_changelist_actions(cms_config.moderation_request_changelist_actions) From 6a3c5719949f1a3d56e4ff4c973dbd69dba01380 Mon Sep 17 00:00:00 2001 From: Vipul Narang <61502917+vipulnarang95@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:43:10 +0530 Subject: [PATCH 71/91] Release 2.2.1 for djangocms-moderation (#275) --- CHANGELOG.rst | 5 ++++- djangocms_moderation/__init__.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4662f0ce..f1e63062 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,10 +4,13 @@ Changelog Unreleased ========== + +2.2.1 (2024-07-02) +================== * feat: Add multi select and add to moderation in admin for moderated_models 2.2.0 (2024-05-16) -========== +================== * Python 3.8, 3.9 support removed * Python 3.10, 3.11 and 3.12 support added * Django 2.2 support removed diff --git a/djangocms_moderation/__init__.py b/djangocms_moderation/__init__.py index 8a124bf6..b19ee4b7 100644 --- a/djangocms_moderation/__init__.py +++ b/djangocms_moderation/__init__.py @@ -1 +1 @@ -__version__ = "2.2.0" +__version__ = "2.2.1" From f8fa4a65a9a6f8c6f52f3189335101d511a608b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:44:57 +0200 Subject: [PATCH 72/91] chore(deps): bump certifi from 2023.11.17 to 2024.7.4 in /docs (#276) --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 6878bbbd..1e5785d8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -14,7 +14,7 @@ black==23.12.1 # via docstrfmt build==1.0.3 # via pip-tools -certifi==2023.11.17 +certifi==2024.7.4 # via requests charset-normalizer==3.3.2 # via requests From cf5cf9c539814479c7b90ba55d8f54ab95586dbf Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 28 Aug 2024 09:42:03 +0200 Subject: [PATCH 73/91] fix: Replace `SortableAdminMixin` by `SortableAdminBase` for `WorkflowAdmin` (#279) * fix: Replace `SortableAdminMixin` by `SortableAdminBase` for `WorkflowAdmin` * Update test action * Add test to ensure workflow admin works * Fix isort issue * django-admin-sortable2 not compatible with django 3.2 * Simplify new test * Fix isort issue --- .github/workflows/test.yml | 3 +-- djangocms_moderation/admin.py | 4 ++-- tests/test_admin.py | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37217349..606c3c76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,6 @@ jobs: dj50_cms41.txt, dj42_cms41.txt, dj42_cms40.txt, - dj32_cms40.txt, ] os: [ ubuntu-20.04, @@ -40,7 +39,7 @@ jobs: python setup.py install - name: Run coverage - run: coverage run setup.py test + run: coverage run ./tests/settings.py - name: Upload Coverage to Codecov uses: codecov/codecov-action@v4 diff --git a/djangocms_moderation/admin.py b/djangocms_moderation/admin.py index f9320fa2..f5c2b031 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 SortableAdminMixin, SortableInlineAdminMixin +from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin from treebeard.admin import TreeAdmin from . import constants, signals @@ -1011,7 +1011,7 @@ def get_extra(self, request, obj=None, **kwargs): @admin.register(Workflow) -class WorkflowAdmin(SortableAdminMixin, admin.ModelAdmin): +class WorkflowAdmin(SortableAdminBase, admin.ModelAdmin): inlines = [WorkflowStepInline] list_display = ["name", "is_default"] fields = [ diff --git a/tests/test_admin.py b/tests/test_admin.py index dad3a0cd..32dbd321 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -4,6 +4,8 @@ from django.test.client import RequestFactory from django.urls import reverse +from cms.utils.urlutils import admin_reverse + from djangocms_versioning.test_utils import factories from djangocms_moderation import conf, constants @@ -425,3 +427,16 @@ def test_tree_admin_burger_menu_present(self): response = self.client.get(url) self.assertContains(response, '/static/djangocms_moderation/js/burger.js') + + def test_workflow_admin_renders_correctly(self): + url = admin_reverse("djangocms_moderation_workflow_change", args=(self.wf.pk,)) + + with self.login_user_context(self.get_superuser()): + result = self.client.get(url, follow=True) + + self.assertEqual(result.status_code, 200) + self.assertContains(result, self.wf.name) + + # django-admin-sortable2 injected its inputs + self.assertContains(result, '') + self.assertContains(result, ' Date: Wed, 28 Aug 2024 09:45:16 +0200 Subject: [PATCH 74/91] chore(deps-dev): bump elliptic from 6.5.3 to 6.5.7 (#281) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.7. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.7) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9ab3ae4..3822eb69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2168,18 +2168,32 @@ "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } } }, "emoji-regex": { From d05b45f19f26ec871c6bf6af53eacb76703185e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 23:19:36 +0200 Subject: [PATCH 75/91] ci: pre-commit autoupdate (#278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0) - [github.com/adamchainz/django-upgrade: 1.19.0 → 1.21.0](https://github.com/adamchainz/django-upgrade/compare/1.19.0...1.21.0) - [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1) - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 59db67e9..e3f5614e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,19 +9,19 @@ ci: repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 + rev: v3.17.0 hooks: - id: pyupgrade args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade - rev: '1.19.0' + rev: '1.21.0' hooks: - id: django-upgrade args: [--target-version, "4.0"] - repo: https://github.com/PyCQA/flake8 - rev: 7.1.0 + rev: 7.1.1 hooks: - id: flake8 @@ -31,7 +31,7 @@ repos: - id: yesqa - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-merge-conflict - id: debug-statements From 648ee88db4c87ed84d422deb5cea30f3ce72def4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 08:15:21 +0100 Subject: [PATCH 76/91] chore(deps-dev): bump elliptic from 6.5.7 to 6.6.0 (#283) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3822eb69..c711ee62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2168,9 +2168,9 @@ "dev": true }, "elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dev": true, "requires": { "bn.js": "^4.11.9", From 6589f669e20d1663198eb9429bcde14feb397b78 Mon Sep 17 00:00:00 2001 From: Josh Yu Date: Wed, 6 Nov 2024 11:13:58 +0800 Subject: [PATCH 77/91] fix: Restore "In Collection" button in the toolbar (#286) Co-authored-by: Josh Yu --- CHANGELOG.rst | 2 ++ djangocms_moderation/cms_toolbars.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f1e63062..5dded14e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,8 @@ Changelog Unreleased ========== +fix: Replace SortableAdminMixin by SortableAdminBase for WorkflowAdmin +fix: Restore "In Collection" button in the toolbar 2.2.1 (2024-07-02) ================== diff --git a/djangocms_moderation/cms_toolbars.py b/djangocms_moderation/cms_toolbars.py index 7679807a..a99a7b66 100644 --- a/djangocms_moderation/cms_toolbars.py +++ b/djangocms_moderation/cms_toolbars.py @@ -54,7 +54,7 @@ def _add_moderation_buttons(self): if not helpers.is_registered_for_moderation(self.toolbar.obj): return - if self._is_versioned() and self.toolbar.edit_mode_active: + if self._is_versioned() and (self.toolbar.edit_mode_active or self.toolbar.preview_mode_active): moderation_request = helpers.get_active_moderation_request(self.toolbar.obj) if moderation_request: title, url = helpers.get_moderation_button_title_and_url( From 9ebe7a4cccdace4c2dfe65b3be65433733bec7e2 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Wed, 6 Nov 2024 08:59:57 +0100 Subject: [PATCH 78/91] fix: Update check framework to not allow publishing for moderated models (#282) * Update `check_publish * Remove patch of get_publish_link * Patch _get_publish_link and check_publish * Add test --- djangocms_moderation/monkeypatch.py | 14 ++++++++++++++ tests/test_admin.py | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/djangocms_moderation/monkeypatch.py b/djangocms_moderation/monkeypatch.py index a439850e..a1ad0f06 100644 --- a/djangocms_moderation/monkeypatch.py +++ b/djangocms_moderation/monkeypatch.py @@ -120,6 +120,17 @@ def inner(self, obj, request): return inner +def _check_registered_for_moderation(message): + """ + Fail check if object is registered for moderation + """ + def inner(version, user): + if is_registered_for_moderation(version.content): + raise ConditionFailed(message) + + return inner + + admin.VersionAdmin._get_publish_link = _get_publish_link( admin.VersionAdmin._get_publish_link ) @@ -152,5 +163,8 @@ def inner(self, obj, request): _("Cannot edit a version in an active moderation collection") ) ] +models.Version.check_publish += [ + _check_registered_for_moderation(_("Content cannot be published directly. Use the moderation process.")) +] fields.PlaceholderRelationField.default_checks += [_is_placeholder_review_unlocked] diff --git a/tests/test_admin.py b/tests/test_admin.py index 32dbd321..2c44b403 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -440,3 +440,11 @@ def test_workflow_admin_renders_correctly(self): # django-admin-sortable2 injected its inputs self.assertContains(result, '') self.assertContains(result, ' Date: Thu, 7 Nov 2024 13:37:41 +0100 Subject: [PATCH 79/91] chore: Add github actions for pypi (#288) --- .github/workflows/publish-to-live-pypi.yml | 41 +++++++++++++++++++++ .github/workflows/publish-to-test-pypi.yml | 43 ++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 .github/workflows/publish-to-live-pypi.yml create mode 100644 .github/workflows/publish-to-test-pypi.yml diff --git a/.github/workflows/publish-to-live-pypi.yml b/.github/workflows/publish-to-live-pypi.yml new file mode 100644 index 00000000..0776261d --- /dev/null +++ b/.github/workflows/publish-to-live-pypi.yml @@ -0,0 +1,41 @@ +name: Publish Python 🐍 distributions 📦 to pypi + +on: + release: + types: + - published + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to pypi + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/djangocms-moderation + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install pypa/build + run: >- + python -m + pip install + build + --user + - name: Build a binary wheel and a source tarball + run: >- + python -m + build + --sdist + --wheel + --outdir dist/ + . + + - name: Publish distribution 📦 to PyPI + if: startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml new file mode 100644 index 00000000..ae0fd790 --- /dev/null +++ b/.github/workflows/publish-to-test-pypi.yml @@ -0,0 +1,43 @@ +name: Publish Python 🐍 distributions 📦 to TestPyPI + +on: + push: + branches: + - master + +jobs: + build-n-publish: + name: Build and publish Python 🐍 distributions 📦 to TestPyPI + runs-on: ubuntu-latest + environment: + name: test + url: https://test.pypi.org/p/djangocms-moderation + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install pypa/build + run: >- + python -m + pip install + build + --user + - name: Build a binary wheel and a source tarball + run: >- + python -m + build + --sdist + --wheel + --outdir dist/ + . + + - name: Publish distribution 📦 to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip_existing: true From 8aa7aad2c650ce812925d6f2eb06576dd404a02f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:53:16 +0100 Subject: [PATCH 80/91] chore(deps): bump codecov/codecov-action from 4 to 5 (#289) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 606c3c76..c489abb2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,4 +42,4 @@ jobs: run: coverage run ./tests/settings.py - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 From 69fc2f8d11f4d3285ccc33e5fde30c7d83547d43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:51:33 +0100 Subject: [PATCH 81/91] chore(deps): bump tornado from 6.4.1 to 6.4.2 in /docs (#290) Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4.1 to 6.4.2. - [Changelog](https://github.com/tornadoweb/tornado/blob/v6.4.2/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.1...v6.4.2) --- updated-dependencies: - dependency-name: tornado dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1e5785d8..f8a8a421 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -118,7 +118,7 @@ tabulate==0.9.0 # via docstrfmt toml==0.10.2 # via docstrfmt -tornado==6.4.1 +tornado==6.4.2 # via livereload typing-extensions==4.9.0 # via From c30c4901b868c19474e5941fde241a3d7cb9e79f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:52:23 +0100 Subject: [PATCH 82/91] ci: pre-commit autoupdate (#284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.17.0 → v3.19.0](https://github.com/asottile/pyupgrade/compare/v3.17.0...v3.19.0) - [github.com/adamchainz/django-upgrade: 1.21.0 → 1.22.1](https://github.com/adamchainz/django-upgrade/compare/1.21.0...1.22.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e3f5614e..e99f5e66 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,13 +9,13 @@ ci: repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.19.0 hooks: - id: pyupgrade args: ["--py310-plus"] - repo: https://github.com/adamchainz/django-upgrade - rev: '1.21.0' + rev: '1.22.1' hooks: - id: django-upgrade args: [--target-version, "4.0"] From 2702238c2a4eec3fea5aedc5c9b3d7fc6f5a7155 Mon Sep 17 00:00:00 2001 From: Vipul Narang <61502917+vipulnarang95@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:34:43 +0530 Subject: [PATCH 83/91] Added data-popup attr to a tag (#291) --- CHANGELOG.rst | 1 + .../static/djangocms_moderation/js/burger.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5dded14e..c5df6e12 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +fix: Add data-popup attr to a tag in burger menu item fix: Replace SortableAdminMixin by SortableAdminBase for WorkflowAdmin fix: Restore "In Collection" button in the toolbar diff --git a/djangocms_moderation/static/djangocms_moderation/js/burger.js b/djangocms_moderation/static/djangocms_moderation/js/burger.js index 3fbcdf9e..93efd262 100644 --- a/djangocms_moderation/static/djangocms_moderation/js/burger.js +++ b/djangocms_moderation/static/djangocms_moderation/js/burger.js @@ -147,6 +147,7 @@ let li_anchor = document.createElement('a'); const itemId = $(item).attr('id'); const itemTarget = $(item).attr('target'); + const itemDataPopup = $(item).attr('data-popup'); li_anchor.setAttribute('class', 'cms-actions-dropdown-menu-item-anchor'); li_anchor.setAttribute('href', $(item).attr('href')); @@ -158,6 +159,10 @@ if (itemTarget !== undefined) { li_anchor.setAttribute('target', itemTarget); } + // Copy the data-popup attribute if it is set + if (itemDataPopup !== undefined) { + li_anchor.setAttribute('data-popup', itemDataPopup); + } if ($(item).hasClass('cms-form-get-method')) { // Ensure the fake-form selector is propagated to the new anchor From a448e16da7e6fa8977d8a165b1cb67836c04521e Mon Sep 17 00:00:00 2001 From: "sourcery-ai[bot]" <58596630+sourcery-ai[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:48:26 +0100 Subject: [PATCH 84/91] Add 'Configuration' section to README for moderation app settings (#293) * Add 'Configuration' section to README for moderation app settings Documentation: - Add a new 'Configuration' section in the README to document various settings of the moderation application. Resolves #20 * Update README.rst * Update README.rst * Add defaults * Fix formatting * Fix typo --------- Co-authored-by: sourcery-ai[bot] Co-authored-by: Fabian Braun --- CHANGELOG.rst | 1 + README.rst | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c5df6e12..1d7eabb0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,7 @@ Unreleased fix: Add data-popup attr to a tag in burger menu item fix: Replace SortableAdminMixin by SortableAdminBase for WorkflowAdmin fix: Restore "In Collection" button in the toolbar +fix: Update README.rst and add overview of settings 2.2.1 (2024-07-02) ================== diff --git a/README.rst b/README.rst index e2d425b2..93572458 100644 --- a/README.rst +++ b/README.rst @@ -23,8 +23,8 @@ Run:: Add the following to your project's ``INSTALLED_APPS``: - - ``'djangocms_moderation'`` - - ``'adminsortable2'`` +- ``'djangocms_moderation'`` +- ``'adminsortable2'`` Run:: @@ -32,6 +32,69 @@ Run:: to perform the application's database migrations. +Configuration +============= + +The following settings can be added to your project's settings file to configure django CMS Moderation's behavior: + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Setting + - Description + * - ``CMS_MODERATION_DEFAULT_COMPLIANCE_NUMBER_BACKEND`` + - Default backend class for generating compliance numbers. + Default is ``djangocms_moderation.backends.uuid4_backend``. + * - ``CMS_MODERATION_COMPLIANCE_NUMBER_BACKENDS`` + - List of available compliance number backend classes. + By default, three backends are configured: ``uuid4_backend``, + ``sequential_number_backend``, and + ``sequential_number_with_identifier_prefix_backend``. + * - ``CMS_MODERATION_ENABLE_WORKFLOW_OVERRIDE`` + - Enable/disable workflow override functionality. Defaults to ``False``. + * - ``CMS_MODERATION_DEFAULT_CONFIRMATION_PAGE_TEMPLATE`` + - Default template for confirmation pages. Defaults to + ``djangocms_moderation/moderation_confirmation.html`` + * - ``CMS_MODERATION_CONFIRMATION_PAGE_TEMPLATES`` + - List of available confirmation page templates. Only includes the + default template by default. + * - ``CMS_MODERATION_COLLECTION_COMMENTS_ENABLED`` + - Enable/disable comments on collections. Defaults to ``True``. + * - ``CMS_MODERATION_REQUEST_COMMENTS_ENABLED`` + - Enable/disable comments on requests. Defaults to ``True``. + * - ``CMS_MODERATION_COLLECTION_NAME_LENGTH_LIMIT`` + - Maximum length for collection names. Defaults to ``24``. + * - ``EMAIL_NOTIFICATIONS_FAIL_SILENTLY`` + - Control email notification error handling. Defaults to ``False``. + +Example Configuration +--------------------- + +Add these settings to your project's settings file: + +.. code-block:: python + + # Custom compliance number backend + CMS_MODERATION_DEFAULT_COMPLIANCE_NUMBER_BACKEND = 'myapp.backends.CustomComplianceNumberBackend' + + # Enable workflow override + CMS_MODERATION_ENABLE_WORKFLOW_OVERRIDE = True + + # Custom confirmation template + CMS_MODERATION_DEFAULT_CONFIRMATION_PAGE_TEMPLATE = 'custom_confirmation.html' + + # Enable comments + CMS_MODERATION_COLLECTION_COMMENTS_ENABLED = True + CMS_MODERATION_REQUEST_COMMENTS_ENABLED = True + + # Set collection name length limit + CMS_MODERATION_COLLECTION_NAME_LENGTH_LIMIT = 100 + + # Control email notification errors + EMAIL_NOTIFICATIONS_FAIL_SILENTLY = False + +============= Documentation ============= From 4d3c25d9ba5bcbe3a2bf6a847ab3d6c404e6c8db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:54:06 +0100 Subject: [PATCH 85/91] Bump postcss, autoprefixer and gulp-postcss (#252) Bumps [postcss](https://github.com/postcss/postcss) to 8.4.33 and updates ancestor dependencies [postcss](https://github.com/postcss/postcss), [autoprefixer](https://github.com/postcss/autoprefixer) and [gulp-postcss](https://github.com/postcss/gulp-postcss). These dependencies need to be updated together. Updates `postcss` from 5.2.18 to 8.4.33 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/commits/8.4.33) Updates `autoprefixer` from 6.7.7 to 10.4.17 - [Release notes](https://github.com/postcss/autoprefixer/releases) - [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/autoprefixer/compare/6.7.7...10.4.17) Updates `gulp-postcss` from 6.4.0 to 9.1.0 - [Release notes](https://github.com/postcss/gulp-postcss/releases) - [Commits](https://github.com/postcss/gulp-postcss/compare/6.4.0...9.1.0) --- updated-dependencies: - dependency-name: postcss dependency-type: indirect - dependency-name: autoprefixer dependency-type: direct:development - dependency-name: gulp-postcss dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 267 ++++++++++++++++++---------------------------- package.json | 4 +- 2 files changed, 107 insertions(+), 164 deletions(-) diff --git a/package-lock.json b/package-lock.json index c711ee62..e8609650 100644 --- a/package-lock.json +++ b/package-lock.json @@ -385,17 +385,43 @@ "dev": true }, "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "dev": true, "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + } + }, + "caniuse-lite": { + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.644", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.644.tgz", + "integrity": "sha512-zOnPndwz3u1sVFSyBcRWcn0529Kz+jr+tDxN9iP69I3CpC5wlvYmjLrK2O7TEsg2oDDoUqooeXqbiHLvXvl6Lg==", + "dev": true + } } }, "aws-sign2": { @@ -1768,21 +1794,6 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - } - }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -2329,6 +2340,12 @@ "es6-symbol": "^3.1.1" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2968,6 +2985,12 @@ } } }, + "fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4371,80 +4394,33 @@ } }, "gulp-postcss": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-6.4.0.tgz", - "integrity": "sha1-eKMuPIeqbNzsWuHJBeGW1HjoxdU=", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-9.1.0.tgz", + "integrity": "sha512-a843mcKPApfeI987uqQbc8l50xXeWIXBsiVvYxtCI5XtVAMzTi/HnU2qzQpGwkB/PAOfsLV8OsqDv2iJZ9qvdw==", "dev": true, "requires": { - "gulp-util": "^3.0.8", - "postcss": "^5.2.12", - "postcss-load-config": "^1.2.0", + "fancy-log": "^2.0.0", + "plugin-error": "^2.0.1", + "postcss-load-config": "^5.0.0", "vinyl-sourcemaps-apply": "^0.2.1" }, "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", - "dev": true - }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "dev": true, - "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" - } - }, - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "color-support": "^1.1.3" } }, - "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "plugin-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", + "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" + "ansi-colors": "^1.0.1" } } } @@ -4598,12 +4574,6 @@ "ansi-regex": "^2.0.0" } }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, "has-gulplog": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", @@ -4998,12 +4968,6 @@ } } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -5362,6 +5326,12 @@ "resolve": "^1.1.7" } }, + "lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true + }, "load-json-file": { "version": "1.1.0", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -5929,6 +5899,12 @@ "vm-browserify": "0.0.4" } }, + "node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, "node-sass": { "version": "4.14.1", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", @@ -6285,7 +6261,7 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, "npm-run-path": { @@ -6309,12 +6285,6 @@ "set-blocking": "~2.0.0" } }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -6657,6 +6627,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -6711,54 +6687,20 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.0.2.tgz", + "integrity": "sha512-Q8QR3FYbqOKa0bnC1UQ2bFq9/ulHX5Bi34muzitMr8aDtUelO5xKeJEYC/5smE0jNE9zdB/NBnOwXKexELbRlw==", "dev": true, "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" } }, "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "prelude-ls": { @@ -7130,12 +7072,6 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", @@ -7771,15 +7707,6 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - }, "table": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", @@ -8221,6 +8148,16 @@ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", "dev": true }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -8697,6 +8634,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true + }, + "yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true } } } diff --git a/package.json b/package.json index 20f8cd45..9f70ede9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "djangoCMS", "private": true, "devDependencies": { - "autoprefixer": "^6.3.6", + "autoprefixer": "^10.4.17", "babel-core": "^6.24.1", "babel-eslint": "^7.2.3", "babel-loader": "^7.1.0", @@ -19,7 +19,7 @@ "gulp-eslint": "^3.0.1", "gulp-if": "1.2.5", "gulp-plumber": "^1.1.0", - "gulp-postcss": "^6.4.0", + "gulp-postcss": "^9.1.0", "gulp-sass": "3.1.0", "gulp-sourcemaps": "2.4.1", "gulp-util": "3.0.5", From cd803916aa1ab5a7c28df7775ba0a6302d567c38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:56:44 +0100 Subject: [PATCH 86/91] Bump fsevents from 1.2.4 to 1.2.13 (#250) Bumps [fsevents](https://github.com/fsevents/fsevents) from 1.2.4 to 1.2.13. - [Release notes](https://github.com/fsevents/fsevents/releases) - [Commits](https://github.com/fsevents/fsevents/compare/v1.2.4...v1.2.13) --- updated-dependencies: - dependency-name: fsevents dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 610 ++-------------------------------------------- 1 file changed, 25 insertions(+), 585 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8609650..9f94f279 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1211,6 +1211,15 @@ "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -2838,6 +2847,12 @@ "object-assign": "^4.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -3007,588 +3022,20 @@ "dev": true }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "bindings": "^1.5.0", + "nan": "^2.12.1" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true, - "optional": true + "nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "dev": true } } }, @@ -5790,13 +5237,6 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, - "nan": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", - "dev": true, - "optional": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", From 09077f60b0b62f4f7854f2c8c69580c105b58f54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:59:04 +0100 Subject: [PATCH 87/91] Bump browserify-sign from 4.0.4 to 4.2.2 (#251) Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.0.4 to 4.2.2. - [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md) - [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.0.4...v4.2.2) --- updated-dependencies: - dependency-name: browserify-sign dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 119 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f94f279..886b5826 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1328,18 +1328,117 @@ } }, "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dev": true, "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.4", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } } }, "browserify-zlib": { From cdb176d49664048c39df706807b95c09cb0ff446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:00:55 +0100 Subject: [PATCH 88/91] Bump liskin/gh-problem-matcher-wrap from 1 to 3 (#245) Bumps [liskin/gh-problem-matcher-wrap](https://github.com/liskin/gh-problem-matcher-wrap) from 1 to 3. - [Release notes](https://github.com/liskin/gh-problem-matcher-wrap/releases) - [Commits](https://github.com/liskin/gh-problem-matcher-wrap/compare/v1...v3) --- updated-dependencies: - dependency-name: liskin/gh-problem-matcher-wrap dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5527c571..b6da3f9d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: - name: Install flake8 run: pip install --upgrade flake8 - name: Run flake8 - uses: liskin/gh-problem-matcher-wrap@v1 + uses: liskin/gh-problem-matcher-wrap@v3 with: linters: flake8 run: flake8 @@ -32,7 +32,7 @@ jobs: python-version: '3.10' - run: python -m pip install isort - name: isort - uses: liskin/gh-problem-matcher-wrap@v1 + uses: liskin/gh-problem-matcher-wrap@v3 with: linters: isort run: isort -c -rc -df ./ From 6fff655a262642a0280d9f92509cdbc1dfdb5d93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:02:54 +0100 Subject: [PATCH 89/91] Bump qs from 6.5.2 to 6.5.3 (#237) Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3. - [Release notes](https://github.com/ljharb/qs/releases) - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3) --- updated-dependencies: - dependency-name: qs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 886b5826..a3649a6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6316,9 +6316,9 @@ "dev": true }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "querystring": { From ab2666569bc1876aad53526c47b4875c17884940 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:04:34 +0100 Subject: [PATCH 90/91] Bump decode-uri-component from 0.2.0 to 0.2.2 (#236) Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a3649a6f..2fc4f8d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2067,9 +2067,9 @@ "dev": true }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true }, "deep-is": { From 71e176e691b98a172b1573d8885705ba5bb80bd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:02:44 +0100 Subject: [PATCH 91/91] Bump y18n from 3.2.1 to 3.2.2 (#189) Bumps [y18n](https://github.com/yargs/y18n) from 3.2.1 to 3.2.2. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabian Braun --- package-lock.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fc4f8d0..09abce12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5731,12 +5731,6 @@ "strip-ansi": "^5.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", @@ -8029,6 +8023,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -8163,9 +8163,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": {