Projet

Général

Profil

0001-general-add-soft-hard-limits-to-number-of-field-6006.patch

Frédéric Péters, 04 mars 2022 16:28

Télécharger (14,7 ko)

Voir les différences:

Subject: [PATCH] general: add soft/hard limits to number of field (#60061)

 tests/admin_pages/test_block.py | 34 ++++++++++++++++++++++++++
 tests/admin_pages/test_card.py  | 43 +++++++++++++++++++++++++++++++++
 tests/admin_pages/test_form.py  | 29 ++++++++++++++--------
 wcs/admin/blocks.py             |  5 ++++
 wcs/admin/fields.py             | 33 +++++++++++++++++++------
 wcs/admin/forms.py              |  3 ++-
 wcs/admin/workflows.py          |  9 +++++++
 wcs/backoffice/cards.py         |  9 ++++++-
 wcs/wf/form.py                  |  2 ++
 9 files changed, 147 insertions(+), 20 deletions(-)
tests/admin_pages/test_block.py
359 359
    resp = resp.forms[0].submit()
360 360
    block.refresh_from_storage()
361 361
    assert not block.category_id
362

  
363

  
364
def test_block_edit_field_warnings(pub):
365
    create_superuser(pub)
366

  
367
    BlockDef.wipe()
368
    blockdef = BlockDef()
369
    blockdef.name = 'block title'
370
    blockdef.fields = [
371
        fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(1, 10)
372
    ]
373
    blockdef.store()
374

  
375
    app = login(get_app(pub))
376
    resp = app.get('/backoffice/forms/blocks/%s/' % blockdef.id)
377
    assert 'more than 20 fields' not in resp.text
378

  
379
    blockdef.fields.extend(
380
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 21)]
381
    )
382
    blockdef.store()
383
    resp = app.get('/backoffice/forms/blocks/%s/' % blockdef.id)
384
    assert 'more than 20 fields' not in resp.text
385
    assert '<div id="new-field"><h3>New Field</h3>' in resp.text
386
    assert '>Duplicate<' in resp.text
387

  
388
    blockdef.fields.extend(
389
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(21, 31)]
390
    )
391
    blockdef.store()
392
    resp = app.get('/backoffice/forms/blocks/%s/' % blockdef.id)
393
    assert 'This block of fields contains 30 fields.' in resp.text
394
    assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
395
    assert '>Duplicate<' not in resp.text
tests/admin_pages/test_card.py
850 850
    assert resp.text.count('<title>card_card_1_1&#45;&gt;card_card_2_2</title>') == 0
851 851
    assert resp.text.count('<title>card_card_2_1&#45;&gt;card_card_2_2</title>') == 8
852 852
    assert resp.text.count('<title>card_card_3_1&#45;&gt;card_card_3_2</title>') == 0
853

  
854

  
855
def test_card_edit_field_warnings(pub):
856
    create_superuser(pub)
857

  
858
    CardDef.wipe()
859
    carddef = CardDef()
860
    carddef.name = 'card title'
861
    carddef.fields = [
862
        fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(1, 10)
863
    ]
864
    carddef.store()
865

  
866
    app = login(get_app(pub))
867
    resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
868
    assert 'more than 200 fields' not in resp.text
869
    assert 'first field should be of type "page"' not in resp.text
870

  
871
    carddef.fields.append(fields.PageField(id='1000', label='page', type='page'))
872
    carddef.store()
873
    resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
874
    assert 'more than 200 fields' not in resp.text
875
    assert 'first field should be of type "page"' in resp.text
876
    assert '<div id="new-field"><h3>New Field</h3>' in resp.text
877

  
878
    carddef.fields.extend(
879
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 210)]
880
    )
881
    carddef.store()
882
    resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
883
    assert 'more than 200 fields' in resp.text
884
    assert 'first field should be of type "page"' in resp.text
885
    assert '>Duplicate<' in resp.text
886

  
887
    carddef.fields.extend(
888
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(210, 310)]
889
    )
890
    carddef.store()
891
    resp = app.get('/backoffice/cards/%s/fields/' % carddef.id)
892
    assert 'This card model contains 310 fields.' in resp.text
893
    assert 'first field should be of type "page"' in resp.text
894
    assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
895
    assert '>Duplicate<' not in resp.text
tests/admin_pages/test_form.py
2438 2438
    formdef.store()
2439 2439

  
2440 2440
    app = login(get_app(pub))
2441
    resp = app.get('/backoffice/forms/1/')
2442
    resp = resp.click(href='fields/')
2443
    assert 'more than 500 fields' not in resp.text
2441
    resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
2442
    assert 'more than 200 fields' not in resp.text
