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 | ||
---|---|---|
2590 | 2590 |
formdef.store() |
2591 | 2591 | |
2592 | 2592 |
app = login(get_app(pub)) |
2593 |
resp = app.get('/backoffice/forms/1/') |
|
2594 |
resp = resp.click(href='fields/') |
|
2595 |
assert 'more than 500 fields' not in resp.text |
|
2593 |
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id) |
|
2594 |
assert 'more than 200 fields' not in resp.text |
|
2596 | 2595 |
assert 'first field should be of type "page"' not in resp.text |
2597 | 2596 | |
2598 | 2597 |
formdef.fields.append(fields.PageField(id='1000', label='page', type='page')) |
2599 | 2598 |
formdef.store() |
2600 |
resp = app.get('/backoffice/forms/1/') |
|
2601 |
resp = resp.click(href='fields/', index=0) |
|
2602 |
assert 'more than 500 fields' not in resp.text |
|
2599 |
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id) |
|
2600 |
assert 'more than 200 fields' not in resp.text |
|
2603 | 2601 |
assert 'first field should be of type "page"' in resp.text |
2602 |
assert '<div id="new-field"><h3>New Field</h3>' in resp.text |
|
2604 | 2603 | |
2605 | 2604 |
formdef.fields.extend( |
2606 |
[fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 510)]
|
|
2605 |
[fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(10, 210)]
|
|
2607 | 2606 |
) |
2608 | 2607 |
formdef.store() |
2609 |
resp = app.get('/backoffice/forms/1/') |
|
2610 |
resp = resp.click(href='fields/', index=0) |
|
2611 |
assert 'more than 500 fields' in resp.text |
|
2608 |
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id) |
|
2609 |
assert 'more than 200 fields' in resp.text |
|
2612 | 2610 |
assert 'first field should be of type "page"' in resp.text |
2611 |
assert '>Duplicate<' in resp.text |
|
2612 | ||
2613 |
formdef.fields.extend( |
|
2614 |
[fields.StringField(id='%d' % i, label='field %d' % i, type='string') for i in range(210, 310)] |
|
2615 |
) |
|
2616 |
formdef.store() |
|
2617 |
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id) |
|
2618 |
assert 'This form contains 310 fields.' in resp.text |
|
2619 |
assert 'no new fields can be added.' in resp.text |
|
2620 |
assert 'first field should be of type "page"' in resp.text |
|
2621 |
assert '<div id="new-field"><h3>New Field</h3>' not in resp.text |
|
2622 |
assert '>Duplicate<' not in resp.text |
|
2623 | ||
2624 |
if not pub.site_options.has_section('options'): |
|
2625 |
pub.site_options.add_section('options') |
|
2626 |
pub.site_options.set('options', 'ignore-hard-limits', 'true') |
|
2627 |
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: |
|
2628 |
pub.site_options.write(fd) |
|
2629 | ||
2630 |
resp = app.get('/backoffice/forms/%s/fields/' % formdef.id) |
|
2631 |
assert 'no new fields should be added.' in resp.text |
|
2632 |
assert '<div id="new-field"><h3>New Field</h3>' in resp.text |
|
2633 |
assert '>Duplicate<' in resp.text |
|
2613 | 2634 | |
2614 | 2635 |
FormDef.wipe() |
2615 | 2636 |
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 | ||
---|---|---|
17 | 17 |
import copy |
18 | 18 |
import json |
19 | 19 | |
20 |
from quixote import get_request, get_response, get_session, redirect |
|
20 |
from quixote import get_publisher, get_request, get_response, get_session, redirect
|
|
21 | 21 |
from quixote.directory import Directory |
22 | 22 |
from quixote.html import TemplateIO, htmlescape, htmltext |
23 | 23 | |
... | ... | |
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) |
... | ... | |
309 | 313 |
r = TemplateIO(html=True) |
310 | 314 | |
311 | 315 |
r += self.index_top() |
316 |
ignore_hard_limits = get_publisher().has_site_option('ignore-hard-limits') |
|
312 | 317 | |
313 | 318 |
if self.objectdef.fields: |
314 |
if len(self.objectdef.fields) > 500:
|
|
319 |
if len(self.objectdef.fields) >= self.fields_count_total_hard_limit:
|
|
315 | 320 |
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 |
) |
|
321 |
r += htmltext(self.field_count_message % len(self.objectdef.fields)) |
|
322 |
r += htmltext(' ') |
|
323 |
if ignore_hard_limits: |
|
324 |
r += htmltext(_('It is over system limits and no new fields should be added.')) |
|
325 |
else: |
|
326 |
r += htmltext(_('It is over system limits and no new fields can be added.')) |
|
327 |
r += htmltext('</div>') |
|
328 |
elif len(self.objectdef.fields) > self.fields_count_total_soft_limit: |
|
329 |
r += htmltext('<div class="errornotice">') |
|
330 |
r += htmltext(self.field_over_count_message % self.fields_count_total_soft_limit) |
|
331 |
r += htmltext(' ') |
|
332 |
r += htmltext(_('It is close to the system limits and no new fields should be added.')) |
|
322 | 333 |
r += htmltext('</div>') |
323 | 334 | |
324 | 335 |
if [x for x in self.objectdef.fields if x.type == 'page']: |
... | ... | |
404 | 415 |
) |
405 | 416 |
r += command_icon('%s/' % field.id, 'edit') |
406 | 417 |
if not self.objectdef.is_readonly(): |
407 |
r += command_icon('%s/duplicate' % field.id, 'duplicate', popup=(field.type == 'page')) |
|
418 |
if len(self.objectdef.fields) < self.fields_count_total_hard_limit or ignore_hard_limits: |
|
419 |
r += command_icon( |
|
420 |
'%s/duplicate' % field.id, 'duplicate', popup=(field.type == 'page') |
|
421 |
) |
|
408 | 422 |
r += command_icon('%s/delete' % field.id, 'remove', popup=True) |
409 | 423 |
r += htmltext('</p></li>') |
410 | 424 |
r += htmltext('</ul>') |
... | ... | |
424 | 438 | |
425 | 439 |
def get_new_field_form(self, page_id): |
426 | 440 |
r = TemplateIO(html=True) |
441 |
ignore_hard_limits = get_publisher().has_site_option('ignore-hard-limits') |
|
442 | ||
443 |
if len(self.objectdef.fields) >= self.fields_count_total_hard_limit: |
|
444 |
if ignore_hard_limits: |
|
445 |
r += htmltext('<div class="errornotice"><p>%s %s</p></div>') % ( |
|
446 |
self.field_count_message % self.fields_count_total_hard_limit, |
|
447 |
_('It is over system limits and no new fields should be added.'), |
|
448 |
) |
|
449 |
else: |
|
450 |
r += htmltext('<div class="errornotice"><p>%s %s</p></div>') % ( |
|
451 |
self.field_count_message % self.fields_count_total_hard_limit, |
|
452 |
_('It is over system limits and no new fields can be added.'), |
|
453 |
) |
|
454 |
return r.getvalue() |
|
455 | ||
427 | 456 |
r += htmltext('<div id="new-field">') |
428 | 457 |
r += htmltext('<h3>%s</h3>') % _('New Field') |
429 | 458 |
get_request().form = None # ignore the eventual ?page=x |
wcs/admin/forms.py | ||
---|---|---|
628 | 628 |
formdef_default_workflow = '_default' |
629 | 629 |
section = 'forms' |
630 | 630 |
options_directory_class = OptionsDirectory |
631 |
fields_directory_class = AdminFieldsDirectory |
|
631 | 632 | |
632 | 633 |
delete_message = _('You are about to irrevocably delete this form.') |
633 | 634 |
delete_title = _('Deleting Form:') |
... | ... | |
647 | 648 |
raise TraversalError() |
648 | 649 |
self.formdefui = self.formdef_ui_class(self.formdef) |
649 | 650 |
get_response().breadcrumb.append((component + '/', self.formdef.name)) |
650 |
self.fields = AdminFieldsDirectory(self.formdef)
|
|
651 |
self.fields = self.fields_directory_class(self.formdef)
|
|
651 | 652 |
self.fields.html_top = self.html_top |
652 | 653 |
self.role = WorkflowRoleDirectory(self.formdef) |
653 | 654 |
self.role.html_top = self.html_top |
wcs/admin/workflows.py | ||
---|---|---|
1051 | 1051 |
field_var_prefix = 'form_option_' |
1052 | 1052 |
readonly_message = _('This workflow is readonly.') |
1053 | 1053 | |
1054 |
field_count_message = _('This workflow contains %d variables.') |
|
1055 |
field_over_count_message = _('This workflow contains more than %d variables.') |
|
1056 |
fields_count_total_soft_limit = 20 |
|
1057 |
fields_count_total_hard_limit = 30 |
|
1058 | ||
1054 | 1059 |
def index_top(self): |
1055 | 1060 |
r = TemplateIO(html=True) |
1056 | 1061 |
r += htmltext('<h2>%s - %s - %s</h2>') % (_('Workflow'), self.objectdef.name, _('Variables')) |
... | ... | |
1073 | 1078 |
blacklisted_attributes = ['condition'] |
1074 | 1079 |
field_var_prefix = 'form_var_' |
1075 | 1080 |
readonly_message = _('This workflow is readonly.') |
1081 |
field_count_message = _('This workflow contains %d backoffice fields.') |
|
1082 |
field_over_count_message = _('This workflow contains more than %d backoffice fields.') |
|
1083 |
fields_count_total_soft_limit = 30 |
|
1084 |
fields_count_total_hard_limit = 45 |
|
1076 | 1085 | |
1077 | 1086 |
def index_top(self): |
1078 | 1087 |
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 |
- |