From 02068e70e786259f03d05f0f0e3251f1693bd654 Mon Sep 17 00:00:00 2001 From: Josh Yu Date: Tue, 2 Apr 2024 21:08:04 +0800 Subject: [PATCH] feat: Provide additional information when sending publish/unpublish events (cherry-pick #348) --- CHANGELOG.rst | 1 + djangocms_versioning/models.py | 16 +++++++++++----- docs/signals.rst | 15 +++++++++++++++ tests/test_signals.py | 23 +++++++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1126dc1d..c3f7f940 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,7 @@ Changelog Unreleased ========== +* feat: Provide additional information when sending publish/unpublish events (cherry-pick #348) * ci: Updated isort params in lint workflow to meet current requirements. * ci: Update actions to v3 where possible, and coverage to v2 due to v1 sunset in Feb * ci: Remove ``os`` from test workflow matrix because it's unused diff --git a/djangocms_versioning/models.py b/djangocms_versioning/models.py index 51af3fe0..579c9f71 100644 --- a/djangocms_versioning/models.py +++ b/djangocms_versioning/models.py @@ -289,13 +289,16 @@ def publish(self, user): content_type=self.content_type, ) for version in to_unpublish: - version.unpublish(user) + version.unpublish(user, to_be_published=self) on_publish = self.versionable.on_publish if on_publish: on_publish(self) # trigger post operation signal send_post_version_operation( - constants.OPERATION_PUBLISH, version=self, token=action_token + constants.OPERATION_PUBLISH, + version=self, + token=action_token, + unpublished=list(to_unpublish), ) if emit_content_change: emit_content_change(self.content) @@ -320,11 +323,11 @@ def _set_publish(self, user): def can_be_unpublished(self): return can_proceed(self._set_unpublish) - def unpublish(self, user): + def unpublish(self, user, to_be_published=None): """Change state to UNPUBLISHED""" # trigger pre operation signal action_token = send_pre_version_operation( - constants.OPERATION_UNPUBLISH, version=self + constants.OPERATION_UNPUBLISH, version=self, to_be_published=to_be_published ) self._set_unpublish(user) self.modified = timezone.now() @@ -340,7 +343,10 @@ def unpublish(self, user): on_unpublish(self) # trigger post operation signal send_post_version_operation( - constants.OPERATION_UNPUBLISH, version=self, token=action_token + constants.OPERATION_UNPUBLISH, + version=self, + token=action_token, + to_be_published=to_be_published, ) if emit_content_change: emit_content_change(self.content) diff --git a/docs/signals.rst b/docs/signals.rst index 9adc9074..d08bbf65 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -32,3 +32,18 @@ The CMS used to provide page publish and unpublish signals which have since been # ... do something +Handling the effect of a (un-)publish to other items via signals +---------------------------------------------------------------- + +Events often times do not happen in isolation. +A publish signal indicates a publish of an item but it also means that potentially other items are unpublished as part of the same action, also triggering unpublish signals. +To be able to react accordingly, information is added to the publish signal which other items were potentially unpublished as part of this action (`unpublished`) and information is also added to the unpublish singal which other items are going to get published (`to_be_published`). +This information allows you to differentiate if an item is published for the first time - because nothing is unpublished - or if it is just a new version of an existing item. + +For example, the differentiation can be benefitial if you integrate with other services like Elasticsearch and you want to update the Elasticsearch index via signals. You can get in the following situations: + - Publish signal with no unpublished item results in a new entry in the index. + - Publish signal with at least one unpublished item results in an update of an existing entry in the index. + - Unpublish singal with no to be published items results in the removal of the entry from the index. + - Unpublish signal with a to be published item results in the update on an existing entry in the index but will be handled in the corresponding publish signal and can be ignored. + +All those situations are distinct, require different information, and can be handled according to requirements. \ No newline at end of file diff --git a/tests/test_signals.py b/tests/test_signals.py index e90055fe..40c4dfc7 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -46,6 +46,29 @@ def test_publish_signals_fired(self): ) self.assertEqual(post_call_kwargs["obj"], version) + def test_publish_signals_fired_with_to_be_published_and_unpublished(self): + poll = factories.PollFactory() + version1 = factories.PollVersionFactory( + state=constants.DRAFT, content__poll=poll + ) + version2 = version1.copy(self.superuser) + + # Here, we just expect the signals for version 1 + with signal_tester(pre_version_operation, post_version_operation) as env: + version1.publish(self.superuser) + self.assertEqual(env.call_count, 2) + + # Here, we expect the signals for the unpublish of version 1 and the + # publish of version 2. + with signal_tester(pre_version_operation, post_version_operation) as env: + version2.publish(self.superuser) + self.assertEqual(env.call_count, 4) + version_1_pre_call_kwargs = env.calls[1][1] + version_2_post_call_kwargs = env.calls[3][1] + + self.assertEqual(version_1_pre_call_kwargs["to_be_published"], version2) + self.assertEqual(version_2_post_call_kwargs["unpublished"], [version1]) + def test_unpublish_signals_fired(self): """ When a version is changed to unpublished the correct signals are fired!