2444 2443
    assert 'first field should be of type "page"' not in resp.text
2445 2444

  
2446 2445
    formdef.fields.append(fields.PageField(id='1000', label='page', type='page'))
2447 2446
    formdef.store()
2448
    resp = app.get('/backoffice/forms/1/')
2449
    resp = resp.click(href='fields/', index=0)
2450
    assert 'more than 500 fields' not in resp.text
2447
    resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
2448
    assert 'more than 200 fields' not in resp.text
2451 2449
    assert 'first field should be of type "page"' in resp.text
2450
    assert '<div id="new-field"><h3>New Field</h3>' in resp.text
2452 2451

  
2453 2452
    formdef.fields.extend(
2454
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 510)]
2453
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 210)]
2455 2454
    )
2456 2455
    formdef.store()
2457
    resp = app.get('/backoffice/forms/1/')
2458
    resp = resp.click(href='fields/', index=0)
2459
    assert 'more than 500 fields' in resp.text
2456
    resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
2457
    assert 'more than 200 fields' in resp.text
2458
    assert 'first field should be of type "page"' in resp.text
2459
    assert '>Duplicate<' in resp.text
2460

  
2461
    formdef.fields.extend(
2462
        [fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(210, 310)]
2463
    )
2464
    formdef.store()
2465
    resp = app.get('/backoffice/forms/%s/fields/' % formdef.id)
2466
    assert 'This form contains 310 fields.' in resp.text
2460 2467
    assert 'first field should be of type "page"' in resp.text
2468
    assert '<div id="new-field"><h3>New Field</h3>' not in resp.text
2469
    assert '>Duplicate<' not in resp.text
2461 2470

  
2462 2471
    FormDef.wipe()
2463 2472

  
wcs/admin/blocks.py
53 53
    support_import = False
54 54
    readonly_message = _('This block of fields is readonly.')
55 55

  
56
    field_count_message = _('This block of fields contains %d fields.')
57
    field_over_count_message = _('This block of fields contains more than %d fields.')
58
    fields_count_total_soft_limit = 20
59
    fields_count_total_hard_limit = 30
60

  
56 61
    def __init__(self, section='forms', *args, **kwargs):
57 62
        if kwargs.pop('component', None):  # snapshot
58 63
            kwargs['objectdef'] = kwargs.pop('instance')
wcs/admin/fields.py
279 279
    page_id = None
280 280
    field_var_prefix = '..._'
281 281
    readonly_message = _('The fields are readonly.')
282
    field_count_message = _('This form contains %d fields.')
283
    field_over_count_message = _('This form contains more than %d fields.')
282 284

  
283 285
    support_import = True
286
    fields_count_total_soft_limit = 200
287
    fields_count_total_hard_limit = 300
284 288

  
285 289
    def html_top(self, title, *args, **kwargs):
286 290
        html_top(self.section, title, *args, **kwargs)
......
311 315
        r += self.index_top()
312 316

  
313 317
        if self.objectdef.fields:
314
            if len(self.objectdef.fields) > 500:
318
            if len(self.objectdef.fields) >= self.fields_count_total_hard_limit:
315 319
                r += htmltext('<div class="errornotice">')
316
                r += htmltext(
317
                    _(
318
                        'This form contains more than 500 fields. '
319
                        'It is close to the database limits and no new fields should be added.'
320
                    )
321
                )
320
                r += htmltext(self.field_count_message % len(self.objectdef.fields))
321
                r += htmltext(' ')
322
                r += htmltext(_('It is over system limits and no new fields can be added.'))
323
                r += htmltext('</div>')
324
            elif len(self.objectdef.fields) > self.fields_count_total_soft_limit:
325
                r += htmltext('<div class="errornotice">')
326
                r += htmltext(self.field_over_count_message % self.fields_count_total_soft_limit)
327
                r += htmltext(' ')
328
                r += htmltext(_('It is close to the system limits and no new fields should be added.'))
322 329
                r += htmltext('</div>')
323 330

  
324 331
            if [x for x in self.objectdef.fields if x.type == 'page']:
......
404 411
                        )
405 412
                    r += command_icon('%s/' % field.id, 'edit')
406 413
                if not self.objectdef.is_readonly():
407
                    r += command_icon('%s/duplicate' % field.id, 'duplicate', popup=(field.type == 'page'))
414
                    if len(self.objectdef.fields) < self.fields_count_total_hard_limit:
415
                        r += command_icon(
416
                            '%s/duplicate' % field.id, 'duplicate', popup=(field.type == 'page')
417
                        )
408 418
                    r += command_icon('%s/delete' % field.id, 'remove', popup=True)
