Projet

Général

Profil

0004-add-live-field-conditions-436.patch

Frédéric Péters, 08 août 2018 10:07

Télécharger (27,6 ko)

Voir les différences:

Subject: [PATCH 4/4] add live field conditions (#436)

 tests/test_admin_pages.py                     |   2 +-
 tests/test_backoffice_pages.py                |  12 +-
 tests/test_fields.py                          |  22 ++--
 tests/test_form_pages.py                      | 112 +++++++++++++++++-
 wcs/backoffice/submission.py                  |   2 +-
 wcs/fields.py                                 |  63 +++++++---
 wcs/formdef.py                                |  11 +-
 wcs/forms/root.py                             |  64 +++++++++-
 wcs/qommon/form.py                            |   7 +-
 wcs/qommon/static/js/qommon.forms.js          |  24 ++++
 wcs/qommon/templates/qommon/forms/widget.html |   6 +-
 11 files changed, 272 insertions(+), 53 deletions(-)
tests/test_admin_pages.py
984 984

  
985 985
    # check it's in the preview
986 986
    resp = app.get('/backoffice/forms/1/')
987
    assert '<h3>baz</h3>' in resp.body
987
    assert '<h3 data-field-id="1">baz</h3>' in resp.body
988 988

  
989 989
def test_form_delete_field(pub):
990 990
    create_role()
tests/test_backoffice_pages.py
4120 4120
    app = login(get_app(pub))
4121 4121

  
4122 4122
    resp = app.get('/backoffice/submission/test/')
4123
    assert '<p class="comment-field ">XbarY</p>' in resp.body
4123
    assert '<p data-field-id="7" class="comment-field ">XbarY</p>' in resp.body
4124 4124

  
4125 4125
    # check with publisher variable in named webservice call
4126 4126
    if not pub.site_options.has_section('variables'):
......
4135 4135
    wscall.store()
4136 4136

  
4137 4137
    resp = app.get('/backoffice/submission/test/')
4138
    assert '<p class="comment-field ">XbarY</p>' in resp.body
4138
    assert '<p data-field-id="7" class="comment-field ">XbarY</p>' in resp.body
4139 4139

  
4140 4140
    # django-templated URL
4141 4141
    wscall.request = {'url': '{{ example_url }}json'}
4142 4142
    wscall.store()
4143 4143
    resp = app.get('/backoffice/submission/test/')
4144
    assert '<p class="comment-field ">XbarY</p>' in resp.body
4144
    assert '<p data-field-id="7" class="comment-field ">XbarY</p>' in resp.body
4145 4145

  
4146 4146
    # webservice call in django template
4147 4147
    formdef.fields = [
......
4150 4150
    formdef.store()
4151 4151
    formdef.data_class().wipe()
4152 4152
    resp = app.get('/backoffice/submission/test/')
4153
    assert '<p class="comment-field ">dja-bar-ngo</p>' in resp.body
4153
    assert '<p data-field-id="7" class="comment-field ">dja-bar-ngo</p>' in resp.body
4154 4154

  
4155 4155
def test_backoffice_session_var(pub):
4156 4156
    open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w').write('''[options]
......
4174 4174
    resp = app.get('/backoffice/submission/test/?session_var_foo=bar')
4175 4175
    assert resp.location.endswith('/backoffice/submission/test/')
4176 4176
    resp = resp.follow()
4177
    assert '<p class="comment-field ">XbarY</p>' in resp.body
4177
    assert '<p data-field-id="7" class="comment-field ">XbarY</p>' in resp.body
4178 4178

  
4179 4179
    # django template
4180 4180
    formdef.fields = [
......
4185 4185
    resp = app.get('/backoffice/submission/test/?session_var_foo=jang')
4186 4186
    assert resp.location.endswith('/backoffice/submission/test/')
4187 4187
    resp = resp.follow()
4188
    assert '<p class="comment-field ">django</p>' in resp.body
4188
    assert '<p data-field-id="7" class="comment-field ">django</p>' in resp.body
4189 4189

  
4190 4190
def test_backoffice_display_message(pub):
4191 4191
    user = create_user(pub)
tests/test_fields.py
133 133
    field = fields.TitleField(label='Foobar')
134 134
    form = Form(use_tokens=False)
135 135
    field.add_to_form(form)
136
    assert '<h3>Foobar</h3>' in str(form.render())
136
    assert '<h3 data-field-id="None">Foobar</h3>' in str(form.render())
137 137

  
138 138
    field = fields.TitleField(label='Foobar', extra_css_class='test')
139 139
    form = Form(use_tokens=False)
140 140
    field.add_to_form(form)
141
    assert '<h3 class="test">Foobar</h3>' in str(form.render())
141
    assert '<h3 data-field-id="None" class="test">Foobar</h3>' in str(form.render())
142 142

  
143 143
def test_subtitle():
144 144
    field = fields.SubtitleField(label='Foobar')
145 145
    form = Form(use_tokens=False)
146 146
    field.add_to_form(form)
147
    assert '<h4>Foobar</h4>' in str(form.render())
147
    assert '<h4 data-field-id="None">Foobar</h4>' in str(form.render())
148 148

  
149 149
    field = fields.SubtitleField(label='Foobar', extra_css_class='test')
150 150
    form = Form(use_tokens=False)
151 151
    field.add_to_form(form)
152
    assert '<h4 class="test">Foobar</h4>' in str(form.render())
152
    assert '<h4 data-field-id="None" class="test">Foobar</h4>' in str(form.render())
153 153

  
154 154
def test_comment():
155 155
    field = fields.CommentField(label='Foobar')
156 156
    form = Form(use_tokens=False)
157 157
    field.add_to_form(form)
158
    assert '<p class="comment-field ">Foobar</p>' in str(form.render())
158
    assert '<p data-field-id="None" class="comment-field ">Foobar</p>' in str(form.render())
159 159

  
160 160
    field = fields.CommentField(label='Foo\n\nBar\n\nBaz')
161 161
    form = Form(use_tokens=False)
162 162
    field.add_to_form(form)
163 163
    assert '<p>Foo</p>\n<p>Bar</p>\n<p>Baz</p>' in str(form.render())
164
    assert '<div class="comment-field "' in str(form.render())
164
    assert '<div data-field-id="None" class="comment-field "' in str(form.render())
165 165

  
166 166
    # test for variable substitution
167 167
    pub.substitutions.feed(MockSubstitutionVariables())
168 168
    field = fields.CommentField(label='{{ bar }}')
169 169
    form = Form(use_tokens=False)
170 170
    field.add_to_form(form)
171
    assert '<p class="comment-field ">Foobar</p>' in str(form.render())
171
    assert '<p data-field-id="None" class="comment-field ">Foobar</p>' in str(form.render())
172 172

  
173 173
    field = fields.CommentField(label='[bar]')
174 174
    form = Form(use_tokens=False)
175 175
    field.add_to_form(form)
176
    assert '<p class="comment-field ">Foobar</p>' in str(form.render())
176
    assert '<p data-field-id="None" class="comment-field ">Foobar</p>' in str(form.render())
177 177

  
178 178
    # test for proper escaping of substitution variables
179 179
    field = fields.CommentField(label='{{ foo }}')
180 180
    form = Form(use_tokens=False)
181 181
    field.add_to_form(form)
182
    assert '<p class="comment-field ">1 &lt; 3</p>' in str(form.render())
182
    assert '<p data-field-id="None" class="comment-field ">1 &lt; 3</p>' in str(form.render())
183 183

  
184 184
    field = fields.CommentField(label='[foo]')
185 185
    form = Form(use_tokens=False)
186 186
    field.add_to_form(form)
187
    assert '<p class="comment-field ">1 &lt; 3</p>' in str(form.render())
187
    assert '<p data-field-id="None" class="comment-field ">1 &lt; 3</p>' in str(form.render())
188 188

  
189 189
    # test for html content
190 190
    field = fields.CommentField(label='<p>Foobar</p>')
191 191
    form = Form(use_tokens=False)
192 192
    field.add_to_form(form)
193 193
    assert '<p>Foobar</p>' in str(form.render())
194
    assert '<div class="comment-field "' in str(form.render())
194
    assert '<div data-field-id="None" class="comment-field "' in str(form.render())
195 195
    assert field.unhtmled_label == 'Foobar'
196 196

  
197 197
    field = fields.CommentField(label='<p>Foobar&eacute;</p>')
tests/test_form_pages.py
1084 1084
    formdef.data_class().wipe()
1085 1085

  
1086 1086
    resp = get_app(pub).get('/test/')
1087
    assert not '<h3>1st page/h3>' in resp.body
1088
    assert '<h4>subtitle of 1st page</h4>' in resp.body
1087
    assert not '<h3 data-field-id="0">1st page/h3>' in resp.body
1088
    assert '<h4 data-field-id="5">subtitle of 1st page</h4>' in resp.body
1089 1089
    resp.form['f1'] = 'foo'
1090 1090
    resp = resp.form.submit('submit')
1091
    assert '<h3>title of second page</h3>' in resp.body
1091
    assert '<h3 data-field-id="6">title of second page</h3>' in resp.body
1092 1092
    resp = resp.form.submit('submit')  # -> validation page
1093 1093
    assert '<h3>1st page</h3>' in resp.body
1094
    assert '<h4>subtitle of 1st page</h4>' in resp.body
1095
    assert '<h3>title of second page</h3>' in resp.body
1094
    assert '<h4 data-field-id="5">subtitle of 1st page</h4>' in resp.body
1095
    assert '<h3 data-field-id="6">title of second page</h3>' in resp.body
1096 1096
    resp.form['f3'] = 'foo'
1097 1097
    resp = resp.form.submit('submit').follow()  # -> submit
1098 1098
    assert '<h3>1st page</h3>' in resp.body
......
5030 5030
    resp = resp.follow()
5031 5031
    assert '<span class="label">Bar</span>' in resp.body
5032 5032
    assert '<span class="label">Foo</span>' not in resp.body
5033

  
5034
def test_field_live_condition(pub):
5035
    FormDef.wipe()
5036
    formdef = FormDef()
5037
    formdef.name = 'Foo'
5038
    formdef.fields = [
5039
        fields.StringField(type='string', id='1', label='Bar', size='40',
5040
            required=True, varname='bar'),
5041
        fields.StringField(type='string', id='2', label='Foo', size='40',
5042
            required=True, varname='foo',
5043
            condition={'type': 'django', 'value': 'form_var_bar == "bye"'}),
5044
    ]
5045
    formdef.store()
5046

  
5047
    app = get_app(pub)
5048
    resp = app.get('/foo/')
5049
    assert 'f1' in resp.form.fields
5050
    assert 'f2' in resp.form.fields
5051
    assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
5052
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
5053
    resp.form['f1'] = 'hello'
5054
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5055
    assert live_resp.json['result']['1']['visible']
5056
    assert not live_resp.json['result']['2']['visible']
5057
    resp.form['f1'] = 'bye'
5058
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5059
    assert live_resp.json['result']['1']['visible']
5060
    assert live_resp.json['result']['2']['visible']
5061
    resp.form['f1'] = 'hello'
5062
    resp = resp.form.submit('submit')
5063
    assert 'Check values then click submit.' in resp.body
5064
    assert 'name="f1"' in resp.body
5065
    assert 'name="f2"' not in resp.body
5066
    resp = resp.form.submit('submit')
5067
    resp = resp.follow()
5068
    assert '<span class="label">Bar</span>' in resp.body
5069
    assert '<span class="label">Foo</span>' not in resp.body
5070

  
5071
    resp = get_app(pub).get('/foo/')
5072
    assert 'f1' in resp.form.fields
5073
    assert 'f2' in resp.form.fields
5074
    resp.form['f1'] = 'bye'
5075
    resp = resp.form.submit('submit')
5076
    assert 'There were errors' in resp.body
5077
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') is None
5078
    resp.form['f2'] = 'bye'
5079
    resp = resp.form.submit('submit')
5080
    assert 'Check values then click submit.' in resp.body
5081
    assert 'name="f1"' in resp.body
5082
    assert 'name="f2"' in resp.body
5083
    resp = resp.form.submit('submit')
5084
    resp = resp.follow()
5085
    assert '<span class="label">Bar</span>' in resp.body
5086
    assert '<span class="label">Foo</span>' in resp.body
5087

  
5088
def test_field_live_condition_multipages(pub):
5089
    FormDef.wipe()
5090
    formdef = FormDef()
5091
    formdef.name = 'Foo'
5092
    formdef.fields = [
5093
        fields.PageField(id='0', label='2nd page', type='page'),
5094
        fields.StringField(type='string', id='1', label='Bar', size='40',
5095
            required=True, varname='bar'),
5096
        fields.StringField(type='string', id='2', label='Foo', size='40',
5097
            required=True, varname='foo',
5098
            condition={'type': 'django', 'value': 'form_var_bar == "bye"'}),
5099
        fields.PageField(id='3', label='1st page', type='page'),
5100
        fields.StringField(type='string', id='4', label='Baz', size='40',
5101
            required=True, varname='baz'),
5102
    ]
5103
    formdef.store()
5104

  
5105
    app = get_app(pub)
5106
    resp = app.get('/foo/')
5107
    assert 'f1' in resp.form.fields
5108
    assert 'f2' in resp.form.fields
5109
    assert resp.html.find('div', {'data-field-id': '1'}).attrs['data-live-source'] == 'true'
5110
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') == 'display: none'
5111
    resp.form['f1'] = 'hello'
5112
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5113
    assert live_resp.json['result']['1']['visible']
5114
    assert not live_resp.json['result']['2']['visible']
5115
    resp.form['f1'] = 'bye'
5116
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5117
    assert live_resp.json['result']['1']['visible']
5118
    assert live_resp.json['result']['2']['visible']
5119
    resp.form['f1'] = 'bye'
5120
    resp.form['f2'] = 'bye'
5121
    resp = resp.form.submit('submit')
5122
    resp = resp.form.submit('previous')
5123
    assert resp.html.find('div', {'data-field-id': '2'}).attrs.get('style') is None
5124
    live_resp = app.post('/foo/live', params=resp.form.submit_fields())
5125
    assert live_resp.json['result']['1']['visible']
5126
    assert live_resp.json['result']['2']['visible']
5127
    resp = resp.form.submit('submit')
5128
    resp.form['f4'] = 'plop'
5129
    resp = resp.form.submit('submit')
5130
    assert 'Check values then click submit.' in resp.body
5131
    assert 'name="f1"' in resp.body
5132
    assert 'name="f2"' in resp.body
5133
    assert 'name="f4"' in resp.body
5134
    resp = resp.form.submit('submit')
wcs/backoffice/submission.py
76 76

  
77 77
class FormFillPage(PublicFormFillPage):
78 78
    _q_exports = ['', 'tempfile', 'autosave', 'code',
79
            ('remove', 'remove_draft')]
79
            ('remove', 'remove_draft'), 'live']
80 80

  
81 81
    filling_templates = ['wcs/formdata_filling.html']
82 82
    validation_templates = ['wcs/formdata_validation.html']
wcs/fields.py
381 381
        except RuntimeError:
382 382
            return True
383 383

  
384
    def get_condition_varnames(self):
385
        return re.findall(r'\bform[_\.]var[_\.]([a-zA-Z0-9_]+?)(?:_raw|\b)', self.condition['value'])
386

  
387
    def has_live_conditions(self, formdef):
388
        varnames = self.get_condition_varnames()
389
        if not varnames:
390
            return False
391
        field_position = formdef.fields.index(self)
392
        # rewind to field page
393
        for field_position in range(field_position, -1, -1):
394
            if formdef.fields[field_position].type == 'page':
395
                break
396
        else:
397
            field_position = -1  # form with no page
398
        # start from there
399
        for field in formdef.fields[field_position+1:]:
400
            if field.type == 'page':
401
                # stop at next page
402
                break
403
            if field.varname in varnames:
404
                return True
405
        return False
406

  
384 407

  
385 408
class WidgetField(Field):
386 409
    hint = None
......
413 436
                widget.extra_css_class = self.extra_css_class
414 437
        if self.varname:
415 438
            widget.div_id = 'var_%s' % self.varname
439
        return widget
416 440

  
417 441
    def perform_more_widget_changes(self, form, kwargs, edit = True):
418 442
        pass
......
519 543
    description = N_('Title')
520 544

  
521 545
    def add_to_form(self, form, value = None):
546
        extra_attributes = ' data-field-id="%s"' % self.id
522 547
        if self.extra_css_class:
523
            extra_css_class = ' class="%s"' % self.extra_css_class
524
        else:
525
            extra_css_class = ''
526
        form.widgets.append(HtmlWidget(
527
                    htmltext('<h3%s>%s</h3>' % (extra_css_class, self.label))))
548
            extra_attributes += ' class="%s"' % self.extra_css_class
549
        widget = HtmlWidget(htmltext('<h3%s>%s</h3>' % (extra_attributes, self.label)))
550
        form.widgets.append(widget)
551
        return widget
528 552
    add_to_view_form = add_to_form
529 553

  
530 554
    def fill_admin_form(self, form):
......
547 571
    description = N_('Subtitle')
548 572

  
549 573
    def add_to_form(self, form, value = None):
574
        extra_attributes = ' data-field-id="%s"' % self.id
550 575
        if self.extra_css_class:
551
            extra_css_class = ' class="%s"' % self.extra_css_class
552
        else:
553
            extra_css_class = ''
554
        form.widgets.append(HtmlWidget(
555
                    htmltext('<h4%s>%s</h4>' % (extra_css_class, self.label))))
576
            extra_attributes += ' class="%s"' % self.extra_css_class
577
        widget = HtmlWidget(htmltext('<h4%s>%s</h4>' % (extra_attributes, self.label)))
578
        form.widgets.append(widget)
579
        return widget
556 580
    add_to_view_form = add_to_form
557 581

  
558 582
register_field_class(SubtitleField)
......
563 587
    description = N_('Comment')
564 588

  
565 589
    def add_to_form(self, form, value = None):
566
        class_attribute = 'class="comment-field %s"' % (self.extra_css_class or '')
590
        tag_attributes = 'data-field-id="%s" class="comment-field %s"' % (
591
                self.id, self.extra_css_class or '')
567 592

  
568 593
        if '\n\n' in self.label:
569 594
            label = '<p>' + re.sub('\n\n+', '</p>\n<p>', self.label) + '</p>'
......
579 604
                enclosing_tag = 'div'
580 605
                break
581 606

  
582
        form.widgets.append(HtmlWidget(
583
                    htmltext('<%s %s>%s</%s>' % (enclosing_tag, class_attribute,
584
                    label, enclosing_tag))))
607
        widget = HtmlWidget(htmltext('<%s %s>%s</%s>' % (
608
            enclosing_tag, tag_attributes, label, enclosing_tag)))
609
        form.widgets.append(widget)
610
        return widget
585 611

  
586 612
    def add_to_view_form(self, *args):
587 613
        pass
......
1050 1076
    def add_to_form(self, form, value=None):
1051 1077
        if value and type(value) is not str:
1052 1078
            value = self.convert_value_to_str(value)
1053
        WidgetField.add_to_form(self, form, value=value)
1079
        return WidgetField.add_to_form(self, form, value=value)
1054 1080

  
1055 1081
    def add_to_view_form(self, form, value = None):
1056 1082
        value = localstrftime(value)
......
1531 1557
        # create variables with values currently being evaluated, not yet
1532 1558
        # available in the formdata.
1533 1559
        from formdata import get_dict_with_varnames
1534
        live_data = get_dict_with_varnames(formdef.fields, dict_vars)
1535
        form_live_data = dict(('form_' + x, y) for x, y in live_data.items())
1560
        live_data = {}
1561
        form_live_data = {}
1562
        if dict_vars is not None:
1563
            live_data = get_dict_with_varnames(formdef.fields, dict_vars)
1564
            form_live_data = dict(('form_' + x, y) for x, y in live_data.items())
1536 1565

  
1537 1566
        # 1) feed the form_var_* variables in the global substitution system,
1538 1567
        # they will shadow formdata context variables with their new "live"
wcs/formdef.py
532 532
                continue
533 533
            if not on_page:
534 534
                continue
535
            if not field.is_visible(form_data, self):
536
                continue
535
            visible = field.is_visible(form_data, self)
536
            if not visible:
537
                if not field.has_live_conditions(self):
538
                    # no live conditions so field can be skipped
539
                    continue
537 540
            if type(displayed_fields) is list:
538 541
                displayed_fields.append(field)
539 542
            value = None
540 543
            if form_data:
541 544
                value = form_data.get(field.id)
542
            field.add_to_form(form, value)
545
            widget = field.add_to_form(form, value)
546
            widget.is_hidden = not(visible)
547
            widget.field = field
543 548

  
544 549
    def get_page(self, page_no):
545 550
        return [x for x in self.fields if x.type == 'page'][page_no]
wcs/forms/root.py
16 16

  
17 17
import copy
18 18
import json
19
import re
19 20
import time
20 21
from StringIO import StringIO
21 22
import sys
......
171 172

  
172 173
class FormPage(Directory, FormTemplateMixin):
173 174
    _q_exports = ['', 'tempfile', 'schema', 'tryauth',
174
            'auth', 'qrcode', 'autosave', 'code', 'removedraft']
175
            'auth', 'qrcode', 'autosave', 'code', 'removedraft', 'live']
175 176

  
176 177
    filling_templates = ['wcs/front/formdata_filling.html', 'wcs/formdata_filling.html']
177 178
    validation_templates = ['wcs/front/formdata_validation.html', 'wcs/formdata_validation.html']
......
378 379
            if not one:
379 380
                req.form = {}
380 381

  
382
        live_condition_fields = {}
381 383
        for field in displayed_fields:
382 384
            if field.prefill:
383 385
                # always set additional attributes as they will be used for
384 386
                # "live prefill", regardless of existing data.
385 387
                form.get_widget('f%s' % field.id).prefill_attributes = field.get_prefill_attributes()
388
            if field.condition:
389
                field.varnames = field.get_condition_varnames()
390
                for varname in field.varnames:
391
                    if not varname in live_condition_fields:
392
                        live_condition_fields[varname] = []
393
                    live_condition_fields[varname].append(field)
394

  
395
        for field in displayed_fields:
396
            if field.varname not in live_condition_fields:
397
                continue
398
            form.get_widget('f%s' % field.id).live_condition_source = True
386 399

  
387 400
        self.html_top(self.formdef.name)
388 401

  
389 402
        form.add_hidden('step', '0')
390 403
        form.add_hidden('page', self.pages.index(page))
404
        if page:
405
            form.add_hidden('page_id', page.id)
391 406

  
392 407
        form.add_submit('cancel', _('Cancel'), css_class = 'cancel')
393 408
        if self.formdef.enable_tracking_codes and not self.edit_mode:
......
696 711

  
697 712
            self.feed_current_data(magictoken)
698 713

  
699
            form = self.create_form(page=page)
714
            submitted_fields = []
715
            form = self.create_form(page=page, displayed_fields=submitted_fields)
700 716
            form.add_submit('previous')
701 717
            if self.formdef.enable_tracking_codes:
702 718
                form.add_submit('removedraft')
......
708 724
            if self.formdef.enable_tracking_codes and form.get_submit() == 'removedraft':
709 725
                return self.removedraft()
710 726

  
727
            form_data = session.get_by_magictoken(magictoken, {})
728
            data = self.formdef.get_data(form)
729
            form_data.update(data)
730

  
711 731
            if self.formdef.enable_tracking_codes and form.get_submit() == 'savedraft':
712
                form_data = session.get_by_magictoken(magictoken, {})
713
                data = self.formdef.get_data(form)
714
                form_data.update(data)
715 732
                filled = self.save_draft(form_data, page_no)
716 733
                return redirect(filled.get_url().rstrip('/'))
717 734

  
735
            for field in submitted_fields:
736
                if not field.is_visible(form_data, self.formdef):
737
                    del form._names['f%s' % field.id]
738

  
718 739
            page_error_messages = []
719 740
            if form.get_submit() == 'submit' and page:
720 741
                post_conditions = page.post_conditions or []
......
988 1009
                    pass
989 1010
        return None
990 1011

  
1012
    def live(self):
1013
        get_request().ignore_session = True
1014
        # live evaluation of fields
1015
        get_response().set_content_type('application/json')
1016
        def result_error(reason):
1017
            return json.dumps({'result': 'error', 'reason': reason})
1018

  
1019
        session = get_session()
1020
        if not session:
1021
            return result_error('missing session')
1022

  
1023
        formdata = self.get_transient_formdata()
1024
        get_publisher().substitutions.feed(formdata)
1025

  
1026
        page_id = get_request().form.get('page_id')
1027
        if page_id:
1028
            for field in self.formdef.fields:
1029
                if str(field.id) == page_id:
1030
                    page = field
1031
                    break
1032
        else:
1033
            page = None
1034

  
1035
        displayed_fields = []
1036
        form = self.create_form(page=page, displayed_fields=displayed_fields)
1037
        formdata.data.update(self.formdef.get_data(form))
1038

  
1039
        result = {}
1040
        for field in displayed_fields:
1041
            result[field.id] = {'visible': field.is_visible(formdata.data, self.formdef)}
1042

  
1043
        return json.dumps({'result': result})
1044

  
991 1045
    def submitted(self, form, existing_formdata = None):
992 1046
        if existing_formdata: # modifying
993 1047
            filled = existing_formdata
wcs/qommon/form.py
417 417
        return self.render_content()
418 418

  
419 419
    def render_content(self):
420
        if self.title:
421
            return htmltext(self.title)
422
        return htmltext(self.string)
420
        content = self.title or self.string or ''
421
        if getattr(self, 'is_hidden', False):
422
            content = htmltext(str(content).replace('>', ' style="display: none">', 1))
423
        return htmltext(content)
423 424

  
424 425
    def has_error(self, request):
425 426
        return False
wcs/qommon/static/js/qommon.forms.js
67 67
    }
