Projet

Général

Profil

0001-misc-add-options-to-display-hide-fields-on-validatio.patch

Frédéric Péters, 28 septembre 2019 17:36

Télécharger (24,4 ko)

Voir les différences:

Subject: [PATCH] misc: add options to display/hide fields on
 validation/summary pages (#36505)

 tests/test_admin_pages.py         |   2 +-
 tests/test_backoffice_pages.py    |  35 ++++++----
 tests/test_form_pages.py          |  32 +++++++++
 wcs/admin/workflows.py            |   6 ++
 wcs/backoffice/data_management.py |   2 +-
 wcs/backoffice/management.py      |   6 +-
 wcs/fields.py                     | 110 ++++++++++++++++++++----------
 wcs/formdef.py                    |  11 ++-
 wcs/forms/common.py               |   9 ++-
 wcs/wf/form.py                    |   2 +-
 10 files changed, 158 insertions(+), 57 deletions(-)
tests/test_admin_pages.py
2739 2739
    resp = resp.follow()
2740 2740
    assert 'foobar' in resp.body
2741 2741
    resp = resp.click('Edit')
2742
    assert not 'in_listing' in resp.form.fields.keys()
2742
    assert not 'display_locations' in resp.form.fields.keys()
2743 2743
    assert 'condition$type' in resp.form.fields.keys()
2744 2744
    resp = resp.form.submit('cancel')
2745 2745
    resp = resp.follow()
tests/test_backoffice_pages.py
121 121

  
122 122
    formdef.fields = [
123 123
            fields.StringField(id='1', label='1st field', type='string',
124
                in_listing=True),
124
                display_locations=['validation', 'summary', 'listings']),
125 125
            fields.ItemField(id='2', label='2nd field', type='item',
126 126
                items=['foo', 'bar', 'baz'],
127
                in_listing=True),
127
                display_locations=['validation', 'summary', 'listings']),
128 128
            fields.ItemField(id='3', label='3rd field', type='item',
129
                data_source=datasource, in_listing=False, varname='foo'),
129
                data_source=datasource, varname='foo'),
130 130
        ]
131 131

  
132 132
    formdef.store()
......
568 568
    create_superuser(pub)
569 569
    create_environment(pub)
570 570
    formdef = FormDef.get_by_urlname('form-title')
571
    formdef.fields.append(fields.FileField(id='4', label='file field', type='file', in_listing=True))
571
    formdef.fields.append(fields.FileField(id='4', label='file field', type='file',
572
                display_locations=['validation', 'summary', 'listings']))
572 573
    formdef.store()
573 574

  
574 575
    upload = PicklableUpload('test.jpeg', 'image/jpeg')
......
654 655
    create_environment(pub)
655 656
    formdef = FormDef.get_by_urlname('form-title')
656 657
    formdef.fields.append(fields.BoolField(id='4', label='4th field',
657
        type='bool', in_listing=True))
658
        type='bool',
659
        display_locations=['validation', 'summary', 'listings']))
658 660
    formdef.store()
659 661

  
660 662
    for i, formdata in enumerate(formdef.data_class().select()):
......
683 685
    create_environment(pub)
684 686
    formdef = FormDef.get_by_urlname('form-title')
685 687
    formdef.fields.append(fields.ItemsField(id='4', label='4th field', type='items',
686
        items=['a', 'b', 'c', 'd'], in_listing=True))
688
        items=['a', 'b', 'c', 'd'],
689
        display_locations=['validation', 'summary', 'listings']))
687 690
    formdef.store()
688 691

  
689 692
    for i, formdata in enumerate(formdef.data_class().select()):
......
735 738
    assert len(resp.body.splitlines()[0].split(',')) == 7
736 739

  
737 740
    formdef = FormDef.get_by_urlname('form-title')
738
    formdef.fields[-1].in_listing = True
741
    formdef.fields[-1].display_locations = ['validation', 'summary', 'listings']
739 742
    formdef.store()
740 743
    resp = app.get('/backoffice/management/form-title/')