409 419
                r += htmltext('</p></li>')
410 420
            r += htmltext('</ul>')
......
424 434

  
425 435
    def get_new_field_form(self, page_id):
426 436
        r = TemplateIO(html=True)
437
        if len(self.objectdef.fields) >= self.fields_count_total_hard_limit:
438
            r += htmltext('<div class="errornotice"><p>%s %s</p></div>') % (
439
                self.field_count_message % self.fields_count_total_hard_limit,
440
                _('It is over database limits and no new fields can be added.'),
441
            )
442
            return r.getvalue()
443

  
427 444
        r += htmltext('<div id="new-field">')
428 445
        r += htmltext('<h3>%s</h3>') % _('New Field')
429 446
        get_request().form = None  # ignore the eventual ?page=x
wcs/admin/forms.py
581 581
    formdef_default_workflow = '_default'
582 582
    section = 'forms'
583 583
    options_directory_class = OptionsDirectory
584
    fields_directory_class = AdminFieldsDirectory
584 585

  
585 586
    delete_message = _('You are about to irrevocably delete this form.')
586 587
    delete_title = _('Deleting Form:')
......
600 601
            raise TraversalError()
601 602
        self.formdefui = self.formdef_ui_class(self.formdef)
602 603
        get_response().breadcrumb.append((component + '/', self.formdef.name))
603
        self.fields = AdminFieldsDirectory(self.formdef)
604
        self.fields = self.fields_directory_class(self.formdef)
604 605
        self.fields.html_top = self.html_top
605 606
        self.role = WorkflowRoleDirectory(self.formdef)
606 607
        self.role.html_top = self.html_top
wcs/admin/workflows.py
1030 1030
    field_var_prefix = 'form_option_'
1031 1031
    readonly_message = _('This workflow is readonly.')
1032 1032

  
1033
    field_count_message = _('This workflow contains %d variables.')
1034
    field_over_count_message = _('This workflow contains more than %d variables.')
1035
    fields_count_total_soft_limit = 20
1036
    fields_count_total_hard_limit = 30
1037

  
1033 1038
    def index_top(self):
1034 1039
        r = TemplateIO(html=True)
1035 1040
        r += htmltext('<h2>%s - %s - %s</h2>') % (_('Workflow'), self.objectdef.name, _('Variables'))
......
1052 1057
    blacklisted_attributes = ['condition']
1053 1058
    field_var_prefix = 'form_var_'
1054 1059
    readonly_message = _('This workflow is readonly.')
1060
    field_count_message = _('This workflow contains %d backoffice fields.')
1061
    field_over_count_message = _('This workflow contains more than %d backoffice fields.')
1062
    fields_count_total_soft_limit = 30
1063
    fields_count_total_hard_limit = 45
1055 1064

  
1056 1065
    def index_top(self):
1057 1066
        r = TemplateIO(html=True)
wcs/backoffice/cards.py
19 19

  
20 20
from wcs.admin import utils
21 21
from wcs.admin.categories import CardDefCategoriesDirectory
22
from wcs.admin.forms import FormDefPage, FormDefUI, FormsDirectory, OptionsDirectory
22
from wcs.admin.forms import AdminFieldsDirectory, FormDefPage, FormDefUI, FormsDirectory, OptionsDirectory
23 23
from wcs.carddef import CardDef, get_cards_graph
24 24
from wcs.categories import CardDefCategory
25 25
from wcs.workflows import Workflow
......
41 41
    section = 'cards'
42 42

  
43 43

  
44
class CardDefFieldsDirectory(AdminFieldsDirectory):
45
    field_count_message = _('This card model contains %d fields.')
46
    field_over_count_message = _('This card model contains more than %d fields.')
47
    readonly_message = _('This card model is readonly.')
48

  
49

  
44 50
class CardDefPage(FormDefPage):
45 51
    formdef_class = CardDef
46 52
    formdef_export_prefix = 'card'
......
49 55
    section = 'cards'
50 56

  
51 57
    options_directory_class = CardDefOptionsDirectory
58
    fields_directory_class = CardDefFieldsDirectory
52 59

  
53 60
    delete_message = _('You are about to irrevocably delete this card model.')
54 61
    delete_title = _('Deleting Card Model:')
wcs/wf/form.py
100 100
    support_import = False
101 101
    blacklisted_types = ['page', 'computed']
102 102
    field_def_page_class = WorkflowFormFieldDefPage
103
    fields_count_total_soft_limit = 20
104
    fields_count_total_hard_limit = 30
103 105

  
104 106

  
105 107
class FormWorkflowStatusItem(WorkflowStatusItem):
106
-