Projet

Général

Profil

0001-fields-make-it-possible-to-include-disabled-items-in.patch

Frédéric Péters, 26 septembre 2016 08:55

Télécharger (33,3 ko)

Voir les différences:

Subject: [PATCH] fields: make it possible to include disabled items in
 datasources (#12967)

 tests/test_datasource.py         |  59 +++++++++++-----
 tests/test_fields.py             |  24 +++++--
 tests/test_form_pages.py         | 142 +++++++++++++++++++++++++++++++++++++++
 wcs/admin/forms.py               |   4 +-
 wcs/admin/settings.py            |   8 +--
 wcs/backoffice/management.py     |   9 +--
 wcs/data_sources.py              |   9 ++-
 wcs/fields.py                    |  42 +++++++++---
 wcs/qommon/form.py               | 102 +++++++++++++++++-----------
 wcs/qommon/static/css/qommon.css |   4 ++
 10 files changed, 322 insertions(+), 81 deletions(-)
tests/test_datasource.py
58 58
def test_python_datasource():
59 59
    plain_list = [('1', 'foo'), ('2', 'bar')]
60 60
    datasource = {'type': 'formula', 'value': repr(plain_list)}
61
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
61
    assert data_sources.get_items(datasource) == [
62
            ('1', 'foo', '1', {'id': '1', 'text': 'foo'}),
63
            ('2', 'bar', '2', {'id': '2', 'text': 'bar'})]
62 64
    assert data_sources.get_structured_items(datasource) == [
63 65
            {'id': '1', 'text': 'foo'}, {'id': '2', 'text': 'bar'}]
64 66

  
......
73 75
    # three-item tuples
74 76
    plain_list = [('1', 'foo', 'a'), ('2', 'bar', 'b')]
75 77
    datasource = {'type': 'formula', 'value': repr(plain_list)}
76
    assert data_sources.get_items(datasource) == [('1', 'foo', 'a'), ('2', 'bar', 'b')]
78
    assert data_sources.get_items(datasource) == [
79
            ('1', 'foo', 'a', {'id': '1', 'key': 'a', 'text': 'foo'}),
80
            ('2', 'bar', 'b', {'id': '2', 'key': 'b', 'text': 'bar'})]
77 81

  
78 82
    # single-item tuples
79 83
    plain_list = [('foo', ), ('bar', )]
80 84
    datasource = {'type': 'formula', 'value': repr(plain_list)}
81
    assert data_sources.get_items(datasource) == [('foo', 'foo', 'foo'), ('bar', 'bar', 'bar')]
85
    assert data_sources.get_items(datasource) == [
86
            ('foo', 'foo', 'foo', {'id': 'foo', 'text': 'foo'}),
87
            ('bar', 'bar', 'bar', {'id': 'bar', 'text': 'bar'})]
82 88

  
83 89
    # list of strings
84 90
    plain_list = ['foo', 'bar']
85 91
    datasource = {'type': 'formula', 'value': repr(plain_list)}
86
    assert data_sources.get_items(datasource) == [('foo', 'foo', 'foo'), ('bar', 'bar', 'bar')]
92
    assert data_sources.get_items(datasource) == [
93
            ('foo', 'foo', 'foo', {'id': 'foo', 'text': 'foo'}),
94
            ('bar', 'bar', 'bar', {'id': 'bar', 'text': 'bar'})]
95

  
96
    # list of dicts
97
    plain_list = [{'id': 'foo', 'text': 'Foo'}, {'id': 'bar', 'text': 'Bar', 'disabled': True}]
98
    datasource = {'type': 'formula', 'value': repr(plain_list)}
99
    assert data_sources.get_items(datasource) == [
100
            ('foo', 'Foo', 'foo', {'id': 'foo', 'text': 'Foo'})]
101
    assert data_sources.get_items(datasource, include_disabled=True) == [
102
            ('foo', 'Foo', 'foo', {'id': 'foo', 'text': 'Foo'}),
103
            ('bar', 'Bar', 'bar', {'id': 'bar', 'text': 'Bar', 'disabled': True})]
87 104

  
88 105
def test_json_datasource():
89 106
    datasource = {'type': 'json', 'value': ''}
......
122 139
    json_file = open(json_file_path, 'w')
123 140
    json.dump({'data': [{'id': '1', 'text': 'foo'}, {'id': '2', 'text': 'bar'}]}, json_file)
124 141
    json_file.close()
125
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
142
    assert data_sources.get_items(datasource) == [
143
            ('1', 'foo', '1', {'id': '1', 'text': 'foo'}),
144
            ('2', 'bar', '2', {'id': '2', 'text': 'bar'})]
126 145
    assert data_sources.get_structured_items(datasource) == [
127 146
            {'id': '1', 'text': 'foo'}, {'id': '2', 'text': 'bar'}]
128 147

  
......
131 150
    json.dump({'data': [{'id': '1', 'text': 'foo', 'more': 'xxx'},
132 151
                        {'id': '2', 'text': 'bar', 'more': 'yyy'}]}, json_file)
133 152
    json_file.close()
134
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
153
    assert data_sources.get_items(datasource) == [
154
            ('1', 'foo', '1', {'id': '1', 'text': 'foo', 'more': 'xxx'}),
155
            ('2', 'bar', '2', {'id': '2', 'text': 'bar', 'more': 'yyy'})]
135 156
    assert data_sources.get_structured_items(datasource) == [
136 157
            {'id': '1', 'text': 'foo', 'more': 'xxx'},
137 158
            {'id': '2', 'text': 'bar', 'more': 'yyy'}]
......
142 163
            return {'json_url': 'file://%s' % json_file_path}
143 164
    pub.substitutions.feed(JsonUrlPath())
144 165
    datasource = {'type': 'json', 'value': '[json_url]'}
145
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
166
    assert data_sources.get_items(datasource) == [
167
            ('1', 'foo', '1', {'id': '1', 'text': 'foo', 'more': 'xxx'}),
168
            ('2', 'bar', '2', {'id': '2', 'text': 'bar', 'more': 'yyy'})]
146 169

  
147 170
    # a json file with integer as 'id'
148 171
    json_file = open(json_file_path, 'w')
149 172
    json.dump({'data': [{'id': 1, 'text': 'foo'}, {'id': 2, 'text': 'bar'}]}, json_file)
150 173
    json_file.close()
151
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
174
    assert data_sources.get_items(datasource) == [
175
            ('1', 'foo', '1', {'id': 1, 'text': 'foo'}),
176
            ('2', 'bar', '2', {'id': 2, 'text': 'bar'})]
152 177
    assert data_sources.get_structured_items(datasource) == [
153 178
            {'id': 1, 'text': 'foo'}, {'id': 2, 'text': 'bar'}]
154 179

  
......
156 181
    json_file = open(json_file_path, 'w')
157 182
    json.dump({'data': [{'id': '1', 'text': ''}, {'id': '2'}]}, json_file)
158 183
    json_file.close()
159
    assert data_sources.get_items(datasource) == [('1', '', '1'), ('2', '2', '2')]
184
    assert data_sources.get_items(datasource) == [
185
            ('1', '', '1', {'id': '1', 'text': ''}),
186
            ('2', '2', '2', {'id': '2', 'text': '2'})]
160 187
    assert data_sources.get_structured_items(datasource) == [
161 188
            {'id': '1', 'text': ''},
162 189
            {'id': '2', 'text': '2'}]
......
193 220
    register_data_source_function(xxx)
194 221

  
195 222
    datasource = {'type': 'formula', 'value': 'xxx()'}
196
    assert data_sources.get_items(datasource) == [('1', 'foo', '1'), ('2', 'bar', '2')]
223
    assert data_sources.get_items(datasource) == [
224
            ('1', 'foo', '1', {'id': '1', 'text': 'foo'}),
225
            ('2', 'bar', '2', {'id': '2', 'text': 'bar'})]
197 226
    assert data_sources.get_structured_items(datasource) == [
198 227
            {'id': '1', 'text': 'foo'}, {'id': '2', 'text': 'bar'}]
199 228

  
......
243 272
    data_source2 = NamedDataSource.select()[0]
244 273
    assert data_source2.data_source == data_source.data_source
245 274
    assert data_sources.get_items({'type': 'foobar'}) == [
246
        ('uné',) * 3,
247
        ('deux',) * 3,
275
        ('uné', 'uné', 'uné', {'id': 'uné', 'text': 'uné'}),
276
        ('deux', 'deux', 'deux', {'id': 'deux', 'text': 'deux'}),
248 277
    ]
249 278

  
250 279
    NamedDataSource.wipe()
......
258 287
        urllib2.urlopen.return_value.read.return_value = \
259 288
                '{"data": [{"id": 0, "text": "zéro"}, {"id": 1, "text": "uné"}, {"id": 2, "text": "deux"}]}'
260 289
        assert data_sources.get_items({'type': 'foobar'}) == [
261
            ('0', 'zéro', '0'),
262
            ('1', 'uné', '1'),
263
            ('2', 'deux', '2'),
290
            ('0', 'zéro', '0', {"id": 0, "text": "zéro"}),
291
            ('1', 'uné', '1', {"id": 1, "text": "uné"}),
292
            ('2', 'deux', '2', {"id": 2, "text": "deux"}),
264 293
        ]
tests/test_fields.py
155 155

  
156 156
def test_item_render():
157 157
    items_kwargs = []
158
    items_kwargs.append({'items': ['a', 'b', 'c']})
158
    items_kwargs.append({'items': ['aa', 'ab', 'ac']})
159 159
    items_kwargs.append({'data_source': {
160 160
        'type': 'formula',
161
        'value': '''['a', 'b', 'c']'''}})
161
        'value': '''['aa', 'ab', 'ac']'''}})
