--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/footer/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,126 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.footer.interfaces import FOOTER_RENDERERS, IFooterRenderer, IFooterSettings, IFooterTarget, \
+ FOOTER_SETTINGS_KEY, IFooterRendererSettings, FOOTER_RENDERER_SETTINGS_KEY
+from zope.traversing.interfaces import ITraversable
+
+# import packages
+from persistent import Persistent
+from pyams_content.features.renderer import RenderedContentMixin
+from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.inherit import BaseInheritInfo, InheritedFieldProperty
+from pyams_utils.request import check_request
+from pyams_utils.traversing import get_parent
+from pyams_utils.vocabulary import vocabulary_config
+from zope.interface import implementer, noLongerProvides, alsoProvides
+from zope.location import Location, locate
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+
+
+@implementer(IFooterSettings)
+class FooterSettings(BaseInheritInfo, RenderedContentMixin, Persistent, Location):
+ """Footer settings persistent class"""
+
+ target_interface = IFooterTarget
+ adapted_interface = IFooterSettings
+
+ _renderer = FieldProperty(IFooterSettings['renderer'])
+ renderer = InheritedFieldProperty(IFooterSettings['renderer'])
+
+ renderer_interface = IFooterRenderer
+
+ def get_renderer(self, request=None, renderer_name=None):
+ if request is None:
+ request = check_request()
+ if not renderer_name:
+ renderer_name = request.params.get('form.widgets.renderer')
+ if renderer_name is None:
+ renderer_name = self.renderer or 'hidden'
+ parent = get_parent(self, IFooterTarget)
+ return request.registry.queryMultiAdapter((parent, request), self.renderer_interface,
+ name=renderer_name)
+
+ def get_settings(self, renderer_name=None):
+ renderer = self.get_renderer(renderer_name=renderer_name)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return None
+ settings_key = FOOTER_RENDERER_SETTINGS_KEY.format(renderer.settings_key)
+ return get_annotation_adapter(self, settings_key,
+ factory=lambda: IFooterRendererSettings(renderer),
+ name='++settings++{0}'.format(renderer.name))
+
+ @property
+ def settings(self):
+ return self.get_settings()
+
+
+@adapter_config(context=IFooterTarget, provides=IFooterSettings)
+def footer_settings_factory(context):
+ """Footer settings factory"""
+ return get_annotation_adapter(context, FOOTER_SETTINGS_KEY, FooterSettings, name='++footer++')
+
+
+@adapter_config(name='footer', context=IFooterTarget, provides=ITraversable)
+class FooterTargetNamespace(ContextAdapter):
+ """Footer target '++footer++' namespace traverser"""
+
+ def traverse(self, name, furtherpath=None):
+ return IFooterSettings(self.context)
+
+
+@adapter_config(context=IFooterSettings, provides=IFooterRendererSettings)
+def footer_settings_renderer_settings_factory(context):
+ """Footer settings renderer settings factory"""
+ return context.settings
+
+
+@adapter_config(name='settings', context=IFooterSettings, provides=ITraversable)
+class FooterSettingsRendererSettingsNamespace(ContextAdapter):
+ """Footer settings '++settings++' namespace traverser"""
+
+ def traverse(self, name, furtherpath=None):
+ if name:
+ return self.context.get_settings(renderer_name=name)
+ else:
+ return IFooterRendererSettings(self.context)
+
+
+@adapter_config(context=IFooterTarget, provides=IFooterRendererSettings)
+def footer_target_renderer_settings_factory(context):
+ """Footer target renderer settings factory"""
+ settings = IFooterSettings(context)
+ return IFooterRendererSettings(settings, None)
+
+
+@vocabulary_config(name=FOOTER_RENDERERS)
+class FooterRendererVocabulary(SimpleVocabulary):
+ """Footer renderers vocabulary"""
+
+ def __init__(self, context=None):
+ request = check_request()
+ if context is None:
+ context = request.context
+ translate = request.localizer.translate
+ registry = request.registry
+ target = get_parent(context, IFooterTarget)
+ terms = [SimpleTerm(name, title=translate(adapter.label))
+ for name, adapter in sorted(registry.getAdapters((target, request), IFooterRenderer),
+ key=lambda x: x[1].weight)]
+ super(FooterRendererVocabulary, self).__init__(terms)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/footer/interfaces/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,60 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.renderer.interfaces import IRenderedContent, IContentRenderer, IRendererSettings
+from pyams_utils.interfaces.inherit import IInheritInfo
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+# import packages
+from zope.interface import Attribute
+from zope.schema import Choice
+
+from pyams_content import _
+
+
+FOOTER_SETTINGS_KEY = 'pyams_content.footer'
+FOOTER_RENDERER_SETTINGS_KEY = 'pyams_content.footer::{0}'
+
+FOOTER_RENDERERS = 'PyAMS.footer.renderers'
+
+
+class IFooterSettings(IInheritInfo, IRenderedContent):
+ """Footer settings interface"""
+
+ renderer = Choice(title=_("Footer template"),
+ description=_("Presentation template used for this footer"),
+ vocabulary=FOOTER_RENDERERS,
+ required=False,
+ default='hidden')
+
+ settings = Attribute("Renderer settings")
+
+
+class IFooterTarget(IAttributeAnnotatable):
+ """Footer target marker interface"""
+
+
+class IFooterRenderer(IContentRenderer):
+ """Footer renderer interface"""
+
+ name = Attribute("Renderer name")
+ settings_key = Attribute("Renderer settings key")
+
+
+class IFooterRendererSettings(IRendererSettings):
+ """Footer renderer settings interface"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/footer/skin/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.footer.interfaces import IFooterTarget, IFooterRenderer, IFooterSettings
+from pyams_content.features.renderer.interfaces import HIDDEN_RENDERER_NAME
+from pyams_skin.layer import IPyAMSLayer
+
+# import packages
+from pyams_content.features.renderer.skin import BaseContentRenderer
+from pyams_utils.adapter import adapter_config
+from pyramid.decorator import reify
+
+from pyams_content import _
+
+
+class BaseFooterRenderer(BaseContentRenderer):
+ """Base footer renderer"""
+
+ @reify
+ def settings(self):
+ if self.settings_interface is None:
+ return None
+ settings = IFooterSettings(self.context)
+ while settings.inherit:
+ settings = IFooterSettings(settings.parent)
+ return settings.settings
+
+
+@adapter_config(name=HIDDEN_RENDERER_NAME, context=(IFooterTarget, IPyAMSLayer), provides=IFooterRenderer)
+class HiddenFooterRenderer(BaseFooterRenderer):
+ """Hidden footer renderer"""
+
+ name = HIDDEN_RENDERER_NAME
+ label = _("Hidden footer")
+ weight = -999
+
+ def render(self):
+ return ''
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/footer/zmi/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,218 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.footer.interfaces import IFooterTarget, IFooterSettings, IFooterRenderer, \
+ IFooterRendererSettings
+from pyams_form.interfaces.form import IWidgetForm, IUncheckedEditFormButtons, IInnerSubForm, \
+ IWidgetsSuffixViewletsManager
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
+from pyams_portal.zmi.interfaces import IPortalContextTemplatePropertiesMenu
+from pyams_skin.interfaces import IInnerPage
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces.data import IObjectData
+from pyams_utils.interfaces.inherit import IInheritInfo
+from z3c.form.interfaces import INPUT_MODE
+
+# import packages
+from pyams_form.form import AJAXEditForm
+from pyams_form.group import NamedWidgetsGroup
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.viewlet.menu import MenuItem, MenuDivider
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config, Viewlet
+from pyams_zmi.form import AdminEditForm, InnerAdminEditForm
+from pyramid.exceptions import NotFound
+from pyramid.response import Response
+from pyramid.view import view_config
+from z3c.form import field, button
+from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+from zope.interface import implementer, alsoProvides, Interface
+
+from pyams_content import _
+
+
+@viewlet_config(name='footer-settings.menu', context=IFooterTarget, layer=IPyAMSLayer,
+ manager=IPortalContextTemplatePropertiesMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=102)
+class FooterSettingsMenu(MenuItem):
+ """Footer settings menu"""
+
+ label = _("Page footer")
+ url = '#footer-settings.html'
+
+
+class IFooterSettingsGroup(Interface):
+ """Footer settings group marker interface"""
+
+
+@pagelet_config(name='footer-settings.html', context=IFooterTarget, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@implementer(IWidgetForm, IInnerPage)
+class FooterSettingsEditForm(AdminEditForm):
+ """Footer settings edit form"""
+
+ @property
+ def title(self):
+ return self.context.title
+
+ legend = _("Edit footer settings")
+
+ def getContent(self):
+ return IFooterSettings(self.context)
+
+ @property
+ def fields(self):
+ if self.getContent().can_inherit:
+ fields = field.Fields(IFooterSettings).select('no_inherit')
+ fields['no_inherit'].widgetFactory = SingleCheckBoxFieldWidget
+ else:
+ fields = field.Fields(Interface)
+ return fields
+
+ @property
+ def buttons(self):
+ if self.mode == INPUT_MODE:
+ return button.Buttons(IUncheckedEditFormButtons)
+ else:
+ return button.Buttons(Interface)
+
+ ajax_handler = 'footer-settings.json'
+
+ def updateGroups(self):
+ if self.getContent().can_inherit:
+ group = NamedWidgetsGroup(self, 'footer', self.widgets,
+ ('no_inherit', ),
+ legend=_("Don't inherit parent footer"),
+ css_class='inner',
+ switch=True,
+ checkbox_switch=True,
+ checkbox_mode='disable',
+ checkbox_field=IFooterSettings['no_inherit'])
+ else:
+ group = NamedWidgetsGroup(self, 'footer', self.widgets, (), css_class='inner')
+ alsoProvides(group, IFooterSettingsGroup)
+ self.add_group(group)
+ super(FooterSettingsEditForm, self).updateGroups()
+
+
+@view_config(name='footer-settings.json', context=IFooterTarget, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+class FooterSettingsAJAXEditForm(AJAXEditForm, FooterSettingsEditForm):
+ """Footer settings edit form, JSON renderer"""
+
+ def get_ajax_output(self, changes):
+ output = super(FooterSettingsAJAXEditForm, self).get_ajax_output(changes) or {}
+ if 'no_inherit' in changes.get(IInheritInfo, ()):
+ output['status'] = 'reload'
+ return output
+
+
+@adapter_config(name='renderer', context=(IFooterTarget, IPyAMSLayer, IFooterSettingsGroup), provides=IInnerSubForm)
+class FooterSettingsRendererEditSubform(InnerAdminEditForm):
+ """Footer settings renderer edit form"""
+
+ legend = None
+
+ fields = field.Fields(IFooterSettings).select('renderer')
+ weight = 1
+
+ def __init__(self, context, request, group):
+ context = IFooterSettings(context)
+ super(FooterSettingsRendererEditSubform, self).__init__(context, request, group)
+
+ def updateWidgets(self, prefix=None):
+ super(FooterSettingsRendererEditSubform, self).updateWidgets(prefix)
+ if 'renderer' in self.widgets:
+ widget = self.widgets['renderer']
+ widget.object_data = {
+ 'ams-change-handler': 'MyAMS.helpers.select2ChangeHelper',
+ 'ams-change-stop-propagation': 'true',
+ 'ams-select2-helper-type': 'html',
+ 'ams-select2-helper-url': absolute_url(self.context, self.request,
+ 'get-footer-settings-renderer-form.html'),
+ 'ams-select2-helper-argument': 'form.widgets.renderer',
+ 'ams-select2-helper-target': '#renderer-settings-helper'
+ }
+ alsoProvides(widget, IObjectData)
+
+ def get_forms(self, include_self=True):
+ if include_self and self.request.method == 'POST':
+ data, errors = self.extractData()
+ if not errors:
+ self.applyChanges(data)
+ for form in super(FooterSettingsRendererEditSubform, self).get_forms(include_self):
+ yield form
+
+
+@adapter_config(name='footer-renderer-settings-form',
+ context=(IFooterRendererSettings, IPyAMSLayer, FooterSettingsRendererEditSubform),
+ provides=IInnerSubForm)
+@adapter_config(name='footer-renderer-settings-form',
+ context=(IFooterTarget, IPyAMSLayer, FooterSettingsAJAXEditForm),
+ provides=IInnerSubForm)
+class FooterSettingsRendererSettingsEditForm(InnerAdminEditForm):
+ """Footer settings renderer settings edit form"""
+
+ legend = _("Footer renderer settings")
+
+ def __new__(cls, context, request, view=None):
+ settings = IFooterRendererSettings(context, None)
+ if settings is None:
+ return None
+ return InnerAdminEditForm.__new__(cls)
+
+ def __init__(self, context, request, view=None):
+ context = IFooterRendererSettings(context)
+ super(FooterSettingsRendererSettingsEditForm, self).__init__(context, request, view)
+
+
+@viewlet_config(name='footer-renderer-settings', context=IFooterSettings, layer=IPyAMSLayer,
+ view=FooterSettingsRendererEditSubform, manager=IWidgetsSuffixViewletsManager)
+@template_config(template='templates/renderer-settings.pt', layer=IPyAMSLayer)
+class FooterSettingsRendererSettingsWidgetsSuffix(Viewlet):
+ """Footer settings renderer settings viewlet"""
+
+ def render_edit_form(self):
+ settings = IFooterSettings(self.context)
+ renderer = settings.get_renderer(self.request)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return ''
+ renderer_settings = IFooterRendererSettings(settings)
+ form = FooterSettingsRendererSettingsEditForm(renderer_settings, self.request)
+ form.update()
+ return form.render()
+
+
+@view_config(name='get-footer-settings-renderer-form.html', context=IFooterSettings,
+ request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, xhr=True)
+def get_footer_settings_renderer_form(request):
+ """Footer settings renderer settings form"""
+ renderer_name = request.params.get('form.widgets.renderer')
+ if renderer_name is None:
+ raise NotFound("No provided renderer argument")
+ if not renderer_name:
+ renderer_name = ''
+ renderer = request.registry.queryMultiAdapter((request.context, request), IFooterRenderer, name=renderer_name)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return Response('')
+ settings = IFooterSettings(request.context)
+ renderer_settings = IFooterRendererSettings(settings)
+ form = FooterSettingsRendererSettingsEditForm(renderer_settings, request)
+ form.update()
+ return Response(form.render())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/footer/zmi/templates/renderer-settings.pt Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,3 @@
+<div id="renderer-settings-helper">
+ <tal:var replace="structure view.render_edit_form()">Edit form</tal:var>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/header/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,126 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.header.interfaces import HEADER_RENDERERS, IHeaderRenderer, IHeaderSettings, IHeaderTarget, \
+ HEADER_SETTINGS_KEY, IHeaderRendererSettings, HEADER_RENDERER_SETTINGS_KEY
+from zope.traversing.interfaces import ITraversable
+
+# import packages
+from persistent import Persistent
+from pyams_content.features.renderer import RenderedContentMixin
+from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.inherit import BaseInheritInfo, InheritedFieldProperty
+from pyams_utils.request import check_request
+from pyams_utils.traversing import get_parent
+from pyams_utils.vocabulary import vocabulary_config
+from zope.interface import implementer, noLongerProvides, alsoProvides
+from zope.location import Location, locate
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+
+
+@implementer(IHeaderSettings)
+class HeaderSettings(BaseInheritInfo, RenderedContentMixin, Persistent, Location):
+ """Header settings persistent class"""
+
+ target_interface = IHeaderTarget
+ adapted_interface = IHeaderSettings
+
+ _renderer = FieldProperty(IHeaderSettings['renderer'])
+ renderer = InheritedFieldProperty(IHeaderSettings['renderer'])
+
+ renderer_interface = IHeaderRenderer
+
+ def get_renderer(self, request=None, renderer_name=None):
+ if request is None:
+ request = check_request()
+ if not renderer_name:
+ renderer_name = request.params.get('form.widgets.renderer')
+ if renderer_name is None:
+ renderer_name = self.renderer or 'hidden'
+ parent = get_parent(self, IHeaderTarget)
+ return request.registry.queryMultiAdapter((parent, request), self.renderer_interface,
+ name=renderer_name)
+
+ def get_settings(self, renderer_name=None):
+ renderer = self.get_renderer(renderer_name=renderer_name)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return None
+ settings_key = HEADER_RENDERER_SETTINGS_KEY.format(renderer.settings_key)
+ return get_annotation_adapter(self, settings_key,
+ factory=lambda: IHeaderRendererSettings(renderer),
+ name='++settings++{0}'.format(renderer.name))
+
+ @property
+ def settings(self):
+ return self.get_settings()
+
+
+@adapter_config(context=IHeaderTarget, provides=IHeaderSettings)
+def header_settings_factory(context):
+ """Header settings factory"""
+ return get_annotation_adapter(context, HEADER_SETTINGS_KEY, HeaderSettings, name='++header++')
+
+
+@adapter_config(name='header', context=IHeaderTarget, provides=ITraversable)
+class HeaderTargetNamespace(ContextAdapter):
+ """Header target '++header++' namespace traverser"""
+
+ def traverse(self, name, furtherpath=None):
+ return IHeaderSettings(self.context)
+
+
+@adapter_config(context=IHeaderSettings, provides=IHeaderRendererSettings)
+def header_settings_renderer_settings_factory(context):
+ """Header settings renderer settings factory"""
+ return context.settings
+
+
+@adapter_config(name='settings', context=IHeaderSettings, provides=ITraversable)
+class HeaderSettingsRendererSettingsNamespace(ContextAdapter):
+ """Header settings '++settings++' namespace traverser"""
+
+ def traverse(self, name, furtherpath=None):
+ if name:
+ return self.context.get_settings(renderer_name=name)
+ else:
+ return IHeaderRendererSettings(self.context)
+
+
+@adapter_config(context=IHeaderTarget, provides=IHeaderRendererSettings)
+def header_target_renderer_settings_factory(context):
+ """Header target renderer settings factory"""
+ settings = IHeaderSettings(context)
+ return IHeaderRendererSettings(settings, None)
+
+
+@vocabulary_config(name=HEADER_RENDERERS)
+class HeaderRendererVocabulary(SimpleVocabulary):
+ """Header renderers vocabulary"""
+
+ def __init__(self, context=None):
+ request = check_request()
+ if context is None:
+ context = request.context
+ translate = request.localizer.translate
+ registry = request.registry
+ target = get_parent(context, IHeaderTarget)
+ terms = [SimpleTerm(name, title=translate(adapter.label))
+ for name, adapter in sorted(registry.getAdapters((target, request), IHeaderRenderer),
+ key=lambda x: x[1].weight)]
+ super(HeaderRendererVocabulary, self).__init__(terms)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/header/interfaces/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,60 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.renderer import IContentRenderer, IRendererSettings, IRenderedContent
+from pyams_utils.interfaces.inherit import IInheritInfo
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+# import packages
+from zope.interface import Attribute
+from zope.schema import Choice
+
+from pyams_content import _
+
+
+HEADER_SETTINGS_KEY = 'pyams_content.header'
+HEADER_RENDERER_SETTINGS_KEY = 'pyams_content.header::{0}'
+
+HEADER_RENDERERS = 'PyAMS.header.renderers'
+
+
+class IHeaderSettings(IInheritInfo, IRenderedContent):
+ """Header settings interface"""
+
+ renderer = Choice(title=_("Header template"),
+ description=_("Presentation template used for this header"),
+ vocabulary=HEADER_RENDERERS,
+ required=False,
+ default='hidden')
+
+ settings = Attribute("Renderer settings")
+
+
+class IHeaderTarget(IAttributeAnnotatable):
+ """Header target marker interface"""
+
+
+class IHeaderRenderer(IContentRenderer):
+ """Header renderer interface"""
+
+ name = Attribute("Renderer name")
+ settings_key = Attribute("Renderer settings key")
+
+
+class IHeaderRendererSettings(IRendererSettings):
+ """Header renderer settings interface"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/header/skin/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.header.interfaces import IHeaderTarget, IHeaderRenderer, IHeaderSettings
+from pyams_content.features.renderer.interfaces import HIDDEN_RENDERER_NAME
+from pyams_skin.layer import IPyAMSLayer
+
+# import packages
+from pyams_content.features.renderer.skin import BaseContentRenderer
+from pyams_utils.adapter import adapter_config
+from pyramid.decorator import reify
+
+from pyams_content import _
+
+
+class BaseHeaderRenderer(BaseContentRenderer):
+ """Base header renderer"""
+
+ @reify
+ def settings(self):
+ if self.settings_interface is None:
+ return None
+ settings = IHeaderSettings(self.context)
+ while settings.inherit:
+ settings = IHeaderSettings(settings.parent)
+ return settings.settings
+
+
+@adapter_config(name=HIDDEN_RENDERER_NAME, context=(IHeaderTarget, IPyAMSLayer), provides=IHeaderRenderer)
+class HiddenHeaderRenderer(BaseHeaderRenderer):
+ """Hidden header renderer"""
+
+ name = HIDDEN_RENDERER_NAME
+ label = _("Hidden header")
+ weight = -999
+
+ def render(self):
+ return ''
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/header/zmi/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,224 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.header.interfaces import IHeaderTarget, IHeaderSettings, IHeaderRenderer, \
+ IHeaderRendererSettings
+from pyams_form.interfaces.form import IWidgetForm, IUncheckedEditFormButtons, IInnerSubForm, \
+ IWidgetsSuffixViewletsManager
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
+from pyams_portal.zmi.interfaces import IPortalContextTemplatePropertiesMenu
+from pyams_skin.interfaces import IInnerPage
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces.data import IObjectData
+from pyams_utils.interfaces.inherit import IInheritInfo
+from z3c.form.interfaces import INPUT_MODE
+
+# import packages
+from pyams_form.form import AJAXEditForm
+from pyams_form.group import NamedWidgetsGroup
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.viewlet.menu import MenuItem, MenuDivider
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config, Viewlet
+from pyams_zmi.form import AdminEditForm, InnerAdminEditForm
+from pyramid.exceptions import NotFound
+from pyramid.response import Response
+from pyramid.view import view_config
+from z3c.form import field, button
+from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
+from zope.interface import implementer, alsoProvides, Interface
+
+from pyams_content import _
+
+
+@viewlet_config(name='header-settings.divider', context=IHeaderTarget, layer=IPyAMSLayer,
+ manager=IPortalContextTemplatePropertiesMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=99)
+class HeaderSettingsDivider(MenuDivider):
+ """Header settings menu divider"""
+
+
+@viewlet_config(name='header-settings.menu', context=IHeaderTarget, layer=IPyAMSLayer,
+ manager=IPortalContextTemplatePropertiesMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=100)
+class HeaderSettingsMenu(MenuItem):
+ """Header settings menu"""
+
+ label = _("Page header")
+ url = '#header-settings.html'
+
+
+class IHeaderSettingsGroup(Interface):
+ """Header settings group marker interface"""
+
+
+@pagelet_config(name='header-settings.html', context=IHeaderTarget, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@implementer(IWidgetForm, IInnerPage)
+class HeaderSettingsEditForm(AdminEditForm):
+ """Header settings edit form"""
+
+ @property
+ def title(self):
+ return self.context.title
+
+ legend = _("Edit header settings")
+
+ def getContent(self):
+ return IHeaderSettings(self.context)
+
+ @property
+ def fields(self):
+ if self.getContent().can_inherit:
+ fields = field.Fields(IHeaderSettings).select('no_inherit')
+ fields['no_inherit'].widgetFactory = SingleCheckBoxFieldWidget
+ else:
+ fields = field.Fields(Interface)
+ return fields
+
+ @property
+ def buttons(self):
+ if self.mode == INPUT_MODE:
+ return button.Buttons(IUncheckedEditFormButtons)
+ else:
+ return button.Buttons(Interface)
+
+ ajax_handler = 'header-settings.json'
+
+ def updateGroups(self):
+ if self.getContent().can_inherit:
+ group = NamedWidgetsGroup(self, 'header', self.widgets,
+ ('no_inherit', ),
+ legend=_("Don't inherit parent header"),
+ css_class='inner',
+ switch=True,
+ checkbox_switch=True,
+ checkbox_mode='disable',
+ checkbox_field=IHeaderSettings['no_inherit'])
+ else:
+ group = NamedWidgetsGroup(self, 'header', self.widgets, (), css_class='inner')
+ alsoProvides(group, IHeaderSettingsGroup)
+ self.add_group(group)
+ super(HeaderSettingsEditForm, self).updateGroups()
+
+
+@view_config(name='header-settings.json', context=IHeaderTarget, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+class HeaderSettingsAJAXEditForm(AJAXEditForm, HeaderSettingsEditForm):
+ """Header settings edit form, JSON renderer"""
+
+ def get_ajax_output(self, changes):
+ output = super(HeaderSettingsAJAXEditForm, self).get_ajax_output(changes) or {}
+ if 'no_inherit' in changes.get(IInheritInfo, ()):
+ output['status'] = 'reload'
+ return output
+
+
+@adapter_config(name='renderer', context=(IHeaderTarget, IPyAMSLayer, IHeaderSettingsGroup), provides=IInnerSubForm)
+class HeaderSettingsRendererEditSubform(InnerAdminEditForm):
+ """Header settings renderer edit form"""
+
+ legend = None
+
+ fields = field.Fields(IHeaderSettings).select('renderer')
+ weight = 1
+
+ def __init__(self, context, request, group):
+ context = IHeaderSettings(context)
+ super(HeaderSettingsRendererEditSubform, self).__init__(context, request, group)
+
+ def updateWidgets(self, prefix=None):
+ super(HeaderSettingsRendererEditSubform, self).updateWidgets(prefix)
+ if 'renderer' in self.widgets:
+ widget = self.widgets['renderer']
+ widget.object_data = {
+ 'ams-change-handler': 'MyAMS.helpers.select2ChangeHelper',
+ 'ams-change-stop-propagation': 'true',
+ 'ams-select2-helper-type': 'html',
+ 'ams-select2-helper-url': absolute_url(self.context, self.request,
+ 'get-header-settings-renderer-form.html'),
+ 'ams-select2-helper-argument': 'form.widgets.renderer',
+ 'ams-select2-helper-target': '#renderer-settings-helper'
+ }
+ alsoProvides(widget, IObjectData)
+
+ def get_forms(self, include_self=True):
+ if include_self and self.request.method == 'POST':
+ data, errors = self.extractData()
+ if not errors:
+ self.applyChanges(data)
+ for form in super(HeaderSettingsRendererEditSubform, self).get_forms(include_self):
+ yield form
+
+
+@adapter_config(name='header-renderer-settings-form',
+ context=(IHeaderRendererSettings, IPyAMSLayer, HeaderSettingsRendererEditSubform),
+ provides=IInnerSubForm)
+@adapter_config(name='header-renderer-settings-form',
+ context=(IHeaderTarget, IPyAMSLayer, HeaderSettingsAJAXEditForm),
+ provides=IInnerSubForm)
+class HeaderSettingsRendererSettingsEditForm(InnerAdminEditForm):
+ """Header settings renderer settings edit form"""
+
+ legend = _("Header renderer settings")
+
+ def __new__(cls, context, request, view=None):
+ settings = IHeaderRendererSettings(context, None)
+ if settings is None:
+ return None
+ return InnerAdminEditForm.__new__(cls)
+
+ def __init__(self, context, request, view=None):
+ context = IHeaderRendererSettings(context)
+ super(HeaderSettingsRendererSettingsEditForm, self).__init__(context, request, view)
+
+
+@viewlet_config(name='header-renderer-settings', context=IHeaderSettings, layer=IPyAMSLayer,
+ view=HeaderSettingsRendererEditSubform, manager=IWidgetsSuffixViewletsManager)
+@template_config(template='templates/renderer-settings.pt', layer=IPyAMSLayer)
+class HeaderSettingsRendererSettingsWidgetsSuffix(Viewlet):
+ """Header settings renderer settings viewlet"""
+
+ def render_edit_form(self):
+ settings = IHeaderSettings(self.context)
+ renderer = settings.get_renderer(self.request)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return ''
+ renderer_settings = IHeaderRendererSettings(settings)
+ form = HeaderSettingsRendererSettingsEditForm(renderer_settings, self.request)
+ form.update()
+ return form.render()
+
+
+@view_config(name='get-header-settings-renderer-form.html', context=IHeaderSettings,
+ request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, xhr=True)
+def get_header_settings_renderer_form(request):
+ """Header settings renderer settings form"""
+ renderer_name = request.params.get('form.widgets.renderer')
+ if renderer_name is None:
+ raise NotFound("No provided renderer argument")
+ if not renderer_name:
+ renderer_name = ''
+ renderer = request.registry.queryMultiAdapter((request.context, request), IHeaderRenderer, name=renderer_name)
+ if (renderer is None) or (renderer.settings_interface is None):
+ return Response('')
+ settings = IHeaderSettings(request.context)
+ renderer_settings = IHeaderRendererSettings(settings)
+ form = HeaderSettingsRendererSettingsEditForm(renderer_settings, request)
+ form.update()
+ return Response(form.render())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/header/zmi/templates/renderer-settings.pt Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,3 @@
+<div id="renderer-settings-helper">
+ <tal:var replace="structure view.render_edit_form()">Edit form</tal:var>
+</div>
--- a/src/pyams_content/features/renderer/__init__.py Thu May 03 11:29:56 2018 +0200
+++ b/src/pyams_content/features/renderer/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -29,11 +29,12 @@
"""Renderer mixin interface"""
renderer = None
+ renderer_interface = IContentRenderer
def get_renderer(self, request=None):
if request is None:
request = check_request()
- return request.registry.queryMultiAdapter((self, request), IContentRenderer, name=self.renderer)
+ return request.registry.queryMultiAdapter((self, request), self.renderer_interface, name=self.renderer)
@adapter_config(context=IRenderedContent, provides=IContentRenderer)
--- a/src/pyams_content/features/renderer/interfaces/__init__.py Thu May 03 11:29:56 2018 +0200
+++ b/src/pyams_content/features/renderer/interfaces/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -23,6 +23,9 @@
from zope.interface import Interface, Attribute
+HIDDEN_RENDERER_NAME = 'hidden'
+
+
class IRenderedContent(IAttributeAnnotatable):
"""Generic interface for any rendered content"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/renderer/skin/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -0,0 +1,71 @@
+#
+# Copyright (c) 2008-2018 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.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.renderer.interfaces import IContentRenderer, IRendererSettings, IRenderedContent, \
+ HIDDEN_RENDERER_NAME
+from pyams_i18n.interfaces import II18n
+from pyams_skin.layer import IPyAMSLayer
+
+# import packages
+from pyams_template.template import get_view_template
+from pyams_utils.adapter import ContextRequestAdapter, adapter_config
+from pyramid.decorator import reify
+from zope.interface import implementer
+
+from pyams_content import _
+
+
+@implementer(IContentRenderer)
+class BaseContentRenderer(ContextRequestAdapter):
+ """Base content renderer"""
+
+ label = None
+ weight = 0
+ settings_interface = None
+
+ language = None
+ context_attrs = ()
+ i18n_context_attrs = ()
+
+ @reify
+ def settings(self):
+ if self.settings_interface is None:
+ return None
+ return IRendererSettings(self.context)
+
+ def update(self):
+ for attr in self.context_attrs:
+ setattr(self, attr, getattr(self.context, attr, None))
+ if self.i18n_context_attrs:
+ i18n = II18n(self.context, None)
+ if i18n is not None:
+ for attr in self.i18n_context_attrs:
+ setattr(self, attr, i18n.get_attribute(attr, lang=self.language, request=self.request))
+
+ render = get_view_template()
+
+
+@adapter_config(name=HIDDEN_RENDERER_NAME, context=(IRenderedContent, IPyAMSLayer), provides=IContentRenderer)
+class HiddenContentRenderer(BaseContentRenderer):
+ """Hidden content renderer"""
+
+ label = _("Hidden content")
+ weight = -999
+
+ def render(self):
+ return ''
--- a/src/pyams_content/features/renderer/zmi/__init__.py Thu May 03 11:29:56 2018 +0200
+++ b/src/pyams_content/features/renderer/zmi/__init__.py Fri May 18 15:50:16 2018 +0200
@@ -18,20 +18,16 @@
# import interfaces
from pyams_content.features.renderer.interfaces import IRenderedContent, IContentRenderer, IRendererSettings
from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
-from pyams_i18n.interfaces import II18n
from pyams_skin.layer import IPyAMSLayer
# import packages
from pyams_form.form import AJAXEditForm
from pyams_pagelet.pagelet import pagelet_config
-from pyams_template.template import get_view_template
-from pyams_utils.adapter import ContextRequestAdapter, adapter_config
from pyams_viewlet.viewlet import BaseContentProvider
from pyams_zmi.form import AdminDialogEditForm
-from pyramid.decorator import reify
from pyramid.view import view_config
from z3c.form import field
-from zope.interface import implementer, Interface
+from zope.interface import Interface
from pyams_content import _
@@ -65,35 +61,6 @@
# Base content renderer
#
-@implementer(IContentRenderer)
-class BaseContentRenderer(ContextRequestAdapter):
- """Base content renderer"""
-
- label = None
- weight = 0
- settings_interface = None
-
- language = None
- context_attrs = ()
- i18n_context_attrs = ()
-
- @reify
- def settings(self):
- if self.settings_interface is None:
- return None
- return IRendererSettings(self.context)
-
- def update(self):
- for attr in self.context_attrs:
- setattr(self, attr, getattr(self.context, attr, None))
- if self.i18n_context_attrs:
- i18n = II18n(self.context, None)
- if i18n is not None:
- for attr in self.i18n_context_attrs:
- setattr(self, attr, i18n.get_attribute(attr, lang=self.language, request=self.request))
-
- render = get_view_template()
-
@pagelet_config(name='renderer-properties.html', context=IRenderedContent, layer=IPyAMSLayer,
permission=MANAGE_CONTENT_PERMISSION)
@@ -121,18 +88,3 @@
permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
class RendererPropertiesAJAXEditForm(AJAXEditForm, RendererPropertiesEditForm):
"""Renderer properties edit form, JSON renderer"""
-
-
-#
-# Default common renderers
-#
-
-@adapter_config(name='hidden', context=(IRenderedContent, IPyAMSLayer), provides=IContentRenderer)
-class HiddenContentRenderer(BaseContentRenderer):
- """Hidden content renderer"""
-
- label = _("Hidden content")
- weight = -999
-
- def render(self):
- return ''