--- a/src/pyams_content/shared/form/__init__.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/__init__.py Wed Sep 04 15:58:26 2019 +0200
@@ -13,6 +13,7 @@
from zope.interface import alsoProvides, implementer, noLongerProvides, provider
from zope.schema.fieldproperty import FieldProperty
+from pyams_content.component.paragraph import IParagraphContainerTarget
from pyams_content.features.checker.interfaces import ERROR_VALUE, IContentChecker
from pyams_content.features.preview.interfaces import IPreviewTarget
from pyams_content.features.review.interfaces import IReviewTarget
@@ -29,7 +30,7 @@
from pyams_content import _
-@implementer(IWfForm, IFormFieldContainerTarget,
+@implementer(IWfForm, IFormFieldContainerTarget, IParagraphContainerTarget,
IPreviewTarget, IReviewTarget)
class WfForm(WfSharedContent):
"""Base form"""
@@ -37,9 +38,10 @@
content_type = FORM_CONTENT_TYPE
content_name = FORM_CONTENT_NAME
+ form_header = FieldProperty(IWfForm['form_header'])
+ alt_title = FieldProperty(IWfForm['alt_title'])
user_title = FieldProperty(IWfForm['user_title'])
auth_only = FieldProperty(IWfForm['auth_only'])
- form_header = FieldProperty(IWfForm['form_header'])
submit_label = FieldProperty(IWfForm['submit_label'])
submit_message = FieldProperty(IWfForm['submit_message'])
_handler = FieldProperty(IWfForm['handler'])
--- a/src/pyams_content/shared/form/field.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/field.py Wed Sep 04 15:58:26 2019 +0200
@@ -9,6 +9,9 @@
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
+from pyams_content.component.paragraph import BaseParagraph, IParagraphFactory, BaseParagraphFactory
+from pyams_content.features.renderer import RenderersVocabulary
+
__docformat__ = 'restructuredtext'
@@ -24,8 +27,10 @@
from zope.schema.fieldproperty import FieldProperty
from zope.traversing.interfaces import ITraversable
-from pyams_content.shared.form.interfaces import FORM_FIELD_CONTAINER_KEY, IFormField, IFormFieldContainer, \
- IFormFieldContainerTarget, IFormFieldFactory, IWfForm
+from pyams_content.shared.form.interfaces import FORM_FIELD_CONTAINER_KEY, IFormField, \
+ IFormFieldContainer, \
+ IFormFieldContainerTarget, IFormFieldFactory, IWfForm, IFormFieldsParagraph, \
+ FORM_FIELDS_PARAGRAPH_NAME, FORM_FIELDS_PARAGRAPH_TYPE, FORM_FIELDS_PARAGRAPH_RENDERERS
from pyams_form.interfaces.form import IFormContextPermissionChecker
from pyams_i18n.interfaces import II18n
from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
@@ -272,3 +277,32 @@
value_type=Choice(values=field.values))
result.__name__ = field.name
return result
+
+
+#
+# Form fields paragraph
+#
+
+@factory_config(provided=IFormFieldsParagraph)
+class FormFieldsParagraph(BaseParagraph):
+ """Form fields paragraph"""
+
+ icon_class = 'fa-th-list'
+ icon_hint = FORM_FIELDS_PARAGRAPH_NAME
+
+ renderer = FieldProperty(IFormFieldsParagraph['renderer'])
+
+
+@utility_config(name=FORM_FIELDS_PARAGRAPH_TYPE, provides=IParagraphFactory)
+class FormFieldsParagraphFactory(BaseParagraphFactory):
+ """Form fields paragraph factory"""
+
+ name = FORM_FIELDS_PARAGRAPH_NAME
+ content_type = FormFieldsParagraph
+
+
+@vocabulary_config(name=FORM_FIELDS_PARAGRAPH_RENDERERS)
+class FormFieldsRendererVocabulary(RenderersVocabulary):
+ """Form fields paragraph renderers vocabulary"""
+
+ content_interface = IFormFieldsParagraph
--- a/src/pyams_content/shared/form/interfaces.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/interfaces.py Wed Sep 04 15:58:26 2019 +0200
@@ -16,6 +16,7 @@
from zope.interface import Attribute, Interface
from zope.schema import Bool, Choice, TextLine
+from pyams_content.component.paragraph import IBaseParagraph
from pyams_content.shared.common.interfaces import ISharedContent, ISharedToolPortalContext, \
IWfSharedContentPortalContext
from pyams_i18n.schema import I18nTextField, I18nTextLineField, I18nHTMLField
@@ -64,11 +65,13 @@
required=False)
placeholder = TextLine(title=_("Placeholder"),
- description=_("Some field types like textline can display a placeholder"),
+ description=_("Some field types like textline can display a "
+ "placeholder"),
required=False)
values = TextLineListField(title=_("Optional values"),
- description=_("List of available values (for 'choice' and 'list' field types)"),
+ description=_("List of available values (for 'choice' and 'list' "
+ "field types)"),
required=False)
default = I18nTextLineField(title=_("Default value"),
@@ -96,6 +99,20 @@
"""Get schema field matching given form field"""
+FORM_FIELDS_PARAGRAPH_TYPE = 'form-fields'
+FORM_FIELDS_PARAGRAPH_NAME = _("Form fields")
+FORM_FIELDS_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.formfields.renderers'
+
+
+class IFormFieldsParagraph(IBaseParagraph):
+ """Form fields paragraph"""
+
+ renderer = Choice(title=_("Form fields template"),
+ description=_("Presentation template used for this paaragraph"),
+ vocabulary=FORM_FIELDS_PARAGRAPH_RENDERERS,
+ default='default')
+
+
class IFormFieldContainer(IContainer):
"""Form fields container interface"""
@@ -118,24 +135,31 @@
class IWfForm(IWfSharedContentPortalContext):
"""Form interface"""
- user_title = I18nTextLineField(title=_("Form title"),
- required=True)
-
- auth_only = Bool(title=_("Authenticated only?"),
- description=_("If 'yes', only authenticated users will be able to see and submit form"),
- required=True,
- default=False)
+ alt_title = I18nTextLineField(title=_("Alternate title"),
+ description=_("If set, this title will be displayed in "
+ "front-office instead of original title"),
+ required=False)
form_header = I18nTextField(title=_("Form header"),
description=_("This header is displayed just above form fields"),
required=False)
+ user_title = I18nTextLineField(title=_("Form title"),
+ required=False)
+
+ auth_only = Bool(title=_("Authenticated only?"),
+ description=_("If 'yes', only authenticated users will be able to see and "
+ "submit form"),
+ required=True,
+ default=False)
+
submit_label = I18nTextLineField(title=_("Submit button"),
description=_("Label of form submit button"),
required=True)
submit_message = I18nHTMLField(title=_("Submit message"),
- description=_("This message will be displayed after form submission"),
+ description=_("This message will be displayed after form "
+ "submission"),
required=True)
handler = Choice(title=_("Form handler"),
@@ -148,16 +172,19 @@
default=True)
client_captcha_key = TextLine(title=_("Site key"),
- description=_("This key is included into HTML code and submitted with form data"),
+ description=_("This key is included into HTML code and submitted "
+ "with form data"),
required=False)
server_captcha_key = TextLine(title=_("Secret key"),
- description=_("This key is used to communicate with Google's reCaptcha services"),
+ description=_("This key is used to communicate with Google's "
+ "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..."),
+ description=_("If your server is behind a proxy, please set it's "
+ "address here; captcha verification requires HTTPS "
+ "support..."),
required=False)
def query_handler(self, handler=None):
--- a/src/pyams_content/shared/form/manager.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/manager.py Wed Sep 04 15:58:26 2019 +0200
@@ -10,28 +10,26 @@
# FOR A PARTICULAR PURPOSE.
#
+from pyramid.events import subscriber
+from zope.component.interfaces import ISite
+from zope.interface import implementer
+from zope.lifecycleevent.interfaces import IObjectAddedEvent
+
+from pyams_content.component.paragraph import IParagraphFactorySettings
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.common.manager import SharedTool
+from pyams_content.shared.form import Form
+from pyams_content.shared.form.interfaces import FORM_CONTENT_TYPE, IFormsManager, \
+ IFormsManagerFactory
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
+from pyams_utils.traversing import get_parent
+
+
__docformat__ = 'restructuredtext'
-# import standard library
-
-# import interfaces
-from pyams_content.shared.common.interfaces import ISharedContentFactory
-from pyams_content.shared.form.interfaces import IFormsManager, FORM_CONTENT_TYPE, IFormsManagerFactory
-from zope.component.interfaces import ISite
-from zope.lifecycleevent.interfaces import IObjectAddedEvent
-
-# import packages
-from pyams_content.shared.common.manager import SharedTool
-from pyams_content.shared.form import Form
-from pyams_utils.adapter import adapter_config
-from pyams_utils.registry import utility_config
-from pyams_utils.traversing import get_parent
-from pyramid.events import subscriber
-from zope.interface import implementer
-
-
-@implementer(IFormsManager)
+@implementer(IFormsManager, IParagraphFactorySettings)
class FormsManager(SharedTool):
"""Forms manager class"""
--- a/src/pyams_content/shared/form/zmi/field.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/zmi/field.py Wed Sep 04 15:58:26 2019 +0200
@@ -60,7 +60,7 @@
@viewlet_config(name='form-fields.menu', context=IFormFieldContainerTarget, layer=IAdminLayer,
- manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=110)
+ manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
class FormFieldsMenu(MenuItem):
"""Form fields menu"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/form/zmi/paragraph.py Wed Sep 04 15:58:26 2019 +0200
@@ -0,0 +1,116 @@
+#
+# 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 z3c.form import button
+from z3c.form.interfaces import INPUT_MODE
+from zope.interface import implementer
+
+from pyams_content.component.paragraph import IParagraphContainerTarget, IParagraphFactorySettings
+from pyams_content.component.paragraph.zmi import IParagraphContainerView, BaseParagraphAddMenu, \
+ BaseParagraphAJAXAddForm, BaseParagraphAddForm, BaseParagraphAJAXEditForm, \
+ BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_content.shared.form import IWfForm
+from pyams_content.shared.form.field import FormFieldsParagraph
+from pyams_content.shared.form.interfaces import FORM_FIELDS_PARAGRAPH_TYPE, IFormFieldsParagraph
+from pyams_form.form import ajax_config
+from pyams_form.interfaces.form import IInnerForm
+from pyams_form.security import ProtectedFormObjectMixin
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
+from pyams_skin.layer import IPyAMSLayer
+from pyams_skin.viewlet.menu import MenuDivider
+from pyams_utils.adapter import adapter_config
+from pyams_utils.traversing import get_parent
+from pyams_viewlet.viewlet import viewlet_config
+
+
+__docformat__ = 'restructuredtext'
+
+from pyams_content import _
+
+
+@viewlet_config(name='add-form-fields-paragraph.divider', context=IParagraphContainerTarget,
+ view=IParagraphContainerView, layer=IPyAMSLayer, manager=IToolbarAddingMenu,
+ weight=899)
+class FormFieldsParagraphAddMenuDivider(ProtectedFormObjectMixin, MenuDivider):
+ """Form fields paragraph add menu divider"""
+
+ def __new__(cls, context, request, view, manager):
+ if not IWfForm.providedBy(context):
+ return None
+ settings = get_parent(context, IParagraphFactorySettings)
+ if (settings is not None) and (
+ FORM_FIELDS_PARAGRAPH_TYPE not in (settings.allowed_paragraphs or ())):
+ return None
+ return MenuDivider.__new__(cls)
+
+
+@viewlet_config(name='add-form-fields-paragraph.menu', context=IParagraphContainerTarget,
+ view=IParagraphContainerView, layer=IPyAMSLayer, manager=IToolbarAddingMenu,
+ weight=900)
+class FormFieldsParagraphAddMenu(BaseParagraphAddMenu):
+ """Form fields paragraph add menu"""
+
+ label = _("Form: input fields...")
+ label_css_class = 'fa fa-fw ' + FormFieldsParagraph.icon_class
+ url = 'add-form-fields-paragraph.html'
+ paragraph_type = FORM_FIELDS_PARAGRAPH_TYPE
+
+ def __new__(cls, context, request, view, manager):
+ if not IWfForm.providedBy(context):
+ return None
+ return BaseParagraphAddMenu.__new__(cls, context, request, view, manager)
+
+
+@pagelet_config(name='add-form-fields-paragraph.html', context=IParagraphContainerTarget,
+ layer=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION)
+@ajax_config(name='add-form-fields-paragraph.json', context=IParagraphContainerTarget,
+ layer=IPyAMSLayer, base=BaseParagraphAJAXAddForm)
+class FormFieldsParagraphAddForm(BaseParagraphAddForm):
+ """Form fields paragraph add form"""
+
+ legend = _("Add new form fields paragraph")
+
+ content_interface = IFormFieldsParagraph
+
+
+@pagelet_config(name='properties.html', context=IFormFieldsParagraph, layer=IPyAMSLayer,
+ permission=MANAGE_CONTENT_PERMISSION)
+@ajax_config(name='properties.json', context=IFormFieldsParagraph, layer=IPyAMSLayer,
+ base=BaseParagraphAJAXEditForm)
+class FormFieldsParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
+ """Form fields paragraph properties edit form"""
+
+ prefix = 'form_fields_properties.'
+
+ legend = _("Edit form fields paragraph properties")
+
+ content_interface = IFormFieldsParagraph
+
+
+@adapter_config(context=(IFormFieldsParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@ajax_config(name='inner-properties.json', context=IFormFieldsParagraph, layer=IPyAMSLayer,
+ base=BaseParagraphAJAXEditForm)
+@implementer(IInnerForm)
+class FormFieldsParagraphInnerEditForm(FormFieldsParagraphPropertiesEditForm):
+ """Form fields paragraph properties inner edit form"""
+
+ legend = None
+
+ @property
+ def buttons(self):
+ if self.mode == INPUT_MODE:
+ return button.Buttons(IParagraphInnerEditFormButtons)
+ else:
+ return button.Buttons()
--- a/src/pyams_content/shared/form/zmi/properties.py Wed Sep 04 15:57:43 2019 +0200
+++ b/src/pyams_content/shared/form/zmi/properties.py Wed Sep 04 15:58:26 2019 +0200
@@ -14,12 +14,16 @@
from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
from zope.interface import Interface
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
from pyams_content.shared.common.zmi.properties import SharedContentPropertiesEditForm
from pyams_content.shared.form.interfaces import IWfForm
+from pyams_form.form import ajax_config
from pyams_form.group import NamedWidgetsGroup
from pyams_form.interfaces.form import IInnerSubForm
+from pyams_pagelet.pagelet import pagelet_config
from pyams_skin.layer import IPyAMSLayer
from pyams_utils.adapter import adapter_config
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
from pyams_zmi.form import InnerAdminEditForm
@@ -28,10 +32,22 @@
from pyams_content import _
+@pagelet_config(name='properties.html', context=IWfForm, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IWfForm, layer=IPyAMSLayer,
+ permission=MANAGE_CONTENT_PERMISSION)
+class FormPropertiesEditForm(SharedContentPropertiesEditForm):
+ """Form properties edit form"""
+
+ interface = IWfForm
+ fieldnames = ('title', 'short_name', 'content_url', 'alt_title',
+ 'header', 'description', 'notepad')
+
+
@adapter_config(name='form-settings',
- context=(IWfForm, IPyAMSLayer, SharedContentPropertiesEditForm),
+ context=(IWfForm, IPyAMSLayer, FormPropertiesEditForm),
provides=IInnerSubForm)
-class FormPropertiesEditForm(InnerAdminEditForm):
+class FormPropertiesInnerEditForm(InnerAdminEditForm):
"""Form properties edit form extension"""
prefix = 'form_properties.'
@@ -39,8 +55,9 @@
legend = _("Main form settings")
fieldset_class = 'bordered no-x-margin margin-y-10'
- fields = field.Fields(IWfForm).select('user_title', 'form_header', 'auth_only', 'submit_label',
- 'submit_message', 'handler', 'use_captcha', 'client_captcha_key',
+ 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')
fields['use_captcha'].widgetFactory = SingleCheckBoxFieldWidget
@@ -48,17 +65,18 @@
def updateGroups(self):
self.add_group(NamedWidgetsGroup(self, 'head', self.widgets,
- ('user_title', 'form_header', 'auth_only',
+ ('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'),
+ ('use_captcha', 'client_captcha_key',
+ 'server_captcha_key', 'captcha_proxy'),
fieldset_class='inner bordered',
legend=_("Add captcha"),
css_class='inner',
switch=True,
checkbox_switch=True,
checkbox_field=IWfForm['use_captcha']))
- super(FormPropertiesEditForm, self).updateGroups()
+ super(FormPropertiesInnerEditForm, self).updateGroups()
def get_ajax_output(self, changes):
if 'handler' in changes.get(IWfForm, ()):
@@ -67,7 +85,7 @@
'message': self.request.localizer.translate(self.successMessage)
}
else:
- return super(FormPropertiesEditForm, self).get_ajax_output(changes)
+ return super(FormPropertiesInnerEditForm, self).get_ajax_output(changes)
@adapter_config(name='handler-settings',
@@ -91,7 +109,8 @@
handler = self.context.query_handler()
if handler is not None:
translate = self.request.localizer.translate
- return translate(_("« {handler} » form handler settings")).format(handler=translate(handler.label))
+ return translate(_("« {handler} » form handler settings")).format(
+ handler=translate(handler.label))
else:
return _("Form handler settings")