741 744
    resp = resp.click('Export as CSV File')
......
877 880
    assert resp.body[:2] == 'PK' # ods has a zip container
878 881

  
879 882
    formdef = FormDef.get_by_urlname('form-title')
880
    formdef.fields.append(fields.FileField(id='4', label='file field', type='file', in_listing=True))
881
    formdef.fields.append(fields.DateField(id='5', label='date field', type='date', in_listing=True))
882
    formdef.fields.append(fields.StringField(id='6', label='number field', type='string', in_listing=True))
883
    formdef.fields.append(fields.StringField(id='7', label='phone field', type='string', in_listing=True))
884
    formdef.fields.append(fields.DateField(id='8', label='very old field', type='date', in_listing=True))
885
    formdef.fields.append(fields.StringField(id='9', label='string field', type='string', in_listing=True))
883
    formdef.fields.append(fields.FileField(id='4', label='file field', type='file',
884
        display_locations=['validation', 'summary', 'listings']))
885
    formdef.fields.append(fields.DateField(id='5', label='date field', type='date',
886
        display_locations=['validation', 'summary', 'listings']))
887
    formdef.fields.append(fields.StringField(id='6', label='number field', type='string',
888
        display_locations=['validation', 'summary', 'listings']))
889
    formdef.fields.append(fields.StringField(id='7', label='phone field', type='string',
890
        display_locations=['validation', 'summary', 'listings']))
891
    formdef.fields.append(fields.DateField(id='8', label='very old field', type='date',
892
        display_locations=['validation', 'summary', 'listings']))
893
    formdef.fields.append(fields.StringField(id='9', label='string field', type='string',
894
        display_locations=['validation', 'summary', 'listings']))
886 895
    formdef.store()
887 896

  
888 897
    formdata = formdef.data_class().select(lambda x: x.status == 'wf-new')[0]
tests/test_form_pages.py
1205 1205
    assert '<h3>3rd page</h3>' in resp.body
1206 1206
    assert '<h3>4th page</h3>' not in resp.body
1207 1207

  
1208
def test_form_display_locations(pub):
1209
    formdef = create_formdef()
1210
    formdef.fields = [
1211
        fields.StringField(id='1', label='string1', display_locations=[]),
1212
        fields.StringField(id='2', label='string2', display_locations=['validation']),
1213
        fields.StringField(id='3', label='string3', display_locations=['summary']),
1214
        fields.CommentField(id='4', label='Bla bla bla', type='comment',
1215
            display_locations=['validation', 'summary']),
1216
    ]
1217
    formdef.store()
1218
    formdef.data_class().wipe()
1219

  
1220
    resp = get_app(pub).get('/test/')
1221
    resp.form['f1'] = 'plop1'
1222
    resp.form['f2'] = 'plop2'
1223
    resp.form['f3'] = 'plop3'
1224
    resp = resp.form.submit('submit')  # -> validation
1225
    pq = resp.pyquery.remove_namespaces()
1226
    assert pq('div[style="display: none;"] [name=f1]')
1227
    assert not pq('div[style="display: none;"] [name=f2]')
1228
    assert pq('div[style="display: none;"] [name=f3]')
1229
    assert 'Bla bla bla' in resp.body
1230

  
1231
    resp = resp.form.submit('submit').follow()  # -> submit
1232
    assert formdef.data_class().select()[0].data['1'] == 'plop1'
1233
    assert formdef.data_class().select()[0].data['2'] == 'plop2'
1234
    assert formdef.data_class().select()[0].data['3'] == 'plop3'
1235
    assert not 'plop1' in resp.body
1236
    assert not 'plop2' in resp.body
1237
    assert 'plop3' in resp.body
1238
    assert 'Bla bla bla' in resp.body
1239

  
1208 1240
def test_form_visit_existing(pub):
1209 1241
    user = create_user(pub)
1210 1242
    formdef = create_formdef()
