Updated form's captcha and proxy management by adding default settings to forms manager
--- a/src/pyams_content/shared/form/__init__.py Mon Oct 07 14:04:31 2019 +0200
+++ b/src/pyams_content/shared/form/__init__.py Wed Oct 16 18:41:45 2019 +0200
@@ -19,10 +19,12 @@
from pyams_content.features.review.interfaces import IReviewTarget
from pyams_content.shared.common import IWfSharedContentFactory, SharedContent, WfSharedContent, WfSharedContentChecker, \
register_content_type
-from pyams_content.shared.form.interfaces import FORM_CONTENT_NAME, FORM_CONTENT_TYPE, IForm, IFormFieldContainer, \
- IFormFieldContainerTarget, IFormHandler, IWfForm, IWfFormFactory
+from pyams_content.shared.form.interfaces import FORM_CONTENT_NAME, FORM_CONTENT_TYPE, IForm, \
+ IFormFieldContainer, \
+ IFormFieldContainerTarget, IFormHandler, IWfForm, IWfFormFactory, IFormsManager
from pyams_utils.adapter import adapter_config
from pyams_utils.registry import get_global_registry
+from pyams_utils.traversing import get_parent
__docformat__ = 'restructuredtext'
@@ -45,10 +47,9 @@
submit_label = FieldProperty(IWfForm['submit_label'])
submit_message = FieldProperty(IWfForm['submit_message'])
_handler = FieldProperty(IWfForm['handler'])
- use_captcha = FieldProperty(IWfForm['use_captcha'])
+ override_captcha = FieldProperty(IWfForm['override_captcha'])
client_captcha_key = FieldProperty(IWfForm['client_captcha_key'])
server_captcha_key = FieldProperty(IWfForm['server_captcha_key'])
- captcha_proxy = FieldProperty(IWfForm['captcha_proxy'])
rgpd_consent = FieldProperty(IWfForm['rgpd_consent'])
rgpd_warning = FieldProperty(IWfForm['rgpd_warning'])
rgpd_user_rights = FieldProperty(IWfForm['rgpd_user_rights'])
@@ -86,6 +87,17 @@
registry = get_global_registry()
return registry.queryUtility(IFormHandler, name=handler)
+ def get_captcha_settings(self):
+ if self.override_captcha:
+ return {
+ 'use_captcha': bool(self.client_captcha_key),
+ 'client_key': self.client_captcha_key,
+ 'server_key': self.server_captcha_key
+ }
+ else:
+ manager = get_parent(self, IFormsManager)
+ return manager.get_captcha_settings()
+
register_content_type(WfForm, shared_content=False)
--- a/src/pyams_content/shared/form/interfaces.py Mon Oct 07 14:04:31 2019 +0200
+++ b/src/pyams_content/shared/form/interfaces.py Wed Oct 16 18:41:45 2019 +0200
@@ -13,8 +13,9 @@
from zope.annotation.interfaces import IAttributeAnnotatable
from zope.container.constraints import containers, contains
from zope.container.interfaces import IContained, IContainer
-from zope.interface import Attribute, Interface
-from zope.schema import Bool, Choice, TextLine
+from zope.interface import Attribute, Interface, invariant
+from zope.interface.interfaces import Invalid
+from zope.schema import Bool, Choice, TextLine, Int, Password
from pyams_content.component.paragraph import IBaseParagraph
from pyams_content.shared.common.interfaces import ISharedContent, ISharedToolPortalContext, \
@@ -37,6 +38,67 @@
class IFormsManager(ISharedToolPortalContext):
"""Forms manager interface"""
+ use_captcha = Bool(title=_("Use captcha?"),
+ description=_("Set default captcha settings"),
+ required=True,
+ default=False)
+
+ default_captcha_client_key = TextLine(title=_("Default captcha site key"),
+ description=_("This key is included into HTML code and "
+ "submitted with form data"),
+ required=False)
+
+ default_captcha_server_key = TextLine(title=_("Default captcha secret key"),
+ description=_("This key is used to communicate with "
+ "Google's reCaptcha services"),
+ required=False)
+
+ def get_captcha_settings(self):
+ """Get default captcha settings"""
+
+ use_proxy = Bool(title=_("Use proxy server?"),
+ description=_("If a proxy server is required to access recaptcha services, "
+ "please set them here"),
+ required=True,
+ default=False)
+
+ proxy_proto = Choice(title=_("Protocol"),
+ description=_("If your server is behind a proxy, please set it's "
+ "protocol here; HTTPS support is required for reCaptcha"),
+ required=False,
+ values=('http', 'https'),
+ default='http')
+
+ proxy_host = TextLine(title=_("Host name"),
+ description=_("If your server is behind a proxy, please set it's "
+ "address here; captcha verification requires HTTPS "
+ "support..."),
+ required=False)
+
+ proxy_port = Int(title=_("Port number"),
+ description=_("If your server is behind a proxy, plase set it's port "
+ "number here"),
+ required=False,
+ default=8080)
+
+ proxy_username = TextLine(title=_("Username"),
+ description=_("If your proxy server requires authentication, "
+ "please set username here"),
+ required=False)
+
+ proxy_password = Password(title=_("Password"),
+ description=_("If your proxy server requires authentication, "
+ "please set password here"),
+ required=False)
+
+ proxy_only_from = TextLine(title=_("Use proxy only from"),
+ description=_("If proxy usage is restricted to several domains "
+ "names, you can set them here, separated by comas"),
+ required=False)
+
+ def get_proxy_url(self, request):
+ """Get proxy server URL"""
+
class IFormsManagerFactory(Interface):
"""Forms manager factory interface"""
@@ -175,10 +237,10 @@
description=_("Select how form data is transmitted"),
vocabulary='PyAMS form handlers')
- use_captcha = Bool(title=_("Use captcha?"),
- description=_("If 'yes', a captcha will be added automatically to the form"),
- required=False,
- default=True)
+ override_captcha = Bool(title=_("Override captcha settings?"),
+ description=_("If 'yes', you can define custom captcha keys here"),
+ required=False,
+ default=True)
client_captcha_key = TextLine(title=_("Site key"),
description=_("This key is included into HTML code and submitted "
@@ -190,11 +252,8 @@
"reCaptcha services"),
required=False)
- captcha_proxy = TextLine(title=_("Recaptcha proxy"),
- description=_("If your server is behind a proxy, please set it's "
- "address here; captcha verification requires HTTPS "
- "support..."),
- required=False)
+ def get_captcha_settings(self):
+ """Get form captcha settings"""
rgpd_consent = Bool(title=_("Required RGPD consent?"),
description=_("If 'yes', an RGPD compliance warning will be displayed "
--- a/src/pyams_content/shared/form/manager.py Mon Oct 07 14:04:31 2019 +0200
+++ b/src/pyams_content/shared/form/manager.py Wed Oct 16 18:41:45 2019 +0200
@@ -14,6 +14,7 @@
from zope.component.interfaces import ISite
from zope.interface import implementer
from zope.lifecycleevent.interfaces import IObjectAddedEvent
+from zope.schema.fieldproperty import FieldProperty
from pyams_content.component.paragraph import IParagraphFactorySettings
from pyams_content.shared.common.interfaces import ISharedContentFactory
@@ -36,6 +37,46 @@
shared_content_type = FORM_CONTENT_TYPE
shared_content_menu = False
+ use_captcha = FieldProperty(IFormsManager['use_captcha'])
+ default_captcha_client_key = FieldProperty(IFormsManager['default_captcha_client_key'])
+ default_captcha_server_key = FieldProperty(IFormsManager['default_captcha_server_key'])
+ use_proxy = FieldProperty(IFormsManager['use_proxy'])
+ proxy_proto = FieldProperty(IFormsManager['proxy_proto'])
+ proxy_host = FieldProperty(IFormsManager['proxy_host'])
+ proxy_port = FieldProperty(IFormsManager['proxy_port'])
+ proxy_username = FieldProperty(IFormsManager['proxy_username'])
+ proxy_password = FieldProperty(IFormsManager['proxy_password'])
+ proxy_only_from = FieldProperty(IFormsManager['proxy_only_from'])
+
+ def get_captcha_settings(self):
+ result = {
+ 'use_captcha': False,
+ 'client_key': None,
+ 'server_key': None
+ }
+ if self.use_captcha:
+ result.update({
+ 'use_captcha': True,
+ 'client_key': self.default_captcha_client_key,
+ 'server_key': self.default_captcha_server_key
+ })
+ return result
+
+ def get_proxy_url(self, request):
+ if self.use_proxy:
+ # check selected domains names
+ if self.proxy_only_from:
+ domains = map(str.strip, self.proxy_only_from.split(','))
+ if request.host not in domains:
+ return None
+ return '{}://{}{}:{}/'.format(self.proxy_proto,
+ '{}{}{}@'.format(self.proxy_username,
+ ':' if self.proxy_password else '',
+ self.proxy_password or '')
+ if self.proxy_username else '',
+ self.proxy_host,
+ self.proxy_port)
+
@utility_config(provides=IFormsManagerFactory)
class FormsManagerFactory(object):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/form/zmi/manager.py Wed Oct 16 18:41:45 2019 +0200
@@ -0,0 +1,91 @@
+#
+# Copyright (c) 2008-2019 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+from pyramid.events import subscriber
+from z3c.form import field
+from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+from z3c.form.interfaces import IDataExtractedEvent
+from zope.interface import Invalid
+
+from pyams_content.shared.common.zmi.manager import SharedToolPropertiesEditForm
+from pyams_content.shared.form.interfaces import IFormsManager
+from pyams_form.group import NamedWidgetsGroup
+from pyams_form.interfaces.form import IInnerSubForm
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.adapter import adapter_config
+from pyams_zmi.form import InnerAdminEditForm
+
+
+__docformat__ = 'restructuredtext'
+
+from pyams_content import _
+
+
+@adapter_config(name='captcha-settings',
+ context=(IFormsManager, IPyAMSLayer, SharedToolPropertiesEditForm),
+ provides=IInnerSubForm)
+class FormManagerCaptchaSettingsEditForm(InnerAdminEditForm):
+ """Form manager captcha settings edit form"""
+
+ prefix = 'captcha_properties.'
+
+ legend = _("Captcha settings")
+ fieldset_class = 'bordered no-x-margin margin-y-10'
+
+ fields = field.Fields(IFormsManager).select('use_captcha',
+ 'default_captcha_client_key',
+ 'default_captcha_server_key',
+ 'use_proxy',
+ 'proxy_proto',
+ 'proxy_host',
+ 'proxy_port',
+ 'proxy_username',
+ 'proxy_password',
+ 'proxy_only_from')
+ fields['use_captcha'].widgetFactory = SingleCheckBoxFieldWidget
+ fields['use_proxy'].widgetFactory = SingleCheckBoxFieldWidget
+
+ weight = 1
+
+ def updateGroups(self):
+ self.add_group(NamedWidgetsGroup(self, 'captcha', self.widgets,
+ ('use_captcha', 'default_captcha_client_key',
+ 'default_captcha_server_key'),
+ fieldset_class='inner bordered',
+ legend=_("Use captcha"),
+ css_class='inner',
+ switch=True,
+ checkbox_switch=True,
+ checkbox_field=IFormsManager['use_captcha']))
+ self.add_group(NamedWidgetsGroup(self, 'proxy', self.widgets,
+ ('use_proxy', 'proxy_proto', 'proxy_host',
+ 'proxy_port', 'proxy_username', 'proxy_password',
+ 'proxy_only_from'),
+ fieldset_class='inner bordered',
+ legend=_("Use proxy server"),
+ css_class='inner',
+ switch=True,
+ checkbox_switch=True,
+ checkbox_field=IFormsManager['use_proxy']))
+ super(FormManagerCaptchaSettingsEditForm, self).updateGroups()
+
+
+@subscriber(IDataExtractedEvent, form_selector=FormManagerCaptchaSettingsEditForm)
+def check_form_captcha_data(event):
+ """Check captcha form input data"""
+ data = event.data
+ if data.get('use_captcha') and not (data.get('default_captcha_client_key') and
+ data.get('default_captcha_server_key')):
+ event.form.widgets.errors += (Invalid(_("You must define client and server key to "
+ "activate a captcha")), )
+ if data.get('use_proxy') and not data.get('proxy_host'):
+ event.form.widgets.errors += (Invalid(_("You must define hostname to use a proxy")), )
--- a/src/pyams_content/shared/form/zmi/properties.py Mon Oct 07 14:04:31 2019 +0200
+++ b/src/pyams_content/shared/form/zmi/properties.py Wed Oct 16 18:41:45 2019 +0200
@@ -58,10 +58,10 @@
fields = field.Fields(IWfForm).select('form_header', 'user_title', 'auth_only',
'submit_label', 'submit_message', 'handler',
- 'use_captcha', 'client_captcha_key',
- 'server_captcha_key', 'captcha_proxy',
+ 'override_captcha', 'client_captcha_key',
+ 'server_captcha_key',
'rgpd_consent', 'rgpd_warning', 'rgpd_user_rights')
- fields['use_captcha'].widgetFactory = SingleCheckBoxFieldWidget
+ fields['override_captcha'].widgetFactory = SingleCheckBoxFieldWidget
fields['rgpd_consent'].widgetFactory = SingleCheckBoxFieldWidget
weight = 1
@@ -96,14 +96,14 @@
('form_header', 'user_title', 'auth_only',
'submit_label', 'submit_message', 'handler')))
self.add_group(NamedWidgetsGroup(self, 'captcha', self.widgets,
- ('use_captcha', 'client_captcha_key',
- 'server_captcha_key', 'captcha_proxy'),
+ ('override_captcha', 'client_captcha_key',
+ 'server_captcha_key'),
fieldset_class='inner bordered',
- legend=_("Add captcha"),
+ legend=_("Override default captcha settings"),
css_class='inner',
switch=True,
checkbox_switch=True,
- checkbox_field=IWfForm['use_captcha']))
+ checkbox_field=IWfForm['override_captcha']))
self.add_group(NamedWidgetsGroup(self, 'rgpd', self.widgets,
('rgpd_consent', 'rgpd_warning', 'rgpd_user_rights'),
fieldset_class='inner bordered',