Skip to content

Commit 8421964

Browse files
authored
Merge pull request #327 from django-cms/fix/issue-326
fix: Attribute error throws for empty workflow
2 parents 6d1b390 + 9931e31 commit 8421964

File tree

3 files changed

+214
-6
lines changed

3 files changed

+214
-6
lines changed

djangocms_moderation/admin.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from django.utils.html import format_html, format_html_join
1313
from django.utils.translation import gettext, gettext_lazy as _, ngettext
1414

15-
from cms.admin.placeholderadmin import PlaceholderAdminMixin
1615
from cms.toolbar.utils import get_object_preview_url
1716
from cms.utils.helpers import is_editable_model
1817

@@ -1162,7 +1161,7 @@ def has_delete_permission(self, request, obj=None):
11621161

11631162

11641163
@admin.register(ConfirmationPage)
1165-
class ConfirmationPageAdmin(PlaceholderAdminMixin, admin.ModelAdmin):
1164+
class ConfirmationPageAdmin(admin.ModelAdmin):
11661165
view_on_site = True
11671166

11681167
def get_urls(self):

djangocms_moderation/managers.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ class CollectionManager(Manager):
5858
def get_queryset(self):
5959
return CollectionQuerySet(self.model, using=self._db)
6060

61+
def prefetch_reviewers(self):
62+
"""
63+
Proxy to the queryset method.
64+
"""
65+
return self.get_queryset().prefetch_reviewers()
66+
6167
def reviewers(self, collection):
6268
"""
6369
Returns a set of all reviewers assigned to any ModerationRequestAction
@@ -80,9 +86,8 @@ def reviewers(self, collection):
8086
reviewers.add(mra.to_user)
8187

8288
if not reviewers_in_actions and collection.status != COLLECTING:
83-
role = collection.workflow.first_step.role
84-
users = role.get_users_queryset()
85-
for user in users:
86-
reviewers.add(user)
89+
first_step = collection.workflow.first_step
90+
if first_step:
91+
reviewers |= set(first_step.role.get_users_queryset())
8792

8893
return reviewers

tests/test_managers.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from djangocms_moderation import constants
12
from djangocms_moderation.models import ModerationCollection
23

34
from .utils.base import BaseTestCase
@@ -12,3 +13,206 @@ def test_reviewers_wont_execute_too_many_queries(self):
1213
colls = ModerationCollection.objects.all().prefetch_reviewers()
1314
for collection in colls:
1415
ModerationCollection.objects.reviewers(collection)
16+
17+
def test_reviewers_returns_users_from_actions(self):
18+
"""Test that reviewers method returns users assigned to actions"""
19+
# collection1 has moderation_request1 with action assigned to user2
20+
reviewers = ModerationCollection.objects.reviewers(self.collection1)
21+
22+
self.assertIn(self.user2, reviewers)
23+
self.assertEqual(len(reviewers), 1)
24+
25+
def test_reviewers_returns_multiple_users_from_actions(self):
26+
"""Test that reviewers method returns all users from multiple actions"""
27+
# collection2 has moderation_request2 with multiple actions to user2
28+
reviewers = ModerationCollection.objects.reviewers(self.collection2)
29+
30+
self.assertIn(self.user2, reviewers)
31+
self.assertEqual(len(reviewers), 1)
32+
33+
def test_reviewers_returns_unique_users(self):
34+
"""Test that reviewers method returns unique users even if assigned multiple times"""
35+
# Add another action with the same user to collection1
36+
self.moderation_request1.actions.create(
37+
to_user=self.user2,
38+
by_user=self.user,
39+
action=constants.ACTION_APPROVED,
40+
step_approved=self.wf1st1,
41+
)
42+
43+
reviewers = ModerationCollection.objects.reviewers(self.collection1)
44+
45+
# Should still be just one user (unique)
46+
self.assertEqual(len(reviewers), 1)
47+
self.assertIn(self.user2, reviewers)
48+
49+
def test_reviewers_falls_back_to_first_step_role_when_no_actions_with_users(self):
50+
"""Test that reviewers falls back to first step role users when actions have no to_user"""
51+
# Create a new collection with no actions or actions without to_user
52+
collection = ModerationCollection.objects.create(
53+
author=self.user,
54+
name="Collection No Actions",
55+
workflow=self.wf1,
56+
status=constants.IN_REVIEW # Not COLLECTING
57+
)
58+
59+
# Create a moderation request without actions with to_user
60+
from djangocms_versioning.test_utils.factories import PageVersionFactory
61+
version = PageVersionFactory()
62+
mr = collection.moderation_requests.create(
63+
version=version,
64+
language="en",
65+
author=self.user,
66+
is_active=True,
67+
)
68+
69+
# Create action without to_user
70+
mr.actions.create(
71+
by_user=self.user,
72+
action=constants.ACTION_STARTED,
73+
to_user=None, # No specific user assigned
74+
)
75+
76+
reviewers = ModerationCollection.objects.reviewers(collection)
77+
78+
# Should include user from first step's role (role1 -> user)
79+
self.assertIn(self.user, reviewers)
80+
81+
def test_reviewers_does_not_fall_back_when_status_is_collecting(self):
82+
"""Test that reviewers does not fall back to role users when status is COLLECTING"""
83+
# Create collection with COLLECTING status
84+
collection = ModerationCollection.objects.create(
85+
author=self.user,
86+
name="Collection Collecting",
87+
workflow=self.wf1,
88+
status=constants.COLLECTING
89+
)
90+
91+
from djangocms_versioning.test_utils.factories import PageVersionFactory
92+
version = PageVersionFactory()
93+
mr = collection.moderation_requests.create(
94+
version=version,
95+
language="en",
96+
author=self.user,
97+
is_active=True,
98+
)
99+
100+
# Create action without to_user
101+
mr.actions.create(
102+
by_user=self.user,
103+
action=constants.ACTION_STARTED,
104+
to_user=None,
105+
)
106+
107+
reviewers = ModerationCollection.objects.reviewers(collection)
108+
109+
# Should be empty since status is COLLECTING and no actions have to_user
110+
self.assertEqual(len(reviewers), 0)
111+
112+
def test_reviewers_includes_group_users_from_first_step(self):
113+
"""Test that reviewers includes all users from a group role in first step"""
114+
# Create collection with workflow that has group role as first step
115+
collection = ModerationCollection.objects.create(
116+
author=self.user,
117+
name="Collection Group Role",
118+
workflow=self.wf2, # wf2st1 has role1 (user), wf2st2 has role3 (group)
119+
status=constants.IN_REVIEW
120+
)
121+
122+
# Change first step to use group role
123+
self.wf2st1.delete()
124+
125+
from djangocms_versioning.test_utils.factories import PageVersionFactory
126+
version = PageVersionFactory()
127+
mr = collection.moderation_requests.create(
128+
version=version,
129+
language="en",
130+
author=self.user,
131+
is_active=True,
132+
)
133+
134+
# Create action without to_user
135+
mr.actions.create(
136+
by_user=self.user,
137+
action=constants.ACTION_STARTED,
138+
to_user=None,
139+
)
140+
141+
reviewers = ModerationCollection.objects.reviewers(collection)
142+
143+
# Should include users from group (user2 and user3)
144+
self.assertIn(self.user2, reviewers)
145+
self.assertIn(self.user3, reviewers)
146+
self.assertGreaterEqual(len(reviewers), 2)
147+
148+
def test_reviewers_returns_empty_set_when_no_moderation_requests(self):
149+
"""Test that reviewers returns empty set when collection has no moderation requests"""
150+
# Create collection without moderation requests
151+
collection = ModerationCollection.objects.create(
152+
author=self.user,
153+
name="Empty Collection",
154+
workflow=self.wf1,
155+
)
156+
157+
reviewers = ModerationCollection.objects.reviewers(collection)
158+
159+
self.assertEqual(len(reviewers), 0)
160+
self.assertIsInstance(reviewers, set)
161+
162+
def test_reviewers_handles_workflow_without_first_step(self):
163+
"""Test that reviewers handles workflow with no first step gracefully"""
164+
# Create workflow with no steps
165+
workflow = self.wf1
166+
workflow.steps.all().delete()
167+
168+
collection = ModerationCollection.objects.create(
169+
author=self.user,
170+
name="Collection No Steps",
171+
workflow=workflow,
172+
status=constants.IN_REVIEW
173+
)
174+
175+
from djangocms_versioning.test_utils.factories import PageVersionFactory
176+
version = PageVersionFactory()
177+
mr = collection.moderation_requests.create(
178+
version=version,
179+
language="en",
180+
author=self.user,
181+
is_active=True,
182+
)
183+
184+
# Create action without to_user
185+
mr.actions.create(
186+
by_user=self.user,
187+
action=constants.ACTION_STARTED,
188+
to_user=None,
189+
)
190+
191+
# Should not raise an error
192+
reviewers = ModerationCollection.objects.reviewers(collection)
193+
194+
self.assertEqual(len(reviewers), 0)
195+
196+
def test_reviewers_aggregates_from_multiple_moderation_requests(self):
197+
"""Test that reviewers aggregates users from multiple moderation requests in collection"""
198+
# collection3 has two moderation requests with different users
199+
reviewers = ModerationCollection.objects.reviewers(self.collection3)
200+
201+
# Both requests have actions assigned to user2
202+
self.assertIn(self.user2, reviewers)
203+
204+
# moderation_request4 was rejected by user3
205+
# Note: rejected actions don't have to_user in the base setup,
206+
# but let's verify the behavior
207+
self.assertGreaterEqual(len(reviewers), 1)
208+
209+
def test_reviewers_with_prefetch_is_efficient(self):
210+
"""Test that using prefetch_reviewers makes the reviewers method efficient"""
211+
# This test verifies the relationship between prefetch and reviewers method
212+
collection = ModerationCollection.objects.prefetch_reviewers().get(pk=self.collection1.pk)
213+
214+
# With prefetch, this should not trigger additional queries
215+
with self.assertNumQueries(0):
216+
reviewers = ModerationCollection.objects.reviewers(collection)
217+
218+
self.assertIn(self.user2, reviewers)

0 commit comments

Comments
 (0)