workflows: delay cleaning of live workflow form data (#64792)
This commit is contained in:
parent
420e68bd29
commit
e8668e89b6
|
@ -7978,6 +7978,166 @@ def test_frontoffice_workflow_form_with_dynamic_comment(pub):
|
|||
assert live_resp.json['result']['blah_2']['content'] == '<p>value is test</p>'
|
||||
|
||||
|
||||
def test_frontoffice_workflow_form_with_dynamic_list(pub):
|
||||
user = create_user(pub)
|
||||
wf = Workflow('dynamic list in workflow')
|
||||
status = wf.add_status('st1')
|
||||
status2 = wf.add_status('st2')
|
||||
display_form = status.add_action('form', id='_display_form')
|
||||
display_form.by = ['_submitter']
|
||||
display_form.varname = 'blah'
|
||||
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
|
||||
display_form.formdef.fields = [
|
||||
fields.ItemField(id='1', label='Test', varname='foo', type='item', items=['10', '20']),
|
||||
fields.ItemField(
|
||||
id='2',
|
||||
label='Test2',
|
||||
type='item',
|
||||
varname='item2',
|
||||
data_source={'type': 'json', 'value': 'http://example.org/{{form_workflow_form_blah_var_foo}}'},
|
||||
),
|
||||
]
|
||||
jump1 = status.add_action('choice', id='_jump')
|
||||
jump1.label = 'Jump'
|
||||
jump1.by = ['_submitter']
|
||||
jump1.status = status2.id
|
||||
|
||||
wf.store()
|
||||
|
||||
formdef = create_formdef()
|
||||
formdef.workflow_id = wf.id
|
||||
formdef.store()
|
||||
|
||||
formdef.data_class().wipe()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.user_id = user.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
app = login(get_app(pub), username='foo', password='foo')
|
||||
resp = app.get(formdata.get_url(backoffice=False))
|
||||
|
||||
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
|
||||
|
||||
def side_effect(url, *args):
|
||||
print('url:', url)
|
||||
if url == 'http://example.org/10':
|
||||
data = [
|
||||
{'id': '1', 'text': 'hello', 'extra': 'foo'},
|
||||
{'id': '2', 'text': 'world', 'extra': 'bar'},
|
||||
]
|
||||
elif url == 'http://example.org/20':
|
||||
data = [
|
||||
{'id': '11', 'text': 'hello2', 'extra': 'foo'},
|
||||
{'id': '21', 'text': 'world2', 'extra': 'bar'},
|
||||
]
|
||||
else:
|
||||
data = []
|
||||
return io.StringIO(json.dumps({'data': data}))
|
||||
|
||||
urlopen.side_effect = side_effect
|
||||
|
||||
live_url = resp.html.find('form').attrs['data-live-url']
|
||||
live_resp = app.post(live_url + '?modified_field_id=init', params=resp.form.submit_fields())
|
||||
assert [x['id'] for x in live_resp.json['result']['blah_2']['items']] == ['1', '2']
|
||||
resp.form['fblah_1'] = '20'
|
||||
live_resp = app.post(live_url + '?modified_field_id=blah_1', params=resp.form.submit_fields())
|
||||
assert [x['id'] for x in live_resp.json['result']['blah_2']['items']] == ['11', '21']
|
||||
|
||||
resp.form['fblah_2'].force_value('11')
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'Technical error, please try again' not in resp.text
|
||||
formdata.refresh_from_storage()
|
||||
pub.substitutions.feed(formdata)
|
||||
context = pub.substitutions.get_context_variables(mode='lazy')
|
||||
assert context['form_workflow_form_blah_var_item2'] == 'hello2'
|
||||
assert context['form_workflow_data_blah_var_item2'] == 'hello2'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('button_position', ['before', 'after'])
|
||||
def test_frontoffice_workflow_form_and_other_button(pub, button_position):
|
||||
user = create_user(pub)
|
||||
wf = Workflow('form and other button')
|
||||
status = wf.add_status('st1')
|
||||
status2 = wf.add_status('st2')
|
||||
display_form = status.add_action('form', id='_display_form')
|
||||
display_form.by = ['_submitter']
|
||||
display_form.varname = 'blah'
|
||||
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
|
||||
display_form.formdef.fields = [
|
||||
fields.StringField(id='1', label='Test', varname='foo', type='string', required=True),
|
||||
fields.StringField(id='2', label='Test2', varname='foo2', type='string', required=True),
|
||||
]
|
||||
jump1 = status.add_action('choice', id='_jump', prepend=bool(button_position == 'before'))
|
||||
jump1.label = 'Jump'
|
||||
jump1.by = ['_submitter']
|
||||
jump1.status = status2.id
|
||||
|
||||
wf.store()
|
||||
|
||||
formdef = create_formdef()
|
||||
formdef.workflow_id = wf.id
|
||||
formdef.store()
|
||||
|
||||
formdef.data_class().wipe()
|
||||
|
||||
app = login(get_app(pub), username='foo', password='foo')
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.user_id = user.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
resp = app.get(formdata.get_url(backoffice=False))
|
||||
resp.form['fblah_1'] = 'a'
|
||||
resp.form['fblah_2'] = 'b'
|
||||
resp = resp.form.submit('submit')
|
||||
formdata.refresh_from_storage()
|
||||
pub.substitutions.reset()
|
||||
pub.substitutions.feed(formdata)
|
||||
context = pub.substitutions.get_context_variables(mode='lazy')
|
||||
assert 'form_workflow_form_blah_var_foo' in context
|
||||
assert context['form_workflow_form_blah_var_foo'] == 'a'
|
||||
assert context['form_workflow_data_blah_var_foo'] == 'a'
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.user_id = user.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
resp = app.get(formdata.get_url(backoffice=False))
|
||||
resp.form['fblah_1'] = 'a'
|
||||
resp.form['fblah_2'] = 'b'
|
||||
resp = resp.form.submit('button_jump')
|
||||
formdata.refresh_from_storage()
|
||||
pub.substitutions.reset()
|
||||
pub.substitutions.feed(formdata)
|
||||
context = pub.substitutions.get_context_variables(mode='lazy')
|
||||
# check workflow form data is not saved (good)
|
||||
assert 'form_workflow_form_blah_var_foo' not in context
|
||||
# but legacy behaviout it leaks into workflow_data :/
|
||||
assert context['form_workflow_data_blah_var_foo'] == 'a'
|
||||
|
||||
# check it also happens with invalid/partial form
|
||||
jump1.ignore_form_errors = True
|
||||
wf.store()
|
||||
formdata = formdef.data_class()()
|
||||
formdata.user_id = user.id
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
resp = app.get(formdata.get_url(backoffice=False))
|
||||
resp.form['fblah_1'] = 'a'
|
||||
resp.form['fblah_2'] = ''
|
||||
resp = resp.form.submit('button_jump')
|
||||
formdata.refresh_from_storage()
|
||||
pub.substitutions.reset()
|
||||
pub.substitutions.feed(formdata)
|
||||
context = pub.substitutions.get_context_variables(mode='lazy')
|
||||
# check workflow form data is not saved (good)
|
||||
assert 'form_workflow_form_blah_var_foo' not in context
|
||||
# but legacy behaviout it leaks into workflow_data :/
|
||||
assert context['form_workflow_data_blah_var_foo'] == 'a'
|
||||
|
||||
|
||||
def test_frontoffice_workflow_form_with_impossible_condition(pub):
|
||||
user = create_user(pub)
|
||||
wf = Workflow.get_default_workflow()
|
||||
|
|
|
@ -343,6 +343,13 @@ class FormData(StorableObject):
|
|||
if changed:
|
||||
self.store()
|
||||
|
||||
def clean_live_evolution_items(self):
|
||||
for evolution in reversed(self.evolution or []):
|
||||
if getattr(evolution, 'parts', None):
|
||||
# cleanup evolution, remove parts that have only been added for
|
||||
# the live evaluation
|
||||
evolution.parts = [x for x in evolution.parts or [] if not getattr(x, 'live', False)]
|
||||
|
||||
@invalidate_substitution_cache
|
||||
def store(self, *args, **kwargs):
|
||||
# make sure the class set under the formdef name in the sys.modules
|
||||
|
@ -359,6 +366,7 @@ class FormData(StorableObject):
|
|||
has_id = self.id is not None
|
||||
if has_id:
|
||||
self.set_auto_fields()
|
||||
self.clean_live_evolution_items()
|
||||
super().store(*args, **kwargs)
|
||||
if not has_id: # got it now
|
||||
if self.set_auto_fields():
|
||||
|
|
|
@ -368,12 +368,7 @@ class FormStatusPage(Directory, FormTemplateMixin):
|
|||
and 'f%s' % field.id in form._names
|
||||
):
|
||||
del form._names['f%s' % field.id]
|
||||
if self.filled.evolution:
|
||||
# cleanup evolution, remove parts that have only been added for
|
||||
# the live evaluation
|
||||
self.filled.evolution[-1].parts = [
|
||||
x for x in self.filled.evolution[-1].parts or [] if not getattr(x, 'live', False)
|
||||
]
|
||||
|
||||
if form:
|
||||
form.attrs['data-live-url'] = self.filled.get_url() + 'live'
|
||||
return form
|
||||
|
|
|
@ -2571,6 +2571,7 @@ class SqlDataMixin(SqlMixin):
|
|||
self.id = cur.fetchone()[0]
|
||||
|
||||
self._set_auto_fields(cur)
|
||||
self.clean_live_evolution_items()
|
||||
|
||||
if self._evolution:
|
||||
for evo in self._evolution:
|
||||
|
|
Loading…
Reference in New Issue