162 162

  
163 163
    for item_kwargs in items_kwargs:
164 164
        field = fields.ItemField(id='1', label='Foobar', **item_kwargs)
......
220 220

  
221 221
def test_item_render_as_radio():
222 222
    items_kwargs = []
223
    items_kwargs.append({'items': ['a', 'b', 'c']})
223
    items_kwargs.append({'items': ['aa', 'ab', 'ac']})
224 224
    items_kwargs.append({'data_source': {
225 225
        'type': 'formula',
226
        'value': '''['a', 'b', 'c']'''}})
226
        'value': '''['aa', 'ab', 'ac']'''}})
227 227

  
228 228
    for item_kwargs in items_kwargs:
229 229
        field = fields.ItemField(id='1', label='Foobar', show_as_radio=True, **item_kwargs)
......
278 278
        form = Form()
279 279
        field.add_to_form(form)
280 280
        assert str(form.render()).count('"radio"') == 1
281

  
282
def test_items_render():
283
    items_kwargs = []
284
    items_kwargs.append({'items': ['aa', 'ab', 'ac']})
285
    items_kwargs.append({'data_source': {
286
        'type': 'formula',
287
        'value': '''['aa', 'ab', 'ac']'''}})
288

  
289
    for item_kwargs in items_kwargs:
