0001-general-add-soft-hard-limits-to-number-of-field-6006.patch
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->card_card_2_2</title>') == 0 |
851 | 851 |
assert resp.text.count('<title>card_card_2_1->card_card_2_2</title>') == 8 |
852 | 852 |
assert resp.text.count('<title>card_card_3_1->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 |
- |