From b0c9dbf30759e30a83c4e195a3a91ee52b7dd8ea Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Wed, 24 Apr 2019 12:18:54 +0200
Subject: [PATCH] unfeed: clean live substitutions vars before executing
 workflow (#32558)

---
 tests/test_form_pages.py | 161 +++++++++++++++++++++++++++++++++++++++
 wcs/forms/root.py        |   3 +
 2 files changed, 164 insertions(+)

diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index be18ae49..b7b10088 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -6158,3 +6158,164 @@ def test_field_live_condition_unknown_page_id(pub):
     params = resp.form.submit_fields()
     params = [(key, value if key != 'page_id' else 'eiuiu') for key, value in params]
     app.post('/foo/live', params=params)
+
+def test_unfeed_structured_list_when_condition_changes(pub):
+    """
+    simulate selection of a structured list via condition on from,
+    followed by an evaluation on workflow in order to get structured value
+    from the selected list.
+    ie: test unfeed on ConditionVars
+    """
+    Workflow.wipe()
+    workflow = Workflow(name='test')
+    workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
+    workflow.backoffice_fields_formdef.fields = [
+        fields.StringField(
+            id='bo1', label='first text', type='string', varname='both_text'),
+        fields.StringField(
+            id='bo2', label='first more', type='string', varname='both_more')
+    ]
+
+    st1 = workflow.add_status('Status1', 'st1')
+    setbo = SetBackofficeFieldsWorkflowStatusItem()
+    setbo.parent = st1
+    setbo.fields = [
+        {'field_id': 'bo1',
+         'value': '{{ form_var_listA }} vs {{ form_var_listB }}'},
+        {'field_id': 'bo2',
+         'value': '{{ form_var_listA_more }} vs {{ form_var_listB_more }}'}
+         ]
+    st1.items.append(setbo)
+    workflow.store()
+
+    items_A = [{'id': '1', 'text': 'A1', 'more':'moreA1'}]
+    items_B = [{'id': '1', 'text': 'B1', 'more':'moreB1'},
+               {'id': '2', 'text': 'B2', 'more':'moreB2'}]
+    formdef = create_formdef()
+    formdef.fields = [
+        fields.ItemField(id='1', varname='choice', items=['A', 'B'],
+                         type='item', label='list to choice'),
+        fields.ItemField(id='2', varname='listA', type='item', label='list A',
+                         data_source={'type': 'formula', 'value': str(items_A)},
+                         condition={'type': 'python',
+                                    'value': 'form_var_choice_raw == "A"'}),
+        fields.ItemField(id='3', varname='listB', type='item', label='list B',
+                         data_source={'type': 'formula', 'value': str(items_B)},
+                         condition={'type': 'python',
+                                    'value': 'form_var_choice_raw == "B"'})
+        ]
+    formdef.confirmation = False
+    formdef.workflow_id = workflow.id
+    formdef.store()
+    formdef.data_class().wipe()
+
+    create_user_and_admin(pub)
+    resp = get_app(pub).get('/test/')
+
+    resp.form['f1'].value = 'B'
+    resp.form['f2'].value = '1'
+    resp.form['f3'].value = '2'
+
+    resp = resp.form.submit('submit').follow()
+    assert 'The form has been recorded' in resp.body
+
+    assert formdef.data_class().count() == 1
+    formdata = formdef.data_class().select()[0]
+    assert formdata.data['1'] == 'B'
+    assert formdata.data.get('2') is None
+    assert formdata.data['3'] == '2'
+
+    assert formdata.data['bo1'] == 'None vs B2'
+    assert formdata.data['bo2'] == ' vs moreB2'
+
+def test_form_edit_and_backoffice_field_condition_change(pub):
+    """
+    test unfeed on ConditionVars within edit context
+    """
+    Workflow.wipe()
+    workflow = Workflow(name='test')
+    workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
+    workflow.backoffice_fields_formdef.fields = [
+        fields.StringField(
+            id='bo1', label='both text', type='string', varname='both_text'),
+        fields.StringField(
+            id='bo2', label='both more', type='string', varname='both_more')
+    ]
+
+    st1 = workflow.add_status('Status1', 'st1')
+    setbo = SetBackofficeFieldsWorkflowStatusItem()
+    setbo.parent = st1
+    setbo.fields = [
+        {'field_id': 'bo1',
+         'value': '{{ form_var_listA }} vs {{ form_var_listB }}'},
+        {'field_id': 'bo2',
+         'value': '{{ form_var_listA_more }} vs {{ form_var_listB_more }}'}
+         ]
+    editable = EditableWorkflowStatusItem()
+    editable.id = '_editable'
+    editable.by = ['_submitter']
+    editable.parent = st1
+    editable.status = st1.id
+    st1.items = [setbo, editable]
+    workflow.store()
+
+    items_A = [{'id': '1', 'text': 'A1', 'more':'moreA1'}]
+    items_B = [{'id': '1', 'text': 'B1', 'more':'moreB1'},
+               {'id': '2', 'text': 'B2', 'more':'moreB2'}]
+    formdef = create_formdef()
+    formdef.fields = [
+        fields.ItemField(id='1', varname='choice', items=['A', 'B'],
+                         type='item', label='list to choice'),
+        fields.ItemField(id='2', varname='listA', type='item', label='list A',
+                         data_source={'type': 'formula', 'value': str(items_A)},
+                         condition={'type': 'python',
+                                    'value': 'form_var_choice_raw == "A"'}),
+        fields.ItemField(id='3', varname='listB', type='item', label='list B',
+                         data_source={'type': 'formula', 'value': str(items_B)},
+                         condition={'type': 'python',
+                                    'value': 'form_var_choice_raw == "B"'})
+        ]
+    formdef.confirmation = False
+    formdef.workflow_id = workflow.id
+    formdef.store()
+    formdef.data_class().wipe()
+
+    create_user(pub)
+    resp = login(get_app(pub), username='foo', password='foo').get('/test/')
+
+    resp.form['f1'].value = 'B'
+    resp.form['f2'].value = '1'
+    resp.form['f3'].value = '2'
+
+    resp = resp.form.submit('submit').follow()
+    assert 'The form has been recorded' in resp.body
+
+    assert formdef.data_class().count() == 1
+    formdata = formdef.data_class().select()[0]
+    assert formdata.data['1'] == 'B'
+    assert formdata.data.get('2') is None
+    assert formdata.data['3'] == '2'
+
+    # check unfeed on FormPage::submitted()
+    assert formdata.data['bo1'] == 'None vs B2'
+    assert formdata.data['bo2'] == ' vs moreB2'
+
+    app = login(get_app(pub), username='foo', password='foo')
+    resp = app.get('/test/%s/' % formdata.id)
+    assert 'button_editable-button' in resp.body
+
+    resp = resp.form.submit('button_editable').follow()
+    assert resp.form['f1'].value == 'B'
+    resp.form['f1'].value = 'A'
+    resp = resp.form.submit('submit').follow()  # -> saved
+    assert 'The form has been recorded' in resp.body
+
+    assert formdef.data_class().count() == 1
+    formdata = formdef.data_class().select()[0]
+    assert formdata.data['1'] == 'A'
+    assert formdata.data['2'] == '1'
+    assert formdata.data.get('3') is None
+
+    # check unfeed on FormPage::submitted_existing()
+    assert formdata.data['bo1'] == 'A1 vs None'
+    assert formdata.data['bo2'] == 'moreA1 vs '
diff --git a/wcs/forms/root.py b/wcs/forms/root.py
index 313a94d7..4fc6273c 100644
--- a/wcs/forms/root.py
+++ b/wcs/forms/root.py
@@ -1148,6 +1148,8 @@ class FormPage(Directory, FormTemplateMixin):
         get_logger().info('form %s - done (id: %s)' % (self.formdef.name, filled.id))
         url = None
         if existing_formdata is None:
+            get_publisher().substitutions.unfeed(lambda x: x.__class__.__name__ == 'ConditionVars')
+            get_publisher().substitutions.unfeed(lambda x: isinstance(x, FormData))
             url = filled.perform_workflow()
 
         if not filled.user_id:
@@ -1181,6 +1183,7 @@ class FormPage(Directory, FormTemplateMixin):
         self.edited_data.data = new_data
         self.edited_data.store()
         # remove previous formdata from substitution variables
+        get_publisher().substitutions.unfeed(lambda x: x.__class__.__name__ == 'ConditionVars')
         get_publisher().substitutions.unfeed(lambda x: isinstance(x, FormData))
         # and add new one
         get_publisher().substitutions.feed(self.edited_data)
-- 
2.20.1