290
        field = fields.ItemsField(id='1', label='Foobar', **item_kwargs)
291
        form = Form()
292
        field.add_to_form(form)
293
        assert str(form.render()).count('type="checkbox"') == 3
294
        assert '>aa<' in str(form.render())
295
        assert '>ab<' in str(form.render())
296
        assert '>ac<' in str(form.render())
tests/test_form_pages.py
3335 3335
    assert 'readonly' in resp.form['f0'].attrs
3336 3336
    assert not 'Check values then click submit.' in resp.body
3337 3337
    assert resp.form['f0'].value == 'foo@localhost'
3338

  
3339
def test_item_field_with_disabled_items(pub):
3340
    user = create_user(pub)
3341
    formdef = create_formdef()
3342
    formdef.data_class().wipe()
3343
    ds = {'type': 'json', 'value': 'http://remote.example.net/json'}
3344
    formdef.fields = [fields.ItemField(id='0', label='string', type='item',
3345
        data_source=ds, display_disabled_items=True)]
3346
    formdef.store()
3347

  
3348
    with mock.patch('urllib2.urlopen') as urlopen:
3349
        data = {'data': [{'id': '1', 'text': 'hello'}, {'id': '2', 'text': 'world'}]}
3350
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3351
        resp = get_app(pub).get('/test/')
3352
        resp.form['f0'] = '1'
3353
        resp.form['f0'] = '2'
3354
        resp = resp.form.submit('submit') # -> validation page
3355
        resp = resp.form.submit('submit') # -> submit
3356
        assert formdef.data_class().select()[0].data['0'] == '2'
3357
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3358

  
3359
    formdef.data_class().wipe()
3360

  
3361
    with mock.patch('urllib2.urlopen') as urlopen:
3362
        data = {'data': [{'id': '1', 'text': 'hello', 'disabled': True}, {'id': '2', 'text': 'world'}]}
