Skip to content

Commit f43c923

Browse files
fsbraunjrief
andauthored
fix: Consistent use of action buttons (#392)
* fix #384: Unlock button in toolbar points onto DRAFT version * fix: consistent logic for action buttons (availability & enabled/disabled) * fix linting * Remove debug statement * Add missing unlock condition --------- Co-authored-by: Jacob Rief <[email protected]>
1 parent 2094542 commit f43c923

File tree

5 files changed

+28
-26
lines changed

5 files changed

+28
-26
lines changed

djangocms_versioning/admin.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -685,13 +685,13 @@ def _get_archive_link(self, obj, request, disabled=False):
685685
icon="archive",
686686
title=_("Archive"),
687687
name="archive",
688-
disabled=not obj.can_be_archived(),
688+
disabled=not obj.check_archive.as_bool(request.user),
689689
)
690690

691691
def _get_publish_link(self, obj, request):
692692
"""Helper function to get the html link to the publish action
693693
"""
694-
if not obj.check_publish.as_bool(request.user):
694+
if not obj.can_be_published():
695695
# Don't display the link if it can't be published
696696
return ""
697697
publish_url = reverse(
@@ -704,14 +704,14 @@ def _get_publish_link(self, obj, request):
704704
title=_("Publish"),
705705
name="publish",
706706
action="post",
707-
disabled=not obj.can_be_published(),
707+
disabled=not obj.check_publish.as_bool(request.user),
708708
keepsideframe=False,
709709
)
710710

711711
def _get_unpublish_link(self, obj, request, disabled=False):
712712
"""Helper function to get the html link to the unpublish action
713713
"""
714-
if not obj.check_unpublish.as_bool(request.user):
714+
if not obj.can_be_unpublished():
715715
# Don't display the link if it can't be unpublished
716716
return ""
717717
unpublish_url = reverse(
@@ -723,15 +723,12 @@ def _get_unpublish_link(self, obj, request, disabled=False):
723723
icon="unpublish",
724724
title=_("Unpublish"),
725725
name="unpublish",
726-
disabled=not obj.can_be_unpublished(),
726+
disabled=not obj.check_unpublish.as_bool(request.user),
727727
)
728728

729729
def _get_edit_link(self, obj, request, disabled=False):
730730
"""Helper function to get the html link to the edit action
731731
"""
732-
if not obj.check_edit_redirect.as_bool(request.user):
733-
return ""
734-
735732
# Only show if no draft exists
736733
if obj.state == PUBLISHED:
737734
pks_for_grouper = obj.versionable.for_content_grouping_values(
@@ -761,14 +758,14 @@ def _get_edit_link(self, obj, request, disabled=False):
761758
title=_("Edit") if icon == "pencil" else _("New Draft"),
762759
name="edit",
763760
action="post",
764-
disabled=disabled,
761+
disabled=not obj.check_edit_redirect.as_bool(request.user) or disabled,
765762
keepsideframe=keepsideframe,
766763
)
767764

768765
def _get_revert_link(self, obj, request, disabled=False):
769766
"""Helper function to get the html link to the revert action
770767
"""
771-
if not obj.check_revert.as_bool(request.user):
768+
if obj.state in (PUBLISHED, DRAFT):
772769
# Don't display the link if it's a draft or published
773770
return ""
774771

@@ -781,13 +778,13 @@ def _get_revert_link(self, obj, request, disabled=False):
781778
icon="undo",
782779
title=_("Revert"),
783780
name="revert",
784-
disabled=disabled,
781+
disabled=not obj.check_revert.as_bool(request.user) or disabled,
785782
)
786783

787784
def _get_discard_link(self, obj, request, disabled=False):
788785
"""Helper function to get the html link to the discard action
789786
"""
790-
if not obj.check_discard.as_bool(request.user):
787+
if obj.state != DRAFT:
791788
# Don't display the link if it's not a draft
792789
return ""
793790

@@ -800,7 +797,7 @@ def _get_discard_link(self, obj, request, disabled=False):
800797
icon="bin",
801798
title=_("Discard"),
802799
name="discard",
803-
disabled=disabled,
800+
disabled=not obj.check_discard.as_bool(request.user) or disabled,
804801
)
805802

806803
def _get_unlock_link(self, obj, request):
@@ -811,20 +808,14 @@ def _get_unlock_link(self, obj, request):
811808
if not conf.LOCK_VERSIONS or obj.state != DRAFT or not version_is_locked(obj):
812809
return ""
813810