wcs/admin/workflows.py
839 839
        form.add(WorkflowVariableWidget, 'varname', title=_('Variable'),
840 840
                value=self.field.varname, advanced=False, required=True,
841 841
                workflow=self.objectdef.workflow)
842
        display_locations = form.get_widget('display_locations')
843
        if display_locations:
844
            del display_locations.widgets[0]  # validation page
842 845
        return form
843 846

  
844 847

  
......
849 852
    def form(self):
850 853
        form = super(WorkflowBackofficeFieldDefPage, self).form()
851 854
        form.remove('prefill')
855
        display_locations = form.get_widget('display_locations')
856
        if display_locations:
857
            del display_locations.widgets[0]  # validation page
852 858
        return form
853 859

  
854 860

  
wcs/backoffice/data_management.py
99 99
    def get_default_columns(self):
100 100
        field_ids = ['id', 'time']
101 101
        for field in self.formdef.get_all_fields():
102
            if hasattr(field, 'get_view_value') and field.in_listing:
102
            if hasattr(field, 'get_view_value') and field.include_in_listing:
103 103
                field_ids.append(field.id)
104 104
        return field_ids
105 105

  
wcs/backoffice/management.py
398 398
        if include_email_column:
399 399
            r += htmltext('<th data-field-sort-key="email"><span>%s</span></th>') % _('Email')
400 400
        for field in formdef.get_all_fields():
401
            if field.in_listing:
401
            if field.include_in_listing:
402 402
                r += htmltext('<th data-field-sort-key="f%s"><span>%s</span></th>') % (
403 403
                        field.id, field.label)
404 404
        r += htmltext('</tr>')
......
412 412
            if include_email_column:
413 413
                r += htmltext('<td>%s</td>') % (user.email or '')
414 414
            for field in formdef.get_all_fields():
415
                if field.in_listing:
415
                if field.include_in_listing:
416 416
                    r += htmltext('<td>%s</td>') % (user.form_data.get(field.id) or '')
417 417
            r += htmltext('</tr>')
418 418

  
......
1232 1232
    def get_default_columns(self):
1233 1233
        field_ids = ['id', 'time', 'last_update_time', 'user-label']
1234 1234
        for field in self.formdef.get_all_fields():
1235
            if hasattr(field, 'get_view_value') and field.in_listing:
1235
            if hasattr(field, 'get_view_value') and field.include_in_listing:
1236 1236
                field_ids.append(field.id)
1237 1237
        field_ids.append('status')
1238 1238
        return field_ids
wcs/fields.py
159 159
    convert_value_from_str = None
160 160
    convert_value_to_str = None
161 161
    convert_value_from_anything = None
162
    in_listing = False
162
    display_locations = []
163 163
    prefill = None
164 164
    store_display_value = None
165 165
    store_structured_value = None
......
178 178
    def init(cls):
179 179
        pass
180 180

  
181
    @property
182
    def include_in_listing(self):
183
        return 'listings' in (self.display_locations or [])
184

  
185
    @property
186
    def include_in_validation_page(self):
187
        return 'validation' in (self.display_locations or [])
188

  
189
    @property
190
    def include_in_summary_page(self):
191
        return 'summary' in (self.display_locations or [])
192

  
181 193
    @property
182 194
    def unhtmled_label(self):
183 195
        charset = get_publisher().site_charset
......
194 206
        else:
195 207
            extra_fields = []
196 208
        for attribute in self.get_admin_attributes() + extra_fields:
197
            if attribute in ('in_listing',):
209
            if attribute == 'display_locations':
198 210
                continue
199 211
            if hasattr(self, attribute) and getattr(self, attribute) is not None:
200 212
                val = getattr(self, attribute)
......
392 404
        pass
393 405

  
394 406
    def migrate(self):
395
        return False
407
        changed = False
408
        if getattr(self, 'in_listing', None) is not None:
409
            if self.in_listing:
410
                self.display_locations = self.display_locations[:]
411
                self.display_locations.append('listings')
412
                changed = True
413
                self.in_listing = None