3363
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3364
        resp = get_app(pub).get('/test/')
3365
        assert '<option disabled="disabled" value="1">hello</option>' in resp.body
3366
        resp.form['f0'] = '1'
3367
        resp.form['f0'] = '2'
3368
        resp = resp.form.submit('submit') # -> validation page
3369
        resp = resp.form.submit('submit') # -> submit
3370
        assert formdef.data_class().select()[0].data['0'] == '2'
3371
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3372

  
3373
        resp = get_app(pub).get('/test/')
3374
        assert '<option disabled="disabled" value="1">hello</option>' in resp.body
3375
        resp.form['f0'] = '1'
3376
        resp = resp.form.submit('submit') # -> validation page
3377
        assert 'There were errors processing the form' in resp.body
3378

  
3379
    formdef.data_class().wipe()
3380
    formdef.fields = [fields.ItemField(id='0', label='string', type='item', data_source=ds,
3381
        display_disabled_items=False)]
3382
    formdef.store()
3383

  
3384
    with mock.patch('urllib2.urlopen') as urlopen:
3385
        data = {'data': [{'id': '1', 'text': 'hello', 'disabled': True}, {'id': '2', 'text': 'world'}]}
3386
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3387
        resp = get_app(pub).get('/test/')
3388
        assert not '<option disabled="disabled" value="1">hello</option>' in resp.body
3389
        resp.form['f0'] = '2'
3390
        resp = resp.form.submit('submit') # -> validation page
3391
        resp = resp.form.submit('submit') # -> submit
3392
        assert formdef.data_class().select()[0].data['0'] == '2'
3393
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3394

  
3395
    formdef.data_class().wipe()
3396
    formdef.fields = [fields.ItemField(id='0', label='string', type='item',
3397
        data_source=ds, show_as_radio=True, display_disabled_items=True)]
3398
    formdef.store()
3399

  
3400
    with mock.patch('urllib2.urlopen') as urlopen:
3401
        data = {'data': [{'id': '1', 'text': 'hello'}, {'id': '2', 'text': 'world'}]}
3402
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3403
        resp = get_app(pub).get('/test/')
3404
        resp.form['f0'] = '1'
3405
        resp.form['f0'] = '2'
3406
        resp = resp.form.submit('submit') # -> validation page
3407
        resp = resp.form.submit('submit') # -> submit
3408
        assert formdef.data_class().select()[0].data['0'] == '2'
3409
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3410

  
3411
    formdef.data_class().wipe()
3412

  
3413
    with mock.patch('urllib2.urlopen') as urlopen:
3414
        data = {'data': [{'id': '1', 'text': 'hello', 'disabled': True}, {'id': '2', 'text': 'world'}]}
3415
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3416
        resp = get_app(pub).get('/test/')
3417
        assert '<input disabled="disabled" type="radio" name="f0" value="1" />' in resp.body
3418
        resp.form['f0'] = '1'
3419
        resp.form['f0'] = '2'
3420
        resp = resp.form.submit('submit') # -> validation page
3421
        resp = resp.form.submit('submit') # -> submit
3422
        assert formdef.data_class().select()[0].data['0'] == '2'
3423
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3424

  
3425
        resp = get_app(pub).get('/test/')
3426
        assert '<input disabled="disabled" type="radio" name="f0" value="1" />' in resp.body
3427
        resp.form['f0'] = '1'
3428
        resp = resp.form.submit('submit') # -> validation page
3429
        assert 'There were errors processing the form' in resp.body
3430

  
3431
def test_items_field_with_disabled_items(pub):
3432
    user = create_user(pub)
3433
    formdef = create_formdef()
3434
    formdef.data_class().wipe()
3435
    ds = {'type': 'json', 'value': 'http://remote.example.net/json'}
3436
    formdef.fields = [fields.ItemsField(id='0', label='string', type='items',
3437
        data_source=ds, display_disabled_items=True)]
3438
    formdef.store()
3439

  
3440
    with mock.patch('urllib2.urlopen') as urlopen:
3441
        data = {'data': [{'id': '1', 'text': 'hello'}, {'id': '2', 'text': 'world'}]}
3442
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3443
        resp = get_app(pub).get('/test/')
3444
        resp.form['f0$element1'].checked = True
3445
        resp.form['f0$element2'].checked = True
3446
        resp = resp.form.submit('submit') # -> validation page
