workflows: add options to limit pages displayed on created formdata (#86411)

This commit is contained in:
Frédéric Péters 2024-02-17 17:52:16 +01:00
parent f5422ddef0
commit 48593b4e86
3 changed files with 162 additions and 9 deletions

View File

@ -1093,3 +1093,73 @@ def test_create_formdata_multiple(pub):
resp = app.get(formdata2.get_url())
resp = resp.form.submit('button-action-1')
assert target_formdef.data_class().count() == 3
@pytest.mark.parametrize('mode', ['single', 'partial'])
def test_create_formdata_edit_single_or_partial_pages(pub, mode):
FormDef.wipe()
pub.tracking_code_class.wipe()
target_formdef = FormDef()
target_formdef.name = 'target-form'
target_formdef.fields = [
fields.PageField(id='1', label='page1'),
fields.StringField(id='2', label='string', varname='foo_string'),
fields.PageField(id='3', label='page2', varname='page2'),
fields.StringField(id='4', label='string2', varname='bar_string'),
fields.PageField(id='4', label='page3'),
]
target_formdef.store()
wf = Workflow(name='create-formdata')
wf.possible_status = Workflow.get_default_workflow().possible_status[:]
create = wf.possible_status[1].add_action('create_formdata', id='_create', prepend=True)
create.label = 'create a new linked form'
create.varname = 'resubmitted'
create.draft = True
create.formdef_slug = target_formdef.url_name
create.attach_to_history = True
create.draft_edit_operation_mode = mode
create.page_identifier = 'page2'
create.mappings = [
Mapping(field_id='2', expression='blah1'),
Mapping(field_id='4', expression='blah2'),
]
wf.store()
source_formdef = FormDef()
source_formdef.name = 'source-form'
source_formdef.fields = []
source_formdef.workflow_id = wf.id
source_formdef.enable_tracking_codes = True
source_formdef.store()
source_formdef.data_class().wipe()
target_formdef.data_class().wipe()
create_user(pub)
app = login(get_app(pub), username='foo', password='foo')
resp = app.get('/source-form/')
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
created_url = resp.pyquery('.wf-links a')[0].attrib['href']
resp = app.get(created_url).follow()
if mode == 'single':
assert resp.pyquery('.wcs-step').length == 2
else:
assert resp.pyquery('.wcs-step').length == 3
assert resp.pyquery('.wcs-step.current .label').text() == 'page2 (current step)'
assert resp.forms[1]['f4'].value == 'blah2'
if mode == 'partial':
resp = resp.forms[1].submit('submit') # -> page 3
assert resp.pyquery('.wcs-step.current .label').text() == 'page3 (current step)'
resp = resp.forms[1].submit('submit') # -> validation
resp = resp.forms[1].submit('submit') # -> submit
assert target_formdef.data_class().count() == 1
formdata = target_formdef.data_class().select()[0]
assert formdata.data == {'2': 'blah1', '4': 'blah2'}

View File

@ -946,9 +946,13 @@ class FormPage(Directory, TempfileDirectoryMixin, FormTemplateMixin):
# create a fake FormData with current submission data
formdata.user = get_request().user
formdata._formdef = self.formdef
if draft_formdata and draft_formdata.submission_context:
# restore submission context, this is required to get access to form_parent_* variables
formdata.submission_context = draft_formdata.submission_context
if draft_formdata:
if draft_formdata.submission_context:
# restore submission context, this is required to get access to form_parent_* variables
formdata.submission_context = draft_formdata.submission_context
if draft_formdata.workflow_data:
# restore workflow_data, this is used for partial edit
formdata.workflow_data = draft_formdata.workflow_data
formdata.data = session_data
formdata.prefilling_data = formdata.data.get('prefilling_data', {})
computed_values = get_session().get_by_magictoken('%s-computed' % magictoken) or {}
@ -1074,7 +1078,8 @@ class FormPage(Directory, TempfileDirectoryMixin, FormTemplateMixin):
def pages(self):
if self._pages:
return self._pages
current_data = self.get_transient_formdata().data
transient_formdata = self.get_transient_formdata()
current_data = transient_formdata.data
pages = [x for x in self.formdef.fields if x.key == 'page']
has_page_fields = bool(pages)
@ -1086,12 +1091,27 @@ class FormPage(Directory, TempfileDirectoryMixin, FormTemplateMixin):
# submitted data) (#27247).
hidden_pages = [x for x in pages if not x.is_visible(current_data, self.formdef)]
if self.edit_mode and self.edit_action and self.edit_action.operation_mode in ('single', 'partial'):
if self.edit_mode and self.edit_action:
operation_mode = self.edit_action.operation_mode
page_identifier = self.edit_action.page_identifier
elif (
not self.edit_mode
and transient_formdata.workflow_data
and '_create_formdata_draft_edit' in transient_formdata.workflow_data
):
operation_mode = transient_formdata.workflow_data['_create_formdata_draft_edit']['operation_mode']
page_identifier = transient_formdata.workflow_data['_create_formdata_draft_edit'][
'page_identifier'
]
else:
operation_mode = 'full'
if operation_mode in ('single', 'partial'):
edit_pages = []
for page in pages:
if self.edit_action.page_identifier == page.varname or edit_pages:
if page_identifier == page.varname or edit_pages:
edit_pages.append(page)
if self.edit_action.operation_mode == 'single':
if operation_mode == 'single':
break
edit_pages = [x for x in edit_pages if x not in hidden_pages]
if not edit_pages:

View File

@ -387,6 +387,8 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
map_fields_by_varname = False
attach_to_history = False
cached_field_labels = None
draft_edit_operation_mode = 'full' # or 'single' or 'partial'
page_identifier = None
def migrate(self):
changed = super().migrate()
@ -412,6 +414,13 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
parameters = super().get_inspect_parameters()
if self.user_association_mode != 'custom' and 'user_association_template' in parameters:
parameters.remove('user_association_template')
if not self.draft:
if 'draft_edit_operation_mode' in parameters:
parameters.remove('draft_edit_operation_mode')
if 'page_identifier' in parameters:
parameters.remove('page_identifier')
if self.draft_edit_operation_mode not in ('single', 'partial') and 'page_identifier' in parameters:
parameters.remove('page_identifier')
return parameters
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
@ -443,7 +452,46 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
**{'data-autocomplete': 'true'},
)
if 'draft' in parameters:
form.add(CheckboxWidget, '%sdraft' % prefix, title=_('Create new draft'), value=self.draft)
form.add(
CheckboxWidget,
'%sdraft' % prefix,
title=_('Create new draft'),
value=self.draft,
attrs={'data-dynamic-display-parent': 'true'},
tab=('draft', _('Draft')),
)
if 'draft_edit_operation_mode' in parameters:
form.add(
RadiobuttonsWidget,
'%sdraft_edit_operation_mode' % prefix,
title=_('Operation mode when a draft is created'),
options=[
('full', _('All pages'), 'full'),
('single', _('Single page'), 'single'),
('partial', _('From specific page'), 'partial'),
],
tab=('draft', _('Draft')),
value=self.draft_edit_operation_mode,
attrs={
'data-dynamic-display-parent': 'true',
'data-dynamic-display-child-of': f'{prefix}draft',
'data-dynamic-display-checked': 'true',
},
extra_css_class='widget-inline-radio',
default_value=self.__class__.draft_edit_operation_mode,
)
if 'page_identifier' in parameters:
form.add(
StringWidget,
'%spage_identifier' % prefix,
title=_('Page Identifier'),
value=self.page_identifier,
tab=('draft', _('Draft')),
attrs={
'data-dynamic-display-child-of': '%sdraft_edit_operation_mode' % prefix,
'data-dynamic-display-value-in': 'single|partial',
},
)
if 'backoffice_submission' in parameters:
form.add(
CheckboxWidget,
@ -566,6 +614,12 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
mapped_subfield_id = f'{field.id}${subfield.id}'
if mapped_subfield_id in mapped_field_ids:
self.cached_field_labels[mapped_subfield_id] = f'{field.label} - {subfield.label}'
if not self.draft:
# cleanup
if 'draft_edit_operation_mode' in self.get_parameters():
delattr(self, 'draft_edit_operation_mode')
if 'page_identifier' in self.get_parameters():
delattr(self, 'page_identifier')
def get_mappings_parameter_view_value(self):
to_id_fields = {str(field.id): field for field in self.formdef.get_widget_fields()}
@ -595,11 +649,13 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
def get_parameters(self):
return (
'action_label',
'draft',
'formdef_slug',
'map_fields_by_varname',
'mappings',
'backoffice_submission',
'draft',
'draft_edit_operation_mode',
'page_identifier',
'user_association_mode',
'user_association_template',
'keep_submission_context',
@ -737,6 +793,13 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
if self.draft:
new_formdata.status = 'draft'
new_formdata.receipt_time = localtime()
if self.draft_edit_operation_mode != 'full':
new_formdata.workflow_data = {
'_create_formdata_draft_edit': {
'operation_mode': self.draft_edit_operation_mode,
'page_identifier': self.page_identifier,
},
}
new_formdata.store()
if formdef.enable_tracking_codes:
code.formdata = new_formdata # this will .store() the code