forms: allow dynamic prefill of block sub fields (#51688)

This commit is contained in:
Frédéric Péters 2021-03-05 10:23:56 +01:00
parent 6565a67a91
commit c6a9bfb535
4 changed files with 96 additions and 23 deletions

View File

@ -43,6 +43,7 @@ from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
from wcs.wf.resubmit import ResubmitWorkflowStatusItem
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
from wcs.blocks import BlockDef
from wcs.categories import Category
from wcs.roles import Role, logged_users_role
from wcs.tracking_code import TrackingCode
@ -7474,6 +7475,47 @@ def test_field_live_string_prefill(pub, http_requests):
live_resp = app.post('/foo/live?modified_field_id=user', params=resp.form.submit_fields(), status=403)
def test_field_live_block_string_prefill(pub, http_requests):
FormDef.wipe()
BlockDef.wipe()
block = BlockDef()
block.name = 'foobar'
block.fields = [
fields.StringField(
id='123',
required=True,
label='Test',
type='string',
prefill={'type': 'string', 'value': '{{form_var_foo|default:""}}'},
),
]
block.store()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [
fields.StringField(id='1', label='test', type='string', varname='foo'),
fields.BlockField(id='2', label='test', type='block:foobar'),
]
formdef.store()
formdef.data_class().wipe()
app = get_app(pub)
resp = app.get(formdef.get_url())
live_url = resp.html.find('form').attrs['data-live-url']
assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
assert resp.pyquery('[data-field-id="123"].widget-prefilled') # block/string
assert resp.form['f2$element0$f123'].value == ''
resp.form['f1'] = 'hello'
live_resp = app.post(live_url + '?modified_field_id=1&prefilled_123=on', params=resp.form.submit_fields())
assert live_resp.json['result'] == {
'1': {'visible': True},
'2': {'visible': True},
'2-123': {'block_id': '2', 'content': 'hello', 'field_id': '123', 'visible': True},
}
def test_form_edit_and_backoffice_field_change(pub):
create_user(pub)

View File

@ -725,7 +725,15 @@ class FormDef(StorableObject):
def set_live_condition_sources(self, form, fields):
live_condition_fields = {}
for field in fields:
def get_all_fields():
for field in fields:
yield field
if field.key == 'block':
for subfield in field.block.fields:
yield subfield
for field in get_all_fields():
if field.condition:
field.varnames = field.get_condition_varnames(formdef=self)
for varname in field.varnames:

View File

@ -23,6 +23,7 @@ from quixote.html import TemplateIO, htmltext
from quixote.util import randbytes
from wcs import data_sources
from wcs.blocks import BlockWidget, BlockSubWidget
from wcs.api_utils import get_user_from_api_query_string, is_url_signed, sign_url_auto_orig
from wcs.fields import WidgetField, FileField
from wcs.workflows import EditableWorkflowStatusItem
@ -738,21 +739,38 @@ class FormStatusPage(Directory, FormTemplateMixin):
result[field.id]['items'] = [
{'id': x[2], 'text': x[1]} for x in field.get_options(mode='lazy')
]
for widget in form.widgets:
if not getattr(widget, 'field', None):
continue
if widget.field.key == 'comment':
result[widget.field.id]['content'] = widget.content
elif widget.field.prefill and widget.field.prefill.get('type') == 'string':
update_prefill = bool('prefilled_%s' % widget.field.id in get_request().form)
def get_all_field_widgets(form):
for widget in form.widgets:
if not getattr(widget, 'field', None):
continue
yield (None, widget.field, widget)
if isinstance(widget, BlockWidget):
for subwidget in widget.widgets:
if isinstance(subwidget, BlockSubWidget):
for field_widget in subwidget.widgets:
yield (widget.field, field_widget.field, field_widget)
for block, field, widget in get_all_field_widgets(form):
if block:
entry = {'visible': True}
result['%s-%s' % (block.id, field.id)] = entry
entry['block_id'] = block.id
entry['field_id'] = field.id
else:
entry = result[field.id]
if field.key == 'comment':
entry['content'] = widget.content
elif field.prefill and field.prefill.get('type') == 'string':
update_prefill = bool('prefilled_%s' % 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':
value, locked = field.get_prefill_value()
entry['content'] = value
elif field.prefill and 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
value, locked = field.get_prefill_value(user=formdata.user)
entry['content'] = value
return json.dumps({'result': result})

View File

@ -304,7 +304,11 @@ $(function() {
headers: {'accept': 'application/json'},
success: function(json) {
$.each(json.result, function(key, value) {
var $widget = $('[data-field-id="' + key + '"]');
if (value.block_id) {
var $widget = $('[data-field-id="' + value.block_id + '"] [data-field-id="' + value.field_id + '"]');
} else {
var $widget = $('[data-field-id="' + key + '"]');
}
if (value.visible) {
var was_visible = $widget.is(':visible');
$widget.css('display', '');
@ -353,16 +357,17 @@ $(function() {
$select.trigger('wcs:options-change', {items: value.items});
}
if (value.content) {
var $widget = $('[data-field-id="' + key + '"]');
if ($widget.hasClass('comment-field')) {
// replace comment content
$widget.html(value.content);
} else {
// replace text input value
if ($widget.is('.widget-prefilled') || $widget.is('.widget-readonly') || data.modified_field == 'user') {
$widget.find('input, textarea').val(value.content);
$widget.each(function(idx, widget) {
if ($widget.hasClass('comment-field')) {
// replace comment content
$widget.html(value.content);
} else {
// replace text input value
if ($(widget).is('.widget-prefilled') || $(widget).is('.widget-readonly') || data.modified_field == 'user') {
$(widget).find('input, textarea').val(value.content);
}
}
}
});
}
if (value.source_url) {
// json change of URL