3447
        resp = resp.form.submit('submit') # -> submit
3448
        assert formdef.data_class().select()[0].data['0'] == ['1', '2']
3449
        assert formdef.data_class().select()[0].data['0_display'] == 'hello, world'
3450

  
3451
    formdef.data_class().wipe()
3452

  
3453
    with mock.patch('urllib2.urlopen') as urlopen:
3454
        data = {'data': [{'id': '1', 'text': 'hello', 'disabled': True}, {'id': '2', 'text': 'world'}]}
3455
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3456
        resp = get_app(pub).get('/test/')
3457
        assert resp.form['f0$element1'].attrs['disabled']
3458
        resp.form['f0$element1'].checked = True
3459
        resp.form['f0$element2'].checked = True
3460
        resp = resp.form.submit('submit') # -> validation page
3461
        resp = resp.form.submit('submit') # -> submit
3462
        assert formdef.data_class().select()[0].data['0'] == ['2']
3463
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
3464

  
3465
    formdef.data_class().wipe()
3466
    formdef.fields = [fields.ItemsField(id='0', label='string', type='items',
3467
        data_source=ds, display_disabled_items=False)]
3468
    formdef.store()
3469

  
3470
    with mock.patch('urllib2.urlopen') as urlopen:
3471
        data = {'data': [{'id': '1', 'text': 'hello', 'disabled': True}, {'id': '2', 'text': 'world'}]}
3472
        urlopen.side_effect = lambda *args: StringIO.StringIO(json.dumps(data))
3473
        resp = get_app(pub).get('/test/')
3474
        assert not 'f0$element1' in resp.form.fields
3475
        resp.form['f0$element2'].checked = True
3476
        resp = resp.form.submit('submit') # -> validation page
3477
        resp = resp.form.submit('submit') # -> submit
3478
        assert formdef.data_class().select()[0].data['0'] == ['2']
3479
        assert formdef.data_class().select()[0].data['0_display'] == 'world'
wcs/admin/forms.py
1178 1178

  
1179 1179
        endpoints = []
1180 1180
        for status in self.formdef.workflow.get_endpoint_status():
1181
            endpoints.append((status.id, status.name))
1181
            endpoints.append((str(status.id), status.name))
1182 1182

  
1183 1183
        form = Form(enctype='multipart/form-data')
1184 1184
        form.add(DateWidget, 'before_request_date',
......
1187 1187
                required=True)
1188 1188
        form.add(CheckboxesWidget, 'endpoints', title=_('Status of the forms to anonymise'),
1189 1189
                value=[endpoint[0] for endpoint in endpoints],
1190
                elements=endpoints,
1190
                options=endpoints,
1191 1191
                inline=False,
1192 1192
                required=True)
1193 1193
        form.add_submit('submit', _('Submit'))
wcs/admin/settings.py
72 72
            methods.insert(0,
73 73
                    ('idp', _('Delegated to SAML identity provider')))
74 74
        form.add(CheckboxesWidget, 'methods', title = _('Methods'),
75
                value = identification_cfg.get('methods'),
76
                elements = methods,
77
                inline = False,
78
                required = True)
75
                value=identification_cfg.get('methods'),
76
                options=methods,
77
                inline=False,
78
                required=True)
