From 8cc7e1a819081a9e279cbaa0f72749893d181d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Wed, 11 Jul 2018 10:30:25 +0200 Subject: [PATCH] forms: make sure latest live data are used to evaluate conditions (#25197) --- tests/test_form_pages.py | 41 ++++++++++++++++++++++++++++++++++++++++ wcs/fields.py | 16 +++++++++------- wcs/forms/root.py | 6 +++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index 5566035d..68ba22a5 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -993,6 +993,47 @@ def test_form_multi_page_post_conditions(pub): resp = resp.forms[0].submit('submit') assert 'Check values then click submit.' in resp.body +def test_form_multi_page_conditions_and_post_conditions(pub): + formdef = create_formdef() + formdef.fields = [ + fields.PageField(id='0', label='1st page', type='page', + post_conditions=[{ + 'condition': {'type': 'python', 'value': 'form_var_bar == "bar"'}, + 'error_message': 'You shall not pass.'}]), + fields.StringField(id='1', label='string', varname='bar'), + fields.PageField(id='3', label='2nd page', type='page'), + ] + + formdef.store() + formdef.data_class().wipe() + + resp = get_app(pub).get('/test/') + resp.form['f1'] = 'bar' + resp = resp.form.submit('submit') + assert_current_page(resp, '2nd page') + + resp = get_app(pub).get('/test/') + resp.form['f1'] = 'foo' + resp = resp.form.submit('submit') + assert 'You shall not pass.' in resp.body + + # add a conditional page, this will cause pages to be evaluated first + # (and would trigger #25197) + formdef.fields.append( + fields.PageField(id='4', label='3rd page', type='page', + condition={'type': 'python', 'value': 'True'})) + formdef.store() + + resp = get_app(pub).get('/test/') + resp.form['f1'] = 'bar' + resp = resp.form.submit('submit') + assert_current_page(resp, '2nd page') + + resp = get_app(pub).get('/test/') + resp.form['f1'] = 'foo' + resp = resp.form.submit('submit') + assert 'You shall not pass.' in resp.body + def test_form_multi_page_page_name_as_title(pub): formdef = create_formdef() formdef.fields = [fields.PageField(id='0', label='1st page', type='page'), diff --git a/wcs/fields.py b/wcs/fields.py index 156e4be8..52d67148 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -1501,22 +1501,24 @@ class PageCondition(Condition): # they will shadow formdata context variables with their new "live" # value, this may be useful when evaluating data sources. class ConditionVars(object): + def __init__(self, id_dict_var): + # keep track of reference dictionary + self.id_dict_var = id_dict_var + def get_substitution_variables(self): return form_live_data def __eq__(self, other): - # Assume all ConditionVars are equal (as they are because they - # are created using the same live data); this avoids filling + # Assume all ConditionVars are equal when initialized with + # the same live data dictionary; this avoids filling # the substitution sources with duplicates and invalidating its # cache. - try: - return self.__class__.__name__ == other.__class__.__name__ - except AttributeError: - return False + return self.id_dict_var == getattr(other, 'id_dict_var', None) + if dict_vars is not None: # Add them only if there is a real dict_vars in context, # ie do nothing on first page condition - get_publisher().substitutions.feed(ConditionVars()) + get_publisher().substitutions.feed(ConditionVars(id(dict_vars))) data = get_publisher().substitutions.get_context_variables() # 2) add live data as var_ variables for local evaluation only, for diff --git a/wcs/forms/root.py b/wcs/forms/root.py index cc27d678..24af84ab 100644 --- a/wcs/forms/root.py +++ b/wcs/forms/root.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . +import copy import json import time from StringIO import StringIO @@ -718,7 +719,10 @@ class FormPage(Directory, FormTemplateMixin): page_error_messages = [] if form.get_submit() == 'submit' and page: post_conditions = page.post_conditions or [] - form_data = session.get_by_magictoken(magictoken, {}) + # create a new dictionary to hold live data, this makes sure + # a new ConditionsVars will get added to the substitution + # variables. + form_data = copy.copy(session.get_by_magictoken(magictoken, {})) data = self.formdef.get_data(form) form_data.update(data) for i, post_condition in enumerate(post_conditions): -- 2.18.0