src/pyams_default_theme/shared/form/__init__.py
changeset 460 77cec5c9ef84
parent 459 07781aca5cd7
child 469 b8d287df9d34
equal deleted inserted replaced
459:07781aca5cd7 460:77cec5c9ef84
    11 #
    11 #
    12 
    12 
    13 import requests
    13 import requests
    14 from pyramid.csrf import get_csrf_token
    14 from pyramid.csrf import get_csrf_token
    15 from z3c.form import button, field
    15 from z3c.form import button, field
       
    16 from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
    16 from z3c.form.interfaces import HIDDEN_MODE
    17 from z3c.form.interfaces import HIDDEN_MODE
    17 from zope.interface import Interface, Invalid
    18 from zope.interface import Interface, Invalid, alsoProvides
    18 from zope.schema import TextLine
    19 from zope.schema import Bool, TextLine
    19 
    20 
    20 from pyams_content.shared.form import IFormFieldContainer, IFormFieldContainerTarget, IWfForm
    21 from pyams_content.shared.form import IFormFieldContainer, IFormFieldContainerTarget, IWfForm
       
    22 from pyams_content.shared.form.interfaces import IFormFieldDataConverter
    21 from pyams_default_theme.component.paragraph.interfaces import IParagraphContainerPortletRenderer
    23 from pyams_default_theme.component.paragraph.interfaces import IParagraphContainerPortletRenderer
    22 from pyams_default_theme.interfaces import IContentTitle
    24 from pyams_default_theme.interfaces import IContentTitle
    23 from pyams_default_theme.layer import IPyAMSDefaultLayer
    25 from pyams_default_theme.layer import IPyAMSDefaultLayer
    24 from pyams_form.form import AddForm
    26 from pyams_form.form import AddForm
    25 from pyams_form.help import FormHelp
    27 from pyams_form.help import FormHelp
    29 from pyams_portal.views import PortalContextIndexPage
    31 from pyams_portal.views import PortalContextIndexPage
    30 from pyams_skin.layer import IPyAMSLayer
    32 from pyams_skin.layer import IPyAMSLayer
    31 from pyams_template.template import template_config
    33 from pyams_template.template import template_config
    32 from pyams_utils.adapter import adapter_config
    34 from pyams_utils.adapter import adapter_config
    33 from pyams_utils.interfaces import PUBLIC_PERMISSION, VIEW_PERMISSION
    35 from pyams_utils.interfaces import PUBLIC_PERMISSION, VIEW_PERMISSION
       
    36 from pyams_utils.interfaces.data import IObjectData
       
    37 from pyams_utils.text import text_to_html
    34 from pyams_utils.url import relative_url
    38 from pyams_utils.url import relative_url
    35 from pyams_viewlet.viewlet import ViewContentProvider, Viewlet, viewlet_config
    39 from pyams_viewlet.viewlet import ViewContentProvider, Viewlet, viewlet_config
    36 
    40 
    37 
    41 
    38 __docformat__ = 'restructuredtext'
    42 __docformat__ = 'restructuredtext'
    40 from pyams_default_theme import _
    44 from pyams_default_theme import _
    41 
    45 
    42 
    46 
    43 CSRF_FIELD_NAME = 'csrf_token'
    47 CSRF_FIELD_NAME = 'csrf_token'
    44 RECAPTCHA_FIELD_NAME = 'g-recaptcha-response'
    48 RECAPTCHA_FIELD_NAME = 'g-recaptcha-response'
       
    49 RGPD_CONSENT_FIELD_NAME = 'rgpd_consent'
    45 
    50 
    46 MISSING_TOKEN_ERROR = _("Missing recaptcha token!")
    51 MISSING_TOKEN_ERROR = _("Missing recaptcha token!")
    47 INVALID_TOKEN_ERROR = _("Can't verify recaptcha token! Are you a robot?")
    52 INVALID_TOKEN_ERROR = _("Can't verify recaptcha token! Are you a robot?")
    48 
    53 
    49 
    54 
    82             if self.context.use_captcha:
    87             if self.context.use_captcha:
    83                 captcha = TextLine(title=_("Captcha"), required=True)
    88                 captcha = TextLine(title=_("Captcha"), required=True)
    84                 captcha.__name__ = RECAPTCHA_FIELD_NAME
    89                 captcha.__name__ = RECAPTCHA_FIELD_NAME
    85                 yield captcha
    90                 yield captcha
    86             yield from IFormFieldContainer(self.context).get_fields()
    91             yield from IFormFieldContainer(self.context).get_fields()
    87 
    92             if self.context.rgpd_consent:
    88         return field.Fields(*tuple(get_fields()))
    93                 consent = Bool(title=II18n(self.context).query_attribute('rgpd_warning',
       
    94                                                                          request=self.request),
       
    95                                required=True,
       
    96                                default=False)
       
    97                 consent.__name__ = RGPD_CONSENT_FIELD_NAME
       
    98                 yield consent
       
    99 
       
   100         fields = field.Fields(*tuple(get_fields()))
       
   101         if self.context.rgpd_consent:
       
   102             fields[RGPD_CONSENT_FIELD_NAME].widgetFactory = SingleCheckBoxFieldWidget
       
   103         return fields
    89 
   104 
    90     def updateActions(self):
   105     def updateActions(self):
    91         super(FormFieldContainerInputForm, self).updateActions()
   106         super(FormFieldContainerInputForm, self).updateActions()
    92         if 'submit' in self.actions:
   107         if 'submit' in self.actions:
    93             self.actions['submit'].title = II18n(self.context).query_attribute('submit_label',
   108             self.actions['submit'].title = II18n(self.context).query_attribute('submit_label',
   101                 widget.mode = HIDDEN_MODE
   116                 widget.mode = HIDDEN_MODE
   102                 widget.value = get_csrf_token(self.request)
   117                 widget.value = get_csrf_token(self.request)
   103             elif widget.field.__name__ == RECAPTCHA_FIELD_NAME:
   118             elif widget.field.__name__ == RECAPTCHA_FIELD_NAME:
   104                 widget.name = RECAPTCHA_FIELD_NAME
   119                 widget.name = RECAPTCHA_FIELD_NAME
   105                 widget.mode = HIDDEN_MODE
   120                 widget.mode = HIDDEN_MODE
       
   121             elif widget.field.__name__ == RGPD_CONSENT_FIELD_NAME:
       
   122                 widget.name = RGPD_CONSENT_FIELD_NAME
       
   123                 widget.required = False
       
   124                 user_rights = II18n(self.context).query_attribute('rgpd_user_rights',
       
   125                                                                   request=self.request)
       
   126                 widget.after_widget_notice = '<div><br />{0}</div>'.format(
       
   127                     text_to_html(user_rights, 'oid_to_href'))
       
   128                 widget.object_data = {
       
   129                     'ams-validate-messages': {
       
   130                         'required': self.request.localizer.translate(
       
   131                             _("You can't submit this form without accepting data usage rules."))
       
   132                     }
       
   133                 }
       
   134                 alsoProvides(widget, IObjectData)
   106             else:
   135             else:
   107                 field = IFormFieldContainer(self.context).get(widget.field.__name__)
   136                 field = IFormFieldContainer(self.context).get(widget.field.__name__)
   108                 if field is not None:
   137                 if field is not None:
   109                     widget.placeholder = field.placeholder
   138                     widget.placeholder = field.placeholder
   110 
   139 
   111     @button.buttonAndHandler('title', name='submit')
   140     @button.buttonAndHandler('title', name='submit')
   112     def update_content(self, action):
   141     def update_content(self, action):
       
   142         request = self.request
   113         form = IWfForm(self.context)
   143         form = IWfForm(self.context)
   114         handler = form.query_handler()
   144         handler = form.query_handler()
   115         if handler is not None:
   145         if handler is not None:
   116             data, errors = self.extractData()
   146             data, errors = self.extractData()
   117             if errors:
   147             if errors:
   118                 self.status = self.formErrorsMessage
   148                 self.status = request.localizer.translate(self.formErrorsMessage)
   119             else:
   149             else:
   120                 # remove custom data fields from handler data
   150                 # remove custom data fields from handler data
   121                 if CSRF_FIELD_NAME in data:
   151                 if CSRF_FIELD_NAME in data:
   122                     del data[CSRF_FIELD_NAME]
   152                     del data[CSRF_FIELD_NAME]
   123                 if self.context.use_captcha:
   153                 if form.use_captcha:
   124                     if RECAPTCHA_FIELD_NAME not in data:
   154                     if RECAPTCHA_FIELD_NAME not in data:
   125                         self.add_error(Invalid(MISSING_TOKEN_ERROR), RECAPTCHA_FIELD_NAME)
   155                         self.add_error(Invalid(MISSING_TOKEN_ERROR), RECAPTCHA_FIELD_NAME)
   126                         return
   156                         return
   127                     proxies = {'https': self.context.captcha_proxy} if self.context.captcha_proxy else {}
   157                     proxies = {'https': form.captcha_proxy} if form.captcha_proxy else {}
   128                     recaptcha_verify_api = self.request.registry.settings.get('pyams.recaptcha.verify')
   158                     recaptcha_verify_api = \
       
   159                         request.registry.settings.get('pyams.recaptcha.verify')
   129                     if not recaptcha_verify_api:
   160                     if not recaptcha_verify_api:
   130                         recaptcha_verify_api = 'https://www.google.com/recaptcha/api/siteverify'
   161                         recaptcha_verify_api = 'https://www.google.com/recaptcha/api/siteverify'
   131                     verify = requests.post(recaptcha_verify_api, {
   162                     verify = requests.post(recaptcha_verify_api, {
   132                         'secret': self.context.server_captcha_key,
   163                         'secret': form.server_captcha_key,
   133                         'response': data[RECAPTCHA_FIELD_NAME]
   164                         'response': data[RECAPTCHA_FIELD_NAME]
   134                     }, proxies=proxies).json()
   165                     }, proxies=proxies).json()
   135                     if not verify['success']:
   166                     if not verify['success']:
   136                         self.add_error(INVALID_TOKEN_ERROR, RECAPTCHA_FIELD_NAME)
   167                         self.add_error(INVALID_TOKEN_ERROR, RECAPTCHA_FIELD_NAME)
   137                         return
   168                         return
   138                     del data[RECAPTCHA_FIELD_NAME]
   169                     del data[RECAPTCHA_FIELD_NAME]
   139                 handler.handle(form, data)
   170                 # convert form data
       
   171                 user_data = data.copy()
       
   172                 for field in IFormFieldContainer(form).get_fields():
       
   173                     converter = request.registry.queryMultiAdapter((field, request),
       
   174                                                                    IFormFieldDataConverter)
       
   175                     if converter is not None:
       
   176                         user_data[field.__name__] = converter.convert(data.get(field.__name__))
       
   177                 output = handler.handle(form, data, user_data)
       
   178                 if output:
       
   179                     request.annotations['form.output'] = output
   140 
   180 
   141 
   181 
   142 @adapter_config(context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldContainerInputForm),
   182 @adapter_config(context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldContainerInputForm),
   143                 provides=IFormHelp)
   183                 provides=IFormHelp)
   144 class FormFieldContainerDisplayFormHelp(FormHelp):
   184 class FormFieldContainerDisplayFormHelp(FormHelp):