79 79

  
80 80
        form.add(CheckboxWidget, 'use_user_hash', title=_('One-way association between user and forms'),
81 81
                value=bool(identification_cfg.get('use_user_hash', False)),
wcs/backoffice/management.py
953 953
                filter_field.required = False
954 954
                options = filter_field.get_options()
955 955
                if options:
956
                    if type(options[0]) in (tuple, list):
957
                        if len(options[0]) == 2:
958
                            options = [(x[0], x[1], x[0]) for x in options]
959
                        options.insert(0, (None, '', None))
960
                    else:
961
                        options.insert(0, '')
956
                    if len(options[0]) == 2:
957
                        options = [(x[0], x[1], x[0]) for x in options]
958
                    options.insert(0, (None, '', ''))
962 959
                    r += SingleSelectWidget(filter_field_key, title=filter_field.label,
963 960
                            options=options, value=filter_field_value,
964 961
                            render_br=False).render()
wcs/data_sources.py
84 84
        return r.getvalue()
85 85

  
86 86

  
87
def get_items(data_source):
87
def get_items(data_source, include_disabled=False):
88 88
    structured_items = get_structured_items(data_source)
89 89
    tupled_items = []
90 90
    for item in structured_items:
91
        tupled_items.append((str(item['id']), str(item['text']), str(item.get('key', item['id']))))
91
        if item.get('disabled') and not include_disabled:
92
            continue
93
        tupled_items.append((str(item['id']),
94
                             str(item['text']),
95
                             str(item.get('key', item['id'])),
96
                             item))
92 97
    return tupled_items
93 98

  
94 99

  
wcs/fields.py
1049 1049
    widget_class = SingleSelectHintWidget
1050 1050
    data_source = {}
1051 1051
    in_filters = False
1052
    display_disabled_items = False
1052 1053

  
1053 1054
    def __init__(self, **kwargs):
1054 1055
        self.items = []
......
1056 1057

  
1057 1058
    def get_options(self):
1058 1059
        if self.data_source:
1059
            return data_sources.get_items(self.data_source)
1060
            return [x[:3] for x in data_sources.get_items(self.data_source)]
1060 1061
        if self.items:
1061
            return self.items[:]
1062
            return [(x, x) for x in self.items]
1062 1063
        return []
1063 1064

  
1064 1065
    def perform_more_widget_changes(self, form, kwargs, edit = True):
......
1066 1067
        if real_data_source and real_data_source.get('type') == 'jsonp':
1067 1068
            kwargs['url'] = real_data_source.get('value')
1068 1069
            self.widget_class = JsonpSingleSelectWidget
1070
        elif self.items:
1071
            kwargs['options'] = self.get_options()
1072
        elif self.data_source:
1073
            items = data_sources.get_items(self.data_source,
1074
                    include_disabled=self.display_disabled_items)
1075
            kwargs['options'] = [x[:3] for x in items if not x[-1].get('disabled')]
1076
            kwargs['options_with_attributes'] = items[:]
1069 1077
        else:
1070 1078
            kwargs['options'] = self.get_options()
1071 1079
        if not kwargs.get('options'):
......
1179 1187
                 hint=_('This will get the available items from an external source.'),
1180 1188
                 required=False,
1181 1189
                 advanced=is_datasource_advanced(self.data_source))
1190
        form.add(CheckboxWidget, 'display_disabled_items',
1191
                title=_('Display disabled items'),
1192
                value=self.display_disabled_items,
1193
                advanced=not(self.display_disabled_items))
1182 1194

  
1183 1195
    def get_admin_attributes(self):
1184 1196
        return WidgetField.get_admin_attributes(self) + ['items',
1185
                'show_as_radio', 'data_source', 'in_filters', 'anonymise']
1197
                'show_as_radio', 'data_source', 'in_filters', 'anonymise',
1198
                'display_disabled_items']
1186 1199

  
1187 1200
    def check_admin_form(self, form):
1188 1201
        items = form.get_widget('items').parse()
......
1226 1239
    items = []
1227 1240
    max_choices = 0
1228 1241
    data_source = {}
1242
    display_disabled_items = False
1229 1243

  
1230 1244
    widget_class = CheckboxesWidget
1231 1245

  
......
1239 1253
        if self.data_source:
1240 1254
            if self._cached_data_source:
1241 1255
                return self._cached_data_source
1242
            self._cached_data_source = data_sources.get_items(self.data_source)
1256
            self._cached_data_source = [x[:3] for x in data_sources.get_items(self.data_source)]
1243 1257
            return self._cached_data_source[:]
1244 1258
        elif self.items:
1245
            return self.items[:]
1259
            return [(x, x) for x in self.items]
1246 1260
        else:
1247 1261
            return []
1248 1262

  
1249 1263
    def perform_more_widget_changes(self, form, kwargs, edit = True):
1250
        kwargs['elements'] = self.get_options()
1264
        kwargs['options'] = self.get_options()
1251 1265
        kwargs['max_choices'] = self.max_choices
1266
        if self.data_source:
1267
            items = data_sources.get_items(self.data_source,
1268
                    include_disabled=self.display_disabled_items)
1269
            kwargs['options'] = [x[:3] for x in items if not x[-1].get('disabled')]
1270
            kwargs['options_with_attributes'] = items[:]
1252 1271

  
1253
        if len(self.get_options()) > 3:
1272
        if len(kwargs['options']) > 3:
