Skip to content

Commit b1bee98

Browse files
committed
fix: Non superusers do not need unlock permission to edit unlocked or own drafts
1 parent 883d077 commit b1bee98

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

djangocms_versioning/conditions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ def inner(version, user):
8080

8181
def user_can_unlock(message: str) -> callable:
8282
def inner(version, user):
83-
if not user.has_perm("djangocms_versioning.delete_versionlock"):
83+
if conf.LOCK_VERSIONS:
84+
if user.has_perm("djangocms_versioning.delete_versionlock"):
85+
return
86+
draft_version = get_latest_draft_version(version)
87+
if draft_version and (draft_version.locked_by == user or draft_version.locked_by is None):
88+
return
8489
raise ConditionFailed(message)
8590
return inner
8691

tests/test_locking.py

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from cms.models import PlaceholderRelationField
44
from cms.test_utils.testcases import CMSTestCase
55
from cms.toolbar.items import TemplateItem
6-
from cms.toolbar.utils import get_object_preview_url
6+
from cms.toolbar.utils import get_object_edit_url, get_object_preview_url
77
from cms.utils import get_current_site
88
from django.contrib import admin
99
from django.contrib.auth.models import Permission
@@ -130,6 +130,132 @@ def test_user_does_not_have_change_permission(self):
130130
self.assertIsNotNone(version.locked_by) # Was locked
131131
self.assertEqual(response.status_code, 403)
132132

133+
def test_editor_without_delete_versionlock_permission_can_edit_unlocked_content(self):
134+
"""
135+
Non-superuser editors with delete_versionlock permission can access edit mode
136+
of unlocked content. This test verifies the fix for the issue where editors
137+
without delete_versionlock permission were unable to access edit mode even
138+
with appropriate change permissions.
139+
"""
140+
# Create an editor user with change permissions and no delete_versionlock permission
141+
editor = self._create_user(
142+
"editor_without_unlock",
143+
is_staff=True,
144+
is_superuser=False,
145+
permissions=["change_pollcontentversion"],
146+
)
147+
148+
# Create a version without a lock (unlocked)
149+
version = factories.PageVersionFactory(state=DRAFT, locked_by=None)
150+
151+
# Editor should be able to access the edit view
152+
url = get_object_edit_url(version.content)
153+
154+
with self.login_user_context(editor):
155+
response = self.client.get(url)
156+
157+
# Should succeed with 200 status - this is the key test
158+
# Without delete_versionlock permission, this would return 403
159+
self.assertEqual(response.status_code, 200)
160+
161+
def test_editor_without_delete_versionlock_permission_can_edit_their_locked_content(self):
162+
"""
163+
Non-superuser editors with delete_versionlock permission can access edit mode
164+
of unlocked content. This test verifies the fix for the issue where editors
165+
without delete_versionlock permission were unable to access edit mode even
166+
with appropriate change permissions.
167+
"""
168+
# Create an editor user with change permissions and no delete_versionlock permission
169+
editor = self._create_user(
170+
"editor_without_unlock",
171+
is_staff=True,
172+
is_superuser=False,
173+
permissions=["change_pollcontentversion"],
174+
)
175+
176+
# Create a version without a lock (unlocked)
177+
version = factories.PageVersionFactory(state=DRAFT, locked_by=editor)
178+
179+
# Editor should be able to access the edit view
180+
url = get_object_edit_url(version.content)
181+
182+
with self.login_user_context(editor):
183+
response = self.client.get(url)
184+
185+
# Should succeed with 200 status - this is the key test
186+
# Without delete_versionlock permission, this would return 403
187+
self.assertEqual(response.status_code, 200)
188+
189+
def test_editor_without_delete_versionlock_permission_cannot_edit_others_content(self):
190+
"""
191+
Non-superuser editors without delete_versionlock permission cannot access
192+
edit mode of content locked by another user. This is the expected behavior
193+
that was causing issues when users didn't have the delete_versionlock permission.
194+
"""
195+
# Create an editor user without delete_versionlock permission
196+
editor_without_unlock = self._create_user(
197+
"editor_without_unlock",
198+
is_staff=True,
199+
is_superuser=False,
200+
permissions=["change_pollcontentversion"],
201+
)
202+
203+
# Create a version locked by another user
204+
author = factories.UserFactory(is_staff=True)
205+
version = factories.PageVersionFactory(
206+
state=DRAFT,
207+
created_by=author,
208+
locked_by=author
209+
)
210+
211+
# Editor without unlock permission should not be able to access edit view
212+
url = get_object_edit_url(version.content)
213+
214+
with self.login_user_context(editor_without_unlock):
215+
response = self.client.get(url)
216+
217+
# Should be denied with 302 status -> redirect to preview
218+
self.assertEqual(response.status_code, 302)
219+
220+
# Version should still be locked by the original author
221+
updated_version = Version.objects.get(pk=version.pk)
222+
self.assertEqual(updated_version.locked_by, author)
223+
224+
def test_editor_with_delete_versionlock_permission_can_edit_others_content(self):
225+
"""
226+
Non-superuser editors without delete_versionlock permission cannot access
227+
edit mode of content locked by another user. This is the expected behavior
228+
that was causing issues when users didn't have the delete_versionlock permission.
229+
"""
230+
# Create an editor user without delete_versionlock permission
231+
editor_with_unlock = self._create_user(
232+
"editor_with_unlock",
233+
is_staff=True,
234+
is_superuser=False,
235+
permissions=["delete_versionlock"],
236+
)
237+
238+
# Create a version locked by another user
239+
author = factories.UserFactory(is_staff=True)
240+
version = factories.PageVersionFactory(
241+
state=DRAFT,
242+
created_by=author,
243+
locked_by=author
244+
)
245+
246+
# Editor without unlock permission should not be able to access edit view
247+
url = get_object_edit_url(version.content)
248+
249+
with self.login_user_context(editor_with_unlock):
250+
response = self.client.get(url)
251+
252+
# Should be redirected with 302 status since user first must unlock
253+
self.assertEqual(response.status_code, 302)
254+
255+
# Version should still be locked by the original author
256+
updated_version = Version.objects.get(pk=version.pk)
257+
self.assertEqual(updated_version.locked_by, author)
258+
133259

134260
@override_settings(DJANGOCMS_VERSIONING_LOCK_VERSIONS=True)
135261
class VersionLockUnlockTestCase(CMSTestCase):

0 commit comments

Comments
 (0)