Skip to content

Commit b8b48e5

Browse files
committed
feat: Add setting to only protect published objects from deletion
1 parent da78ec1 commit b8b48e5

File tree

4 files changed

+78
-8
lines changed

4 files changed

+78
-8
lines changed

djangocms_versioning/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@
2626
"archived": _("Archived"),
2727
"empty": _("Empty"),
2828
}
29+
30+
DELETE_NON_PUBLIC_ONLY = "non-public only"
31+
DELETE_ANY = "any"
32+
DELETE_NONE = "none"

djangocms_versioning/models.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,18 @@
3535
permission_error_message = _("You do not have permission to perform this action")
3636

3737

38+
def PROTECT_IF_PUBLIC_VERSION(collector, field, sub_objs, using):
39+
state = sub_objs[0].state # State of the first version in sub_objs
40+
if state == constants.PUBLISHED:
41+
models.PROTECT(collector, field, sub_objs, using)
42+
models.SET_NULL(collector, field, sub_objs, using)
43+
44+
3845
def allow_deleting_versions(collector, field, sub_objs, using):
39-
if ALLOW_DELETING_VERSIONS:
46+
if ALLOW_DELETING_VERSIONS == constants.DELETE_NON_PUBLIC_ONLY:
47+
PROTECT_IF_PUBLIC_VERSION(collector, field, sub_objs, using)
48+
elif ALLOW_DELETING_VERSIONS == constants.DELETE_ANY or ALLOW_DELETING_VERSIONS is True:
49+
# Backwards compatibility: True means DELETE_ANY
4050
models.SET_NULL(collector, field, sub_objs, using)
4151
else:
4252
models.PROTECT(collector, field, sub_objs, using)

docs/api/settings.rst

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Settings for djangocms Versioning
44

55
.. py:attribute:: DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS
66
7-
Defaults to ``False``
7+
Defaults to ``False`` (``constants.DELETE_NONE``)
88

99
This setting controls if the ``source`` field of a ``Version`` object is
1010
protected. It is protected by default which implies that Django will not allow a user
@@ -14,10 +14,17 @@ Settings for djangocms Versioning
1414

1515
This is to protect the record of how different versions have come about.
1616

17-
If set to ``True`` users can delete version objects if the have the appropriate
18-
rights. Set this to ``True`` if you want users to be able to delete versioned
19-
objects and you do not need a full history of versions, e.g. for documentation
20-
purposes.
17+
If set to ``constants.DELETE_ANY`` (``"any"``)) users can delete version objects
18+
if the have the appropriate rights. Set this to ``"any"`` if you want users to be
19+
able to delete versioned objects and you do not need a full history of versions,
20+
e.g. for documentation purposes.
21+
22+
If set to ``constants.DELETE_NON_PUBLIC_ONLY`` (``"non-public only"``) users can
23+
delete version objects which are not published and which are not a source for
24+
another version object. This allows users to delete draft, unpublished, or archived
25+
versions, but protects published versions from deletion. This is particularly
26+
useful if you want to allow users to clean up their version history, but still want to
27+
ensure that published content remains documented and is not deleted accidentally.
2128

2229
The latest version (which is not a source of a newer version) can always be
2330
deleted (if the user has the appropriate rights).

tests/test_settings.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
class DeletionTestCase(CMSTestCase):
11-
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=False)
11+
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=constants.DELETE_NONE)
1212
def test_deletion_not_possible(self):
1313
# Since djangocms_versionings.models stores the setting, we need to update that copy
1414
versioning_models.ALLOW_DELETING_VERSIONS = settings.DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS
@@ -32,7 +32,7 @@ def test_deletion_not_possible(self):
3232
self.assertRaises(models.deletion.ProtectedError,
3333
versioning_models.Version.objects.get(pk=pk1).content.delete)
3434

35-
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=True)
35+
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=constants.DELETE_ANY)
3636
def test_deletion_possible(self):
3737
# Since djangocms_versionings.models stores the setting, we need to update that copy
3838
versioning_models.ALLOW_DELETING_VERSIONS = settings.DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS
@@ -55,3 +55,52 @@ def test_deletion_possible(self):
5555
# try deleting and see if error is raised
5656
versioning_models.Version.objects.get(pk=pk1).content.delete()
5757
self.assertIsNone(versioning_models.Version.objects.get(pk=version2.pk).source)
58+
59+
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=constants.DELETE_NON_PUBLIC_ONLY)
60+
def test_deletion_non_public_possible(self):
61+
# Since djangocms_versionings.models stores the setting, we need to update that copy
62+
versioning_models.ALLOW_DELETING_VERSIONS = settings.DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS
63+
poll = factories.PollFactory()
64+
version1 = factories.PollVersionFactory(
65+
content__poll=poll,
66+
content__language="en",
67+
)
68+
pk1 = version1.pk
69+
# Now publish and then edit redirect to create a draft on top of published version
70+
version1.publish(user=self.get_superuser())
71+
self.assertEqual(versioning_models.Version.objects.get(pk=pk1).state, constants.PUBLISHED)
72+
73+
version2 = version1.copy(created_by=self.get_superuser())
74+
version2.save()
75+
76+
# Check of source field is set
77+
self.assertIsNotNone(version2.source)
78+
79+
# try deleting and see if error is raised
80+
versioning_models.Version.objects.get(pk=pk1).content.delete()
81+
self.assertIsNone(versioning_models.Version.objects.get(pk=version2.pk).source)
82+
83+
@override_settings(DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS=constants.DELETE_NON_PUBLIC_ONLY)
84+
def test_deletion_public_not_possible(self):
85+
# Since djangocms_versionings.models stores the setting, we need to update that copy
86+
versioning_models.ALLOW_DELETING_VERSIONS = settings.DJANGOCMS_VERSIONING_ALLOW_DELETING_VERSIONS
87+
poll = factories.PollFactory()
88+
version1 = factories.PollVersionFactory(
89+
content__poll=poll,
90+
content__language="en",
91+
)
92+
pk1 = version1.pk
93+
# Now publish and then edit redirect to create a draft on top of published version
94+
version1.publish(user=self.get_superuser())
95+
self.assertEqual(versioning_models.Version.objects.get(pk=pk1).state, constants.PUBLISHED)
96+
97+
version2 = version1.copy(created_by=self.get_superuser())
98+
version2.save()
99+
version2.publish(user=self.get_superuser())
100+
101+
# Check of source field is set
102+
self.assertIsNotNone(version2.source)
103+
104+
# try deleting and see if error is raised
105+
self.assertRaises(models.deletion.ProtectedError,
106+
versioning_models.Version.objects.get(pk=pk1).content.delete)

0 commit comments

Comments
 (0)