1254 1273
            kwargs['inline'] = False
1255 1274

  
1256 1275
    def fill_admin_form(self, form):
......
1268 1287
                 hint=_('This will get the available items from an external source.'),
1269 1288
                 required=False,
1270 1289
                 advanced=is_datasource_advanced(self.data_source))
1290
        form.add(CheckboxWidget, 'display_disabled_items',
1291
                title=_('Display disabled items'),
1292
                value=self.display_disabled_items,
1293
                advanced=not(self.display_disabled_items))
1271 1294

  
1272 1295
    def get_admin_attributes(self):
1273 1296
        return WidgetField.get_admin_attributes(self) + ['items',
1274
                'max_choices', 'data_source', 'anonymise']
1297
                'max_choices', 'data_source', 'anonymise',
1298
                'display_disabled_items']
1275 1299

  
1276 1300
    def check_admin_form(self, form):
1277 1301
        items = form.get_widget('items').parse()
......
1973 1997
            ('sha1', _('SHA1')),
1974 1998
            ]
1975 1999
        form.add(CheckboxesWidget, 'formats', title=_('Storage formats'),
1976
                value=self.formats, elements=formats, inline=True)
2000
                value=self.formats, options=formats, inline=True)
1977 2001
        form.add(IntWidget, 'min_length', title=_('Minimum length'),
1978 2002
                value=self.min_length)
