forms: recompute prefills on live changes (#51330)

This commit is contained in:
Frédéric Péters 2021-02-22 15:50:20 +01:00
parent 36febae164
commit b091599f36
7 changed files with 114 additions and 18 deletions

View File

@ -1569,10 +1569,6 @@ def test_backoffice_submission_user_selection_then_live(pub):
assert resp.form['f3'].value == random_user.email
assert resp.form['f4'].value == random_user.email
live_url = resp.html.find('form').attrs['data-live-url']
live_resp = app.post(live_url, params=resp.form.submit_fields())
assert live_resp.json['result']['4']['content'] == random_user.email
resp.form['f4'] = 'altered value' # alter value
resp = resp.form.submit('submit') # -> validation page
@ -1580,6 +1576,43 @@ def test_backoffice_submission_user_selection_then_live(pub):
assert 'altered value' not in resp
def test_backoffice_submission_user_selection_then_live_prefill(pub):
user = create_user(pub)
for i in range(10):
random_user = pub.user_class()
random_user.name = 'random user %s' % i
random_user.email = 'test%s@invalid' % i
random_user.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.PageField(id='0', label='1st page', type='page'),
fields.StringField(
id='3',
label='Field on 2nd page',
type='string',
varname='plop',
prefill={'type': 'user', 'value': 'email'},
),
]
formdef.backoffice_submission_roles = user.roles[:]
formdef.workflow_roles = {'_receiver': 1}
formdef.store()
app = login(get_app(pub))
url = '/backoffice/submission/%s/' % formdef.url_name
resp = app.get(url)
live_url = resp.html.find('form').attrs['data-live-url']
assert resp.pyquery('.submit-user-selection')
resp.form['user_id'] = str(random_user.id) # happens via javascript
live_resp = app.post(live_url + '?modified_field_id=user', params=resp.form.submit_fields())
assert live_resp.json == {'result': {'3': {'visible': True, 'content': 'test9@invalid'}}}
def test_backoffice_submission_sidebar_lateral_block(pub):
user = create_user(pub)

View File