814-
disabled = True
815-
# Check whether the lock can be removed
816-
# Check that the user has unlock permission
817-
if request.user.has_perm("djangocms_versioning.delete_versionlock"):
818-
disabled = False
819-
820811
unlock_url = reverse(f"admin:{obj._meta.app_label}_{self.model._meta.model_name}_unlock", args=(obj.pk,))
821812
return self.admin_action_button(
822813
unlock_url,
823814
icon="unlock",
824815
title=_("Unlock"),
825816
name="unlock",
826817
action="post",
827-
disabled=disabled,
818+
disabled=not obj.check_unlock.as_bool(request.user),
828819
)
829820

830821
def get_actions_list(self):

djangocms_versioning/conditions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ def inner(version, user):
7777
raise ConditionFailed(message)
7878
return inner
7979

80+
def user_can_unlock(message: str) -> callable:
81+
def inner(version, user):
82+
if not user.has_perm("djangocms_versioning.delete_versionlock"):
83+
raise ConditionFailed(message)
84+
return inner
85+
8086
def user_can_publish(message: str) -> callable:
8187
def inner(version, user):
8288
if not version.has_publish_permission(user):
@@ -89,4 +95,3 @@ def inner(version, user):
8995
if not version.has_change_permission(user):
9096
raise ConditionFailed(message)
9197
return inner
92-

djangocms_versioning/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
is_not_locked,
1919
user_can_change,
2020
user_can_publish,
21+
user_can_unlock,
2122
)
2223
from .conf import ALLOW_DELETING_VERSIONS, LOCK_VERSIONS
2324
from .operations import send_post_version_operation, send_pre_version_operation
@@ -492,6 +493,7 @@ def _has_permission(self, perm: str, user) -> bool:
492493
[
493494
in_state([constants.DRAFT], not_draft_error),
494495
draft_is_not_locked(lock_draft_error_message),
496+
user_can_unlock(permission_error_message),
495497
]
496498
)
497499
check_revert = Conditions(
@@ -529,6 +531,7 @@ def _has_permission(self, perm: str, user) -> bool:
529531
[
530532
in_state([constants.DRAFT, constants.PUBLISHED], not_draft_error),
531533
draft_is_locked(_("Draft version is not locked"))
534+
532535
]
533536
)
534537

tests/test_admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ class StateActionsTestCase(CMSTestCase):
729729
def test_archive_in_state_actions_for_draft_version(self):
730730
version = factories.PollVersionFactory(state=constants.DRAFT)
731731
request = RequestFactory().get("/admin/polls/pollcontent/")
732-
request.user = factories.UserFactory()
732+
request.user = self.get_superuser()
733733
# Get the version model proxy from the main admin site
734734
# Trying to test this on the plain Version model throws exceptions
735735
version_model_proxy = [

tests/test_locking.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,13 @@ def test_unlock_link_not_present_for_user_with_no_unlock_privileges(self):
270270
locked_by=self.user_author)
271271
changelist_url = version_list_url(poll_version.content)
272272
unlock_url = self.get_admin_url(self.versionable.version_model_proxy, "unlock", poll_version.pk)
273-
273+
exprected_disabled_button = (
274+
f'<a class="btn cms-form-post-method cms-action-btn cms-action-unlock js-action js-keep-sideframe" '
275+
f'href="{unlock_url}" title="Unlock"><span class="cms-icon cms-icon-unlock"></span></a>'
276+
)
274277
with self.login_user_context(self.user_has_no_unlock_perms):
275278
response = self.client.post(changelist_url)
276-
277-
self.assertNotContains(response, unlock_url)
279+
self.assertInHTML(exprected_disabled_button, response.content.decode("utf-8"))
278280

279281
def test_unlock_link_present_for_user_with_privileges(self):
280282
poll_version = factories.PollVersionFactory(
@@ -392,11 +394,12 @@ def test_edit_action_link_disabled_state(self):
392394
author_request.user = self.user_author
393395
otheruser_request = RequestFactory()
394396
otheruser_request.user = self.superuser
397+
expected_disabled_state = "inactive"
395398

396399
actual_disabled_state = self.version_admin._get_edit_link(version, otheruser_request)
397400

398401
self.assertFalse(version.check_edit_redirect.as_bool(self.superuser))
399-
self.assertEqual("", actual_disabled_state)
402+
self.assertIn(expected_disabled_state, actual_disabled_state)
400403

401404

402405
@override_settings(DJANGOCMS_VERSIONING_LOCK_VERSIONS=True)

0 commit comments

Comments
 (0)