414
        return changed
396 415

  
397 416
    def evaluate_condition(self, dict_vars, formdef, condition):
398 417
        return PageCondition(condition, {'dict_vars': dict_vars, 'formdef': formdef}).evaluate()
......
436 455
class WidgetField(Field):
437 456
    hint = None
438 457
    required = True
439
    in_listing = False
458
    display_locations = ['validation', 'summary']
440 459
    extra_attributes = []
441 460
    prefill = {}
442 461

  
......
494 513
            else:
495 514
                widget.extra_css_class = self.extra_css_class
496 515

  
516
    def get_display_locations_options(self):
517
        return [('validation', _('Validation Page')),
518
                ('summary', _('Summary Page')),
519
                ('listings', _('Management Listings'))]
520

  
497 521
    def fill_admin_form(self, form):
498 522
        form.add(StringWidget, 'label', title = _('Label'), value = self.label,
499 523
                required = True, size = 50)
......
504 528
                hint=_('This is used as suffix for variable names.'))
505 529
        form.add(TextWidget, 'hint', title = _('Hint'), value = self.hint,
506 530
                cols=60, rows=3)
507
        form.add(CheckboxWidget, 'in_listing', title = _('Display in listings'),
508
                value = self.in_listing, advanced=True)
531
        form.add(CheckboxesWidget, 'display_locations', title=_('Display Locations'),
532
                options=self.get_display_locations_options(),
533
                value=self.display_locations,
534
                advanced=True)
509 535
        form.add(StringWidget, 'extra_css_class', title = _('Extra classes for CSS styling'),
510 536
                value=self.extra_css_class, size=30, advanced=(not self.extra_css_class))
511 537
        prefill_in_advanced = (not self.prefill or self.prefill.get('type') == 'none')
......
531 557

  
532 558
    def get_admin_attributes(self):
533 559
        return Field.get_admin_attributes(self) + ['required', 'hint',
534
                'varname', 'in_listing',
560
                'varname', 'display_locations',
535 561
                'extra_css_class', 'prefill']
536 562

  
537 563
    def get_csv_heading(self):
......
569 595
class TitleField(Field):
570 596
    key = 'title'
571 597
    description = N_('Title')
598
    display_locations = ['validation', 'summary']
572 599

  
573 600
    def add_to_form(self, form, value = None):
574 601
        extra_attributes = ' data-field-id="%s"' % self.id
......
579 606
        return widget
580 607
    add_to_view_form = add_to_form
581 608

  
609
    def get_display_locations_options(self):
610
        return [('validation', _('Validation Page')),
611
                ('summary', _('Summary Page'))]
612

  
582 613
    def fill_admin_form(self, form):
583 614
        form.add(StringWidget, 'label', title = _('Label'), value = self.label,
584 615
                required = True, size = 50)
......
587 618
        form.add(ConditionWidget, 'condition', title=_('Display Condition'), value=self.condition,
588 619
                required=False, size=50, django_only=True,
589 620
                advanced=not(bool(self.condition)))
621
        form.add(CheckboxesWidget, 'display_locations', title=_('Display Locations'),
622
                options=self.get_display_locations_options(),
623
                value=self.display_locations,
624
                advanced=True)
590 625

  
591 626
    def get_admin_attributes(self):
592
        return Field.get_admin_attributes(self) + ['extra_css_class']
627
        return Field.get_admin_attributes(self) + ['extra_css_class', 'display_locations']
593 628

  
594 629
register_field_class(TitleField)
595 630

  
......
613 648
class CommentField(Field):
614 649
    key = 'comment'
615 650
    description = N_('Comment')
651
    display_locations = []
616 652

  
617
    def add_to_form(self, form, value=None):
653
    def get_text(self):
618 654
        import wcs.workflows
619 655
        label = self.get_html_content()
620
        label = wcs.workflows.template_on_html_string(label)
656
        return wcs.workflows.template_on_html_string(label)
657

  
658
    def add_to_form(self, form, value=None):
