# HG changeset patch # User Thierry Florac # Date 1526651416 -7200 # Node ID ae803782cc37bae64f3ce8722800fc683be86ddb # Parent 6928ddfc1c0f20e52c8d243484f1224b44d87106 Moved skin-related features into ".skin" modules diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/footer/__init__.py --- /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 +# 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) diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/footer/interfaces/__init__.py --- /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 +# 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""" diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/footer/skin/__init__.py --- /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 +# 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 '' diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/footer/zmi/__init__.py --- /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 +# 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()) diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/footer/zmi/templates/renderer-settings.pt --- /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 @@ +
+ Edit form +
diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/header/__init__.py --- /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 +# 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) diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/header/interfaces/__init__.py --- /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 +# 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""" diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/header/skin/__init__.py --- /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 +# 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 '' diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/header/zmi/__init__.py --- /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 +# 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()) diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/header/zmi/templates/renderer-settings.pt --- /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 @@ +
+ Edit form +
diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/renderer/__init__.py --- 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) diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/renderer/interfaces/__init__.py --- 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""" diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/renderer/skin/__init__.py --- /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 +# 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 '' diff -r 6928ddfc1c0f -r ae803782cc37 src/pyams_content/features/renderer/zmi/__init__.py --- 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 ''