diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cdda81be..41938de6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,7 @@ Unreleased ========== * fix: Add keyword arguments in VersionAdminMixin render_change_form * feat: Reversable generic foreign key lookup from version +* feat: Provide additional information about unpublished/published versions when sending signals * fix: formatted files through ruff to fix tests * fix: Remove version check when evaluating CMS PageContent objects diff --git a/djangocms_versioning/models.py b/djangocms_versioning/models.py index b36cc5bd..4f3c2689 100644 --- a/djangocms_versioning/models.py +++ b/djangocms_versioning/models.py @@ -357,13 +357,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) @@ -391,11 +394,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() @@ -411,7 +414,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/api/signals.rst b/docs/api/signals.rst index 9adc9074..73eb45f3 100644 --- a/docs/api/signals.rst +++ b/docs/api/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. diff --git a/tests/test_signals.py b/tests/test_signals.py index 47b24ec2..3d5d25f9 100644 --- a/tests/test_signals.py +++ b/tests/test_signals.py @@ -48,6 +48,31 @@ 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!