From 5e5fb23a5075f3c8cb80aee0fcbc2bbc2a0b98a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Wed, 20 Jan 2016 18:42:24 +0100 Subject: [PATCH] forms: add post-conditions to page fields (#8962) --- wcs/fields.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++-------- wcs/forms/root.py | 27 +++++++++++++++++++++++++-- wcs/qommon/form.py | 24 +++++++++++++++++++++--- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/wcs/fields.py b/wcs/fields.py index b1cd313..fcb9cf8 100644 --- a/wcs/fields.py +++ b/wcs/fields.py @@ -1257,29 +1257,59 @@ class ItemsField(WidgetField): register_field_class(ItemsField) + +class PostConditionsRowWidget(CompositeWidget): + def __init__(self, name, value=None, **kwargs): + CompositeWidget.__init__(self, name, value, **kwargs) + if not value: + value = {} + self.add(StringWidget, name='condition', title=_('Condition'), + value=value.get('condition'), size=50) + self.add(StringWidget, name='error_message', title=_('Error Message'), + value=value.get('error_message'), size=50) + + def _parse(self, request): + if self.get('condition') or self.get('error_message'): + self.value = { + 'condition': self.get('condition'), + 'error_message': self.get('error_message') + } + else: + self.value = None + + +class PostConditionsTableWidget(WidgetListAsTable): + readonly = False + def __init__(self, name, **kwargs): + super(PostConditionsTableWidget, self).__init__(name, + element_type=PostConditionsRowWidget, **kwargs) + + class PageField(Field): key = 'page' description = N_('New Page') condition = None + post_conditions = None def fill_admin_form(self, form): form.add(StringWidget, 'label', title = _('Label'), value = self.label, required = True, size = 50) form.add(StringWidget, 'condition', title = _('Condition'), value = self.condition, required = False, size = 50) + form.add(PostConditionsTableWidget, 'post_conditions', + title=_('Post Conditions'), + value=self.post_conditions, + advanced=not(self.post_conditions)) def get_admin_attributes(self): - return Field.get_admin_attributes(self) + ['condition'] + return Field.get_admin_attributes(self) + ['condition', 'post_conditions'] def add_to_view_form(self, *args): pass - def is_visible(self, dict, formdef): - if dict is None: - return True - - if not self.condition: + def evaluate_condition(self, dict, formdef, condition): + if not condition: return True # create variables with values currently being evaluated, not yet @@ -1301,14 +1331,22 @@ class PageField(Field): data = get_publisher().substitutions.get_context_variables() try: - if eval(self.condition, get_publisher().get_global_eval_dict(), data): + if eval(condition, get_publisher().get_global_eval_dict(), data): return True except Exception, e: get_logger().warn('failed to evaluate condition "%s" (%r)' % (self.condition, e)) - return True + raise RuntimeError() return False + def is_visible(self, dict, formdef): + if dict is None: + return True + try: + return self.evaluate_condition(dict, formdef, self.condition) + except RuntimeError: + return True + register_field_class(PageField) diff --git a/wcs/forms/root.py b/wcs/forms/root.py index d9b405e..8fbde81 100644 --- a/wcs/forms/root.py +++ b/wcs/forms/root.py @@ -330,7 +330,7 @@ class FormPage(Directory): return self.page(0) - def page(self, page_no, page_change=True, log_detail=None): + def page(self, page_no, page_change=True, log_detail=None, page_error_messages=None): r = TemplateIO(html=True) displayed_fields = [] @@ -345,6 +345,8 @@ class FormPage(Directory): self.feed_current_data(magictoken) form = self.formdef.create_form(page_no, displayed_fields) + if page_error_messages: + form.add_global_errors(page_error_messages) if getattr(session, 'ajax_form_token', None): form.add_hidden('_ajax_form_token', session.ajax_form_token) if get_request().is_in_backoffice(): @@ -661,6 +663,26 @@ class FormPage(Directory): filled = self.save_draft(form_data, page_no) return redirect(filled.get_url().rstrip('/')) + page_error_messages = [] + if form.get_submit() == 'submit': + pages = [x for x in self.formdef.fields if x.type == 'page'] + try: + page = pages[page_no] + post_conditions = page.post_conditions or [] + except IndexError: + post_conditions = [] + form_data = session.get_by_magictoken(magictoken, {}) + for i, post_condition in enumerate(post_conditions): + condition = post_condition.get('condition') + error_message = post_condition.get('error_message') + try: + if not page.evaluate_condition(form_data, self.formdef, condition): + form.add(HiddenErrorWidget, 'post_condition%d' % i) + form.set_error('post_condition%d' % i, 'error') + page_error_messages.append(error_message) + except RuntimeError: + pass + # form.get_submit() returns the name of the clicked button, and # it will return True if the form has been submitted, but not # by clicking on a submit widget; for example if an "add row" @@ -671,7 +693,8 @@ class FormPage(Directory): # page hidden field had an error in its submission), in # that case we just fall back to the first page. page_no = 0 - return self.page(page_no, page_change=False) + return self.page(page_no, page_change=False, + page_error_messages=page_error_messages) form_data = session.get_by_magictoken(magictoken, {}) data = self.formdef.get_data(form) diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py index 5fc3adb..c7551dd 100644 --- a/wcs/qommon/form.py +++ b/wcs/qommon/form.py @@ -261,6 +261,7 @@ class Form(QuixoteForm): if kwargs.get('advanced_label'): self.advanced_label = kwargs.pop('advanced_label') QuixoteForm.__init__(self, *args, **kwargs) + self.global_error_messages = None def keep_referer(self): self.add(HiddenWidget, '__keep_referer', @@ -297,6 +298,9 @@ class Form(QuixoteForm): l.append(self.captcha) return l + def add_global_errors(self, error_messages): + self.global_error_messages = error_messages + def _get_default_action(self): if get_request().get_header('x-popup') == 'true': # do not leave action empty for popups, as they get embedded into @@ -331,12 +335,21 @@ class Form(QuixoteForm): return htmltext('
%s
' % _(self.info)) def _render_error_notice(self): - return htmltext('
%s
' % _( - QuixoteForm._render_error_notice(self))) + errors = [] + if self.has_errors(): + errors.append(_(QuixoteForm._render_error_notice(self))) + if self.global_error_messages: + errors.extend(self.global_error_messages) + t = TemplateIO(html=True) + t += htmltext('
') + for error in errors: + t += htmltext('

%s

') % error + t += htmltext('
') + return t.getvalue() def _render_body(self): r = TemplateIO(html=True) - if self.has_errors(): + if self.has_errors() or self.global_error_messages: r += self._render_error_notice() if self.info: r += self._render_info_notice() @@ -2086,3 +2099,8 @@ class MapWidget(CompositeWidget): def _parse(self, request): CompositeWidget._parse(self, request) self.value = self.get('latlng') + + +class HiddenErrorWidget(HiddenWidget): + def set_error(self, error): + Widget.set_error(self, error) -- 2.7.0.rc3