@ -2936,7 +2936,8 @@ def test_form_page_template_prefill(pub):
resp = app.get('/test/')
assert resp.form['f0'].value == ''
assert 'widget-prefilled' not in resp.text
# still marked with a css class, in case of live changes.
assert 'widget-prefilled' in resp.text
def test_form_page_session_var_prefill(pub):
@ -7436,6 +7437,43 @@ def test_field_live_comment_content_from_structured_item_data(pub, http_requests
assert live_resp.json['result']['7']['content'] == '<p>bla bar bla</p>'
def test_field_live_string_prefill(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'Foo'
formdef.fields = [
fields.StringField(type='string', id='1', label='Bar', size='40', required=True, varname='bar'),
fields.StringField(
type='string',
id='2',
label='Bar2',
size='40',
required=True,
varname='bar2',
prefill={'type': 'string', 'value': '{{form_var_bar|default:""}}'},
),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get('/foo/')
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('#var_bar2.widget-prefilled') # second field is marked as prefilled
assert resp.form['f2'].value == ''
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True, 'content': 'hello'}
resp.form['f2'] = 'other' # manually changed -> widget-prefilled class will be removed
resp.form['f1'] = 'xxx'
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
assert live_resp.json['result']['2'] == {'visible': True}
# check it's not possible to declare user change from frontoffice
live_resp = app.post('/foo/live?modified_field_id=user', params=resp.form.submit_fields(), status=403)
def test_form_edit_and_backoffice_field_change(pub):
create_user(pub)
@ -8394,10 +8432,10 @@ def test_field_live_locked_prefilled_field(pub, http_requests):
assert 'f1' in resp.form.fields
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
resp.form['f1'] = 'hello'
live_resp = app.post('/foo/live', params=resp.form.submit_fields())
live_resp = app.post('/foo/live?prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] == 'bla hello bla'
resp.form['f1'] = 'toto'
live_resp = app.post('/foo/live?modified_field_id=1', params=resp.form.submit_fields())
live_resp = app.post('/foo/live?modified_field_id=1&prefilled_2=on', params=resp.form.submit_fields())
assert live_resp.json['result']['2']['content'] == 'bla toto bla'

View File

@ -741,7 +741,7 @@ class FormDef(StorableObject):
if not varname in live_condition_fields:
live_condition_fields[varname] = []
live_condition_fields[varname].append(field)
if field.prefill and field.prefill.get('locked') and field.prefill.get('type') == 'string':
if field.prefill and field.prefill.get('type') == 'string':
for varname in field.get_referenced_varnames(
formdef=self, value=field.prefill.get('value', '')
):

View File

@ -706,6 +706,12 @@ class FormStatusPage(Directory, FormTemplateMixin):
for field in displayed_fields:
if field.key == 'item' and field.display_mode == 'list' and field.varname:
modified_field_varnames.add(field.varname)
elif get_request().form.get('modified_field_id') == 'user' and get_request().is_in_frontoffice():
# not allowed in frontoffice.
raise errors.AccessForbiddenError()
elif get_request().form.get('modified_field_id') == 'user':
# user selection in sidebar
formdata.user_id = get_request().form.get('user_id')
elif get_request().form.get('modified_field_id'):
for field in displayed_fields:
if field.id == get_request().form.get('modified_field_id'):
@ -737,13 +743,16 @@ class FormStatusPage(Directory, FormTemplateMixin):
continue
if widget.field.key == 'comment':
result[widget.field.id]['content'] = widget.content
elif (
widget.field.prefill
and widget.field.prefill.get('locked')
and widget.field.prefill.get('type') == 'string'
):
value, locked = widget.field.get_prefill_value()
result[widget.field.id]['content'] = value
elif widget.field.prefill and widget.field.prefill.get('type') == 'string':
update_prefill = bool('prefilled_%s' % widget.field.id in get_request().form)
if update_prefill:
value, locked = widget.field.get_prefill_value()
result[widget.field.id]['content'] = value
elif widget.field.prefill and widget.field.prefill.get('type') == 'user':
update_prefill = bool(get_request().form.get('modified_field_id') == 'user')
if update_prefill:
value, locked = widget.field.get_prefill_value(user=formdata.user)
result[widget.field.id]['content'] = value
return json.dumps({'result': result})

View File

@ -416,8 +416,12 @@ class FormPage(Directory, FormTemplateMixin):
# turn off prefilling from geolocation attributes if
# the form is filled from the backoffice
v = None
if v:
prefilled = True
else:
if v:
prefilled = True
# always mark widget as prefilled, even for empty content,
# this will add a widget-prefilled CSS class that will be
# used for live prefill changes.
widget.prefilled = True
if not prefilled and widget:

View File

@ -163,6 +163,10 @@ $(function() {
$('div.submit-user-selection select').select2(select2_options);
$('div.submit-user-selection').show().find('select').on('change', function() {
$('input[type=hidden][name=user_id]').val($(this).val());
$('form[data-live-url]').trigger(
'wcs:change',
{modified_field: 'user', selected_user_id: $(this).val()}
);
});
}

View File

@ -291,6 +291,9 @@ $(function() {
if (data && data.modified_field) {
new_data += '&modified_field_id=' + data.modified_field;
}
$('.widget-prefilled').each(function(idx, elem) {
new_data += '&prefilled_' + $(elem).data('field-id') + '=true';
});
var live_url = $(this).data('live-url');
live_evaluation = $.ajax({
type: 'POST',
@ -355,7 +358,9 @@ $(function() {
$widget.html(value.content);
} else {
// replace text input value
$widget.find('input, textarea').val(value.content);
if ($widget.is('.widget-prefilled') || $widget.is('.widget-readonly') || data.modified_field == 'user') {
$widget.find('input, textarea').val(value.content);
}
}
}
if (value.source_url) {
@ -375,6 +380,9 @@ $(function() {
});
}
$('form div[data-live-source]').parents('form').trigger('wcs:change', {modified_field: 'init'});
$('div.widget-prefilled').on('change input paste', function(ev) {
$(this).removeClass('widget-prefilled');
});
/* searchable select */
$('select[data-autocomplete]').each(function(i, elem) {