621 659
        widget = CommentWidget(
622
                content=label,
660
                content=self.get_text(),
623 661
                extra_css_class=self.extra_css_class)
624 662
        form.widgets.append(widget)
625 663
        return widget
626 664

  
627 665
    def add_to_view_form(self, *args):
628
        pass
666
        if self.include_in_validation_page:
667
            return self.add_to_form(*args)
668
        return None
629 669

  
630 670
    def get_html_content(self):
631 671
        if not self.label:
......
638 678
            return '<p>' + label + '</p>'
639 679
        return '<p>%s</p>' % str(htmlescape(self.label))
640 680

  
681
    def get_display_locations_options(self):
682
        return [('validation', _('Validation Page')),
683
                ('summary', _('Summary Page'))]
684

  
641 685
    def fill_admin_form(self, form):
642 686
        if self.label and (self.label[0] != '<' and '[end]' in self.label):
643 687
            form.add(TextWidget, 'label', title=_('Label'), value=self.label,
......
650 694
        form.add(ConditionWidget, 'condition', title=_('Display Condition'), value=self.condition,
651 695
                required=False, size=50, django_only=True,
652 696
                advanced=not(bool(self.condition)))
697
        form.add(CheckboxesWidget, 'display_locations', title=_('Display Locations'),
698
                options=self.get_display_locations_options(),
699
                value=self.display_locations,
700
                advanced=True)
653 701

  
654 702
    def get_admin_attributes(self):
655
        return Field.get_admin_attributes(self) + ['extra_css_class']
703
        return Field.get_admin_attributes(self) + ['extra_css_class', 'display_locations']
656 704

  
657 705

  
658 706
register_field_class(CommentField)
......
721 769
        return str(value)
722 770

  
723 771
    def migrate(self):
772
        changed = super(StringField, self).migrate()
724 773
        if isinstance(self.validation, basestring):
725 774
            self.validation = {'type': 'regex', 'value': self.validation}
726
            return True
727
        return False
775
            changed = True
776
        return changed
728 777

  
729 778
    def init_with_xml(self, element, charset, include_id=False):
730 779
        super(StringField, self).init_with_xml(element, charset, include_id=include_id)
......
1036 1085
        return document_types
1037 1086

  
1038 1087
    def migrate(self):
1088
        changed = super(FileField, self).migrate()
1039 1089
        if 'file_type' in self.__dict__:
1040 1090
            self.document_type = {}
1041 1091
            if self.__dict__['file_type']:
......
1064 1114
                        'mimetypes': file_type,
1065 1115
                    }
1066 1116
            del self.__dict__['file_type']
1067
            return True
1068
        return False
1117
            changed = True
1118
        return changed
1069 1119

  
1070 1120
    def export_to_xml(self, charset, include_id=False):
1071 1121
        # convert some sub-fields to strings as export_to_xml() only supports
......
1784 1834
class TableField(WidgetField):
1785 1835
    key = 'table'
1786 1836
    description = N_('Table')
1787
    in_listing = False # no way to represent data in a single cell
1788 1837

  
1789 1838
    rows = None
1790 1839
    columns = None
......
1800 1849
        kwargs['rows'] = self.rows
1801 1850
        kwargs['columns'] = self.columns
1802 1851

  
1852
    def get_display_locations_options(self):
1853
        return [('validation', _('Validation Page')),
1854
                ('summary', _('Summary Page'))]
1855

  
1803 1856
    def fill_admin_form(self, form):
1804 1857
        WidgetField.fill_admin_form(self, form)
1805
        try:
1806
            form.remove('in_listing')
1807
        except KeyError: # perhaps it was already removed
1808
            pass
1809 1858
        try:
1810 1859
            form.remove('prefill')
1811 1860
        except KeyError: # perhaps it was already removed
......
1821 1870

  
1822 1871
    def get_admin_attributes(self):
1823 1872
        t = WidgetField.get_admin_attributes(self) + ['rows', 'columns']
1824
        try:
