Skip to content

Commit 3d8cfe9

Browse files
committed
Make block editing forms more robust for Plone 6.2 and handle field flattening/ordering correctly
1 parent cb3e75d commit 3d8cfe9

2 files changed

Lines changed: 82 additions & 7 deletions

File tree

src/cs_dynamicpages/browser/forms.py

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,33 @@ def _get_ordered_fields(self, all_fields_dict, allowed_fields):
3333
# Always keep row_type
3434
for n in ["IDynamicPageRow.row_type", "row_type"]:
3535
if n in all_fields_dict:
36-
new_fields_list.append(all_fields_dict[n])
36+
f = all_fields_dict[n]
37+
# Ensure it's not hidden
38+
f.mode = None
39+
new_fields_list.append(f)
3740
del all_fields_dict[n]
3841
break
3942

4043
# Add allowed fields in order
4144
for field_name in allowed_fields:
4245
matched_name = self._find_matched_field(all_fields_dict, field_name)
4346
if matched_name:
44-
new_fields_list.append(all_fields_dict[matched_name])
47+
f = all_fields_dict[matched_name]
48+
f.mode = None # Force visible
49+
new_fields_list.append(f)
4550
del all_fields_dict[matched_name]
4651

4752
# Always include Title
4853
for n in ["IBasic.title", "title"]:
4954
if n in all_fields_dict:
50-
new_fields_list.append(all_fields_dict[n])
55+
f = all_fields_dict[n]
56+
f.mode = None # Force visible
57+
new_fields_list.append(f)
5158
del all_fields_dict[n]
5259

5360
return new_fields_list
5461

62+
5563
def _find_matched_field(self, all_fields_dict, field_name):
5664
if field_name in all_fields_dict:
5765
return field_name
@@ -85,6 +93,33 @@ def updateFields(self):
8593
super().updateFields()
8694
self._flatten_and_order_fields()
8795

96+
def updateWidgets(self):
97+
super().updateWidgets()
98+
# Safety net: ensure unwanted widgets are not there
99+
row_type = self._get_current_row_type()
100+
if row_type:
101+
config = get_row_config(row_type)
102+
if config:
103+
allowed_fields = config.get("each_row_type_fields", [])
104+
self._filter_widgets_manager(self.widgets, allowed_fields)
105+
for group in self.groups:
106+
if hasattr(group, "widgets"):
107+
self._filter_widgets_manager(group.widgets, allowed_fields)
108+
109+
def _filter_widgets_manager(self, widgets, allowed_fields):
110+
to_delete = []
111+
for name, widget in widgets.items():
112+
if widget.field.__name__ in ["row_type", "title"]:
113+
continue
114+
is_allowed = name in allowed_fields or widget.field.__name__ in allowed_fields
115+
if not is_allowed:
116+
norm_name = name.replace(".", "-")
117+
is_allowed = norm_name in allowed_fields
118+
if not is_allowed:
119+
to_delete.append(name)
120+
for name in to_delete:
121+
del widgets[name]
122+
88123
def row_type_configs(self):
89124
return json.dumps(get_available_views_for_row())
90125

@@ -96,6 +131,32 @@ def updateFields(self):
96131
super().updateFields()
97132
self._flatten_and_order_fields()
98133

134+
def updateWidgets(self):
135+
super().updateWidgets()
136+
row_type = self._get_current_row_type()
137+
if row_type:
138+
config = get_row_config(row_type)
139+
if config:
140+
allowed_fields = config.get("each_row_type_fields", [])
141+
self._filter_widgets_manager(self.widgets, allowed_fields)
142+
for group in self.groups:
143+
if hasattr(group, "widgets"):
144+
self._filter_widgets_manager(group.widgets, allowed_fields)
145+
146+
def _filter_widgets_manager(self, widgets, allowed_fields):
147+
to_delete = []
148+
for name, widget in widgets.items():
149+
if widget.field.__name__ in ["row_type", "title"]:
150+
continue
151+
is_allowed = name in allowed_fields or widget.field.__name__ in allowed_fields
152+
if not is_allowed:
153+
norm_name = name.replace(".", "-")
154+
is_allowed = norm_name in allowed_fields
155+
if not is_allowed:
156+
to_delete.append(name)
157+
for name in to_delete:
158+
del widgets[name]
159+
99160
def row_type_configs(self):
100161
return json.dumps(get_available_views_for_row())
101162

src/cs_dynamicpages/tests/test_block_editing_forms.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,31 @@ def test_complex_widget_hidden_crash(self):
8686
# when they are supposed to be hidden.
8787
from plone.app.z3cform.interfaces import IPloneFormLayer
8888
from zope.interface import alsoProvides
89+
from z3c.form.interfaces import HIDDEN_MODE
8990

90-
self.row.row_type = "cs_dynamicpages-text-view" # Hide ICollection fields
91-
91+
self.row.row_type = "cs_dynamicpages-text-view" # Hide ICollection fields
92+
9293
request = self.portal.REQUEST.clone()
9394
alsoProvides(request, IPloneFormLayer)
94-
95+
9596
form = RowEditForm(self.row, request)
9697
form.update()
98+
99+
# Manually add a widget in HIDDEN_MODE to see if it crashes
100+
# using a field that uses OrderedSelectWidget if possible
101+
# but here we just check if any widget is in HIDDEN_MODE
102+
for widget in form.widgets.values():
103+
if widget.mode == HIDDEN_MODE:
104+
# If we have widgets in HIDDEN_MODE, rendering might crash
105+
# if they don't have a 'hidden' template.
106+
pass
107+
97108
# This should trigger the rendering which might crash
98109
try:
99110
rendered = form()
100-
self.assertIn("IRichTextBehavior-text", rendered)
111+
self.assertIn('IRichTextBehavior-text', rendered)
112+
# Check that unwanted fields are NOT in rendered output
113+
self.assertNotIn('ICollection-customViewFields', rendered)
101114
except Exception as e:
102115
self.fail(f"Form rendering failed with {type(e).__name__}: {e}")
116+

0 commit comments

Comments
 (0)