68 68
    return true;
69 69
  });
70
  var live_evaluation = null;
71
  $('form div[data-live-source] input, form div[data-live-source] select').on('change keyup paste', function() {
72
    var new_data = $(this).parents('form').serialize();
73
    if (live_evaluation) {
74
      live_evaluation.abort();
75
    }
76
    live_evaluation = $.ajax({
77
      type: 'POST',
78
      url: window.location.pathname + 'live',
79
      dataType: 'json',
80
      data: new_data,
81
      headers: {'accept': 'application/json'},
82
      success: function(json) {
83
        $.each(json.result, function(key, value) {
84
          var $widget = $('[data-field-id="' + key + '"]');
85
          if (value.visible) {
86
            $widget.show();
87
          } else {
88
            $widget.hide();
89
          }
90
        });
91
      }
92
    });
93
  });
70 94
});
wcs/qommon/templates/qommon/forms/widget.html
4 4
     {% if widget.get_error %}widget-with-error{% endif %}
5 5
     {% if widget.is_required %}widget-required{% else %}widget-optional{% endif %}
6 6
     {% if widget.is_prefilled %}widget-prefilled{% endif %}"
7
     {% if widget.is_hidden %}style="display: none"{% endif %}
8
     {% if widget.field %}data-field-id="{{ widget.field.id }}"{% endif %}
7 9
     {% if widget.div_id %}id="{{widget.div_id}}" data-valuecontainerid="form_{{widget.name}}"{% endif %}
8 10
     {% for attr in widget.prefill_attributes %}
9 11
     data-{{attr}}="{{widget.prefill_attributes|get:attr}}"
......
13 15
     {% endif %}
14 16
     {% if "data-dynamic-display-value" in widget.attrs %}
15 17
     data-dynamic-display-value="{{widget.attrs|get:"data-dynamic-display-value"}}"
16
     {% endif %}>
18
     {% endif %}
19
     {% if widget.live_condition_source %}data-live-source="true"{% endif %}
20
     >
17 21
  {% block widget-title %}
18 22
  {{widget.rendered_title}}
19 23
  {% endblock %}
20
-