1979 2003
        form.add(IntWidget, 'max_length', title=_('Maximum password length'),
wcs/qommon/form.py
200 200
SubmitWidget.render_content = submit_render_content
201 201

  
202 202

  
203
def radiobuttons_render_content(self):
204
    tags = []
205
    for object, description, key in self.options:
206
        if self.is_selected(object):
207
            checked = 'checked'
208
        else:
209
            checked = None
210
        r = htmltag("input", xml_end=True,
211
                    type="radio",
212
                    name=self.name,
213
                    value=key,
214
                    checked=checked,
215
                    **self.attrs)
216
        tags.append(htmltext('<label>') + r + htmlescape(description) + htmltext('</label>'))
217
    return htmlescape(self.delim).join(tags)
218
RadiobuttonsWidget.render_content = radiobuttons_render_content
203
class RadiobuttonsWidget(quixote.form.RadiobuttonsWidget):
204
    def __init__(self, name, value=None, **kwargs):
205
        self.options_with_attributes = kwargs.pop('options_with_attributes', None)
206
        super(RadiobuttonsWidget, self).__init__(name, value=value, **kwargs)
219 207

  
208
    def render_content(self):
209
        include_disabled = False
210
        options = self.options[:]
211
        if self.options_with_attributes:
212
            options = self.options_with_attributes
213
            include_disabled = True
214

  
215
        tags = []
216
        for option in options:
217
            object, description, key = option[:3]
218
            html_attrs = self.attrs.copy()
219
            if self.is_selected(object):
220
                html_attrs['checked'] = 'checked'
221
            if self.options_with_attributes and option[-1].get('disabled'):
222
                html_attrs['disabled'] = 'disabled'
223
            r = htmltag("input", xml_end=True,
224
                        type="radio",
225
                        name=self.name,
226
                        value=key,
227
                        **html_attrs)
228
            tags.append(htmltext('<label>') + r + htmlescape(description) + htmltext('</label>'))
229
        return htmlescape(self.delim).join(tags)
220 230

  
221 231
def checkbox_render_content(self):
222 232
    attrs = {'id': 'form_' + self.name}
......
1100 1110
class CheckboxesWidget(CompositeWidget):
1101 1111
    readonly = False
1102 1112

  
1103
    def __init__(self, name, value = None, elements = None, **kwargs):
1113
    def __init__(self, name, value=None, options=None, **kwargs):
1104 1114
        CompositeWidget.__init__(self, name, value, **kwargs)
1105 1115
        self.element_names = {}
1106 1116

  
......
1112 1122
        self.inline = kwargs.get('inline', True)
1113 1123
        self.max_choices = int(kwargs.get('max_choices', 0) or 0)
1114 1124

  
1115
        for v in elements:
1116
            if type(v) is tuple:
1117
                title = v[1]
1118
                key = v[0]
1119
                if type(key) is int:
1120
                    name = 'element%d' % v[0]
1121
                elif type(key) in (str, htmltext):
1122
                    name = str('element%s' % v[0])
1123
                    key = str(key)
1124
                else:
1125
                    raise NotImplementedError()
1126
            else:
1127
                title = v
1128
                key = v
1129
                name = 'element%d' % len(self.element_names.keys())
1125
        self.options_with_attributes = kwargs.pop('options_with_attributes', None)
1126
        self.disabled_options = []
1127
        if self.options_with_attributes:
1128
            options = self.options_with_attributes
1129

  
1130
        for option in options:
1131
            key, title = option[:2]
1132
            key = str(key)
1133
            name = 'element%s' % key
1134

  
1135
            element_kwargs = kwargs.copy()
1136
            if self.options_with_attributes and option[-1].get('disabled'):
1137
                element_kwargs['disabled'] = 'disabled'
1138
                self.disabled_options.append(name)
1130 1139

  
1131 1140
            if value and key in value:
1132 1141
                checked = True
1133 1142
            else:
1134 1143
                checked = False
1135
            self.add(CheckboxWidget, name, title = title, value = checked, **kwargs)
1144
            self.add(CheckboxWidget, name, title=title, value=checked, **element_kwargs)
1136 1145
            self.element_names[name] = key
1137 1146

  
1138 1147
    def _parse(self, request):
......
1140 1149
            return
1141 1150
        values = []
1142 1151
        for name in self.element_names:
1152
            if name in self.disabled_options:
1153
                continue
1143 1154
            value = self.get(name)
1144 1155
            if value is True:
1145 1156
                values.append(self.element_names[name])
......
1168 1179
        else:
1169 1180
            r += htmltext('<ul>')
1170 1181
        for widget in self.get_widgets():
1171
            r += htmltext('<li><label>')
1182
            if widget.attrs and 'disabled' in widget.attrs:
1183
                r += htmltext('<li class="disabled"><label>')
1184
            else:
1185
                r += htmltext('<li><label>')
1172 1186
            if self.readonly:
1173 1187
                widget.attrs['disabled'] = 'disabled'
1174 1188
                if widget.value:
......
1533 1547

  
1534 1548

  
1535 1549
class SingleSelectHintWidget(SingleSelectWidget):
1550
    def __init__(self, name, value=None, **kwargs):
1551
        self.options_with_attributes = kwargs.pop('options_with_attributes', None)
1552
        super(SingleSelectHintWidget, self).__init__(name, value=value, **kwargs)
1536 1553

  
1537 1554
    def separate_hint(self):
1538 1555
        return (self.hint and len(self.hint) > 80)
......
1543 1560
            attrs.update(self.attrs)
1544 1561
        tags = [htmltag('select', name=self.name, **attrs)]
1545 1562
        options = self.options[:]
1563
        include_disabled = False
1564
        if self.options_with_attributes:
1565
            options = self.options_with_attributes
1566
            include_disabled = True
1546 1567
        if not self.separate_hint() and self.hint:
1547 1568
            r = htmltag('option', value='', selected=None)
1548 1569
            tags.append(r + htmlescape(self.hint) + htmltext('</option>'))
......
1550 1571
                # hint has been put as first element, skip the default empty
1551 1572
                # value.
1552 1573
                options = self.options[1:]
1553
        for object, description, key in options:
1574
        for option in options:
1575
            object, description, key = option[:3]
1576
            html_attrs = {}
1577
            html_attrs['value'] = key
1554 1578
            if self.is_selected(object):
1555
                selected = 'selected'
1556
            else:
1557
                selected = None
1579
                html_attrs['selected'] = 'selected'
1580
            if self.options_with_attributes and option[-1].get('disabled'):
1581
                html_attrs['disabled'] = 'disabled'
1558 1582
            if description is None:
1559 1583
                description = ''
1560
            r = htmltag('option', value=key, selected=selected)
1584
            r = htmltag('option', **html_attrs)
1561 1585
            tags.append(r + htmlescape(description) + htmltext('</option>'))
1562 1586
        tags.append(htmltext('</select>'))
1563 1587
        return htmltext('\n').join(tags)
wcs/qommon/static/css/qommon.css
269 269
	clear: both;
270 270
}
271 271

  
272
div.CheckboxesWidget div.content ul li.disabled {
273
	color: #aaa;
274
}
275

  
272 276
div.CheckboxesWidget div.content ul {
273 277
	list-style: none;
274 278
	padding: 0;
275
-