1825
            t.remove('in_listing')
1826
        except ValueError:
1827
            pass
1828 1873
        try:
1829 1874
            t.remove('prefill')
1830 1875
        except ValueError:
......
1955 2000
class TableRowsField(WidgetField):
1956 2001
    key = 'tablerows'
1957 2002
    description = N_('Table with rows')
1958
    in_listing = False # no way to represent data in a single cell
1959 2003

  
1960 2004
    total_row = True
1961 2005
    columns = None
......
1970 2014
        kwargs['columns'] = self.columns
1971 2015
        kwargs['add_element_label'] = _('Add row')
1972 2016

  
2017
    def get_display_locations_options(self):
2018
        return [('validation', _('Validation Page')),
2019
                ('summary', _('Summary Page'))]
2020

  
1973 2021
    def fill_admin_form(self, form):
1974 2022
        WidgetField.fill_admin_form(self, form)
1975
        try:
1976
            form.remove('in_listing')
1977
        except KeyError: # perhaps it was already removed
1978
            pass
1979 2023
        try:
1980 2024
            form.remove('prefill')
1981 2025
        except KeyError: # perhaps it was already removed
......
1989 2033

  
1990 2034
    def get_admin_attributes(self):
1991 2035
        t = WidgetField.get_admin_attributes(self) + ['columns', 'total_row']
1992
        try:
1993
            t.remove('in_listing')
1994
        except ValueError:
1995
            pass
1996 2036
        try:
1997 2037
            t.remove('prefill')
1998 2038
        except ValueError:
wcs/formdef.py
635 635
                # same text as the page.
636 636
                continue
637 637

  
638
            if field.type in ('comment', 'page'):
638
            if field.type == 'comment' and not field.include_in_validation_page:
639
                # don't render field that wouldn't be displayed.
639 640
                continue
640 641

  
641 642
            if not field.is_visible(dict, self):
......
643 644

  
644 645
            current_page_fields.append(field)
645 646
            value = dict.get(field.id)
646
            field.add_to_view_form(form, value)
647

  
648
            if not field.include_in_validation_page:
649
                form.widgets.append(HtmlWidget(htmltext('<div style="display: none;">')))
650
                field.add_to_view_form(form, value)
651
                form.widgets.append(HtmlWidget(htmltext('</div>')))
652
            else:
653
                field.add_to_view_form(form, value)
647 654

  
648 655
        if on_page:
649 656
            form.widgets.append(HtmlWidget(htmltext('</div></div>')))
wcs/forms/common.py
417 417
                # same text as the page.
418 418
                continue
419 419

  
420
            if f.type in ('title', 'subtitle'):
420
            if f.type in ('title', 'subtitle', 'comment') and f.include_in_summary_page:
421 421
                current_page_fields.append({'field': f})
422 422
                continue
423 423

  
424 424
            if not hasattr(f, 'get_view_value'):
425 425
                continue
426 426

  
427
            if not f.include_in_summary_page:
428
                continue
429

  
427 430
            value = get_value(f)
428 431
            if value is None and not (f.required and include_unset_required_fields):
429 432
                continue
......
461 464
                r += htmltext('<div class="subtitle %s"><h4>%s</h4></div>') % (f.extra_css_class or '', f.label)
462 465
                continue
463 466

  
467
            if f.type == 'comment':
468
                r += htmltext('<div class="comment-field %s">%s</div>' % (f.extra_css_class or '', f.get_text()))
469
                continue
470

  
464 471
            css_classes = ['field', 'field-type-%s' % f.key]
465 472
            if f.extra_css_class:
466 473
                css_classes.append(f.extra_css_class)
wcs/wf/form.py
56 56

  
57 57
class WorkflowFormFieldDefPage(FieldDefPage):
58 58
    section = 'workflows'
59
    blacklisted_attributes = ['in_listing']
59
    blacklisted_attributes = ['display_locations']
60 60

  
61 61
    def get_deletion_extra_warning(self):
62 62
        return None
63
-