# HG changeset patch # User Thierry Florac # Date 1628148468 -7200 # Node ID fca4100c17331bb156252fb58002e68340455e73 # Parent 390514bce78ae658b068c95952a7bce7c4e9c872 Added support for custom template container CSS class diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/interfaces.py --- a/src/pyams_portal/interfaces.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/interfaces.py Thu Aug 05 09:27:48 2021 +0200 @@ -228,9 +228,13 @@ required=True, default=True) + container_css_class = TextLine(title=_("Container CSS class"), + description=_("CSS class applied to this slot container"), + required=False) + xs_width = Int(title=_("Extra small device width"), - description=_("Slot width, in columns count, on extra small devices (phones...); " - "set to 0 to hide the portlet"), + description=_("Slot width, in columns count, on extra small devices " + "(phones...); set to 0 to hide the portlet"), required=False, min=0, max=12) @@ -243,15 +247,15 @@ max=12) md_width = Int(title=_("Medium devices width"), - description=_("Slot width, in columns count, on medium desktop devices (>= 992 pixels); " - "set to 0 to hide the portlet"), + description=_("Slot width, in columns count, on medium desktop devices " + "(>= 992 pixels); set to 0 to hide the portlet"), required=False, min=0, max=12) lg_width = Int(title=_("Large devices width"), - description=_("Slot width, in columns count, on large desktop devices (>= 1200 pixels); " - "set to 0 to hide the portlet"), + description=_("Slot width, in columns count, on large desktop devices " + "(>= 1200 pixels); set to 0 to hide the portlet"), required=False, min=0, max=12) @@ -374,6 +378,11 @@ description=_("Two registered templates can't share the same name..."), required=True) + css_class = TextLine(title=_("CSS class"), + description=_("This CSS class can be included into main presentation " + "layout..."), + required=False) + class IPortalTemplateContainer(IContainer, IAttributeAnnotatable): """Portal template container interface""" diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.mo Binary file src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.mo has changed diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po --- a/src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po Thu Aug 05 09:27:48 2021 +0200 @@ -126,7 +126,7 @@ #: src/pyams_portal/interfaces.py:259 msgid "CSS class" -msgstr "Class CSS" +msgstr "Classe CSS" #: src/pyams_portal/interfaces.py:260 msgid "CSS class applied to this slot" diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/skin/template.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_portal/skin/template.py Thu Aug 05 09:27:48 2021 +0200 @@ -0,0 +1,47 @@ +# +# Copyright (c) 2015-2021 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. +# + +"""PyAMS_portal.skin.template module + +This module provides a TALES extension which allows to get current template +configuration and CSS class. +""" + +__docformat__ = 'restructuredtext' + +from zope.interface import Interface + +from pyams_portal.interfaces import IPortalContext, IPortalPage +from pyams_skin.layer import IPyAMSLayer +from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config +from pyams_utils.interfaces.tales import ITALESExtension +from pyams_utils.traversing import get_parent + + +@adapter_config(name='template_container_class', + context=(Interface, IPyAMSLayer, Interface), + provides=ITALESExtension) +class TemplateContainerClassTALESExtension(ContextRequestViewAdapter): + """Template class getter TALES extension""" + + def render(self, context=None, default=''): + if context is None: + context = self.context + result = default + portal_context = get_parent(context, IPortalContext) + if portal_context is not None: + page = IPortalPage(portal_context, None) + if page is not None: + template = page.template + if template is not None: + result = template.css_class + return result diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/slot.py --- a/src/pyams_portal/slot.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/slot.py Thu Aug 05 09:27:48 2021 +0200 @@ -33,6 +33,7 @@ slot_name = FieldProperty(ISlotConfiguration['slot_name']) _portlet_ids = FieldProperty(ISlotConfiguration['portlet_ids']) visible = FieldProperty(ISlotConfiguration['visible']) + container_css_class = FieldProperty(ISlotConfiguration['container_css_class']) xs_width = FieldProperty(ISlotConfiguration['xs_width']) sm_width = FieldProperty(ISlotConfiguration['sm_width']) md_width = FieldProperty(ISlotConfiguration['md_width']) diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/template.py --- a/src/pyams_portal/template.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/template.py Thu Aug 05 09:27:48 2021 +0200 @@ -80,6 +80,7 @@ """Portal template class""" name = FieldProperty(IPortalTemplate['name']) + css_class = FieldProperty(IPortalTemplate['css_class']) content_name = _("Portal template") diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/templates/pagelet.pt --- a/src/pyams_portal/templates/pagelet.pt Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/templates/pagelet.pt Thu Aug 05 09:27:48 2021 +0200 @@ -5,14 +5,17 @@
-
-
-
- Content + tal:omit-tag="not:slot_config.container_css_class" + class="${slot_config.container_css_class}"> +
+
+
+ Content +
diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/container.py --- a/src/pyams_portal/zmi/container.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/container.py Thu Aug 05 09:27:48 2021 +0200 @@ -70,7 +70,8 @@ return absolute_url(self.request.root, self.request, 'admin#portal-templates.html') -@adapter_config(context=(IPortalTemplateContainer, IAdminLayer, ITable), provides=ITableElementEditor) +@adapter_config(context=(IPortalTemplateContainer, IAdminLayer, ITable), + provides=ITableElementEditor) class PortalTemplateContainerTableElementEditor(DefaultElementEditorAdapter): """Portal template container table element editor""" @@ -83,7 +84,8 @@ return resource_url(site, self.request, 'admin#{0}'.format(self.view_name)) -@viewlet_config(name='portal-templates.menu', context=ISite, layer=IAdminLayer, manager=IControlPanelMenu, +@viewlet_config(name='portal-templates.menu', + context=ISite, layer=IAdminLayer, manager=IControlPanelMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=20) @viewletmanager_config(name='portal-templates.menu', context=ISite, layer=IAdminLayer) @implementer(IPortalTemplateContainerMenu) @@ -114,10 +116,12 @@ return attributes -@adapter_config(context=(IPortalTemplate, IAdminLayer, PortalTemplateContainerTable), provides=ITableElementEditor) +@adapter_config(context=(IPortalTemplate, IAdminLayer, PortalTemplateContainerTable), + provides=ITableElementEditor) class PortalTemplateTableElementEditor(DefaultElementEditorAdapter): """Portal template table element editor""" + view_name = 'layout.html' modal_target = False @property @@ -125,14 +129,16 @@ return resource_url(self.context, self.request, 'admin#{0}'.format(self.view_name)) -@adapter_config(name='name', context=(Interface, IAdminLayer, PortalTemplateContainerTable), provides=IColumn) +@adapter_config(name='name', context=(Interface, IAdminLayer, PortalTemplateContainerTable), + provides=IColumn) class PortalTemplateContainerNameColumn(NameColumn): """Portal template container name column""" attrName = 'name' -@adapter_config(name='trash', context=(Interface, IAdminLayer, PortalTemplateContainerTable), provides=IColumn) +@adapter_config(name='trash', context=(Interface, IAdminLayer, PortalTemplateContainerTable), + provides=IColumn) class PortalTemplateContainerTrashColumn(TrashColumn): """Portal template container trash column""" @@ -140,14 +146,16 @@ permission = MANAGE_TEMPLATE_PERMISSION -@view_config(name='delete-element.json', context=IPortalTemplateContainer, request_type=IPyAMSLayer, +@view_config(name='delete-element.json', + context=IPortalTemplateContainer, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def delete_portal_template(request): """Delete selected template""" return delete_container_element(request, ignore_permission=True) -@adapter_config(context=(ISite, IAdminLayer, PortalTemplateContainerTable), provides=IValues) +@adapter_config(context=(ISite, IAdminLayer, PortalTemplateContainerTable), + provides=IValues) class PortalTemplateContainerValuesAdapter(ContextRequestViewAdapter): """Portal template container values adapter""" @@ -159,7 +167,8 @@ return () -@pagelet_config(name='portal-templates.html', context=ISite, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) +@pagelet_config(name='portal-templates.html', + context=ISite, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) @implementer(IInnerPage) class PortalTemplateContainerView(AdminView, ContainerView): """Portal template container view""" @@ -170,7 +179,8 @@ super(PortalTemplateContainerView, self).__init__(context, request) -@adapter_config(context=(ISite, IAdminLayer, PortalTemplateContainerView), provides=IPageHeader) +@adapter_config(context=(ISite, IAdminLayer, PortalTemplateContainerView), + provides=IPageHeader) class PortalTemplateContainerHeaderAdapter(DefaultPageHeaderAdapter): """Portal template container header adapter""" @@ -181,8 +191,10 @@ # Templates container configuration views # -@viewlet_config(name='templates-container-configuration.menu', context=ISite, layer=IAdminLayer, - manager=IPortalTemplateContainerMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=1) +@viewlet_config(name='templates-container-configuration.menu', + context=ISite, layer=IAdminLayer, + manager=IPortalTemplateContainerMenu, weight=1, + permission=MANAGE_TEMPLATE_PERMISSION) class PortalTemplatesContainerPropertiesMenu(MenuItem): """Portal template container configuration menu""" diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/layout.py --- a/src/pyams_portal/zmi/layout.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/layout.py Thu Aug 05 09:27:48 2021 +0200 @@ -65,19 +65,23 @@ return _("Template management") -@viewlet_config(name='template-properties.menu', context=IPortalTemplate, layer=IAdminLayer, - manager=IContentManagementMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=1) -@viewletmanager_config(name='template-properties.menu', layer=IAdminLayer, provides=IPropertiesMenu) +@viewlet_config(name='template-layout.menu', + context=IPortalTemplate, layer=IAdminLayer, + manager=IContentManagementMenu, weight=1, + permission=MANAGE_TEMPLATE_PERMISSION) +@viewletmanager_config(name='template-layout.menu', + layer=IAdminLayer, + provides=IPropertiesMenu) @implementer(IPropertiesMenu) class PortalTemplatePropertiesMenu(MenuItem): """Portal template properties menu""" - label = _("Properties") + label = _("Layout") icon_class = 'fa-edit' - url = '#properties.html' + url = '#layout.html' -@pagelet_config(name='properties.html', context=IPortalTemplate, layer=IPyAMSLayer, +@pagelet_config(name='layout.html', context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) @template_config(template='templates/layout.pt', layer=IAdminLayer) @implementer(IInnerPage) @@ -92,14 +96,11 @@ page = IPortalPage(context) if page.use_local_template: return _("Local template configuration") - else: - if page.template.name == LOCAL_TEMPLATE_NAME: - return _("Inherited local template configuration") - else: - translate = self.request.localizer.translate - return translate(_("Shared template configuration ({0})")).format(page.template.name) - else: - return _("Template configuration") + if page.template.name == LOCAL_TEMPLATE_NAME: + return _("Inherited local template configuration") + translate = self.request.localizer.translate + return translate(_("Shared template configuration ({0})")).format(page.template.name) + return _("Template configuration") def get_template(self): return self.context @@ -141,18 +142,16 @@ portlet = self.get_portlet(name) if portlet is not None: return self.request.localizer.translate(portlet.label) - else: - return self.request.localizer.translate(_("{{ missing portlet }}")) + return self.request.localizer.translate(_("{{ missing portlet }}")) def get_portlet_preview(self, portlet_id): settings = self.portlet_configuration.get_portlet_configuration(portlet_id).settings - previewer = self.request.registry.queryMultiAdapter((self.get_context(), self.request, self, settings), - IPortletPreviewer) + previewer = self.request.registry.queryMultiAdapter( + (self.get_context(), self.request, self, settings), IPortletPreviewer) if previewer is not None: previewer.update() return previewer.render() - else: - return '' + return '' # @@ -170,7 +169,8 @@ url = 'PyAMS_portal.template.addRow' -@view_config(name='add-template-row.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='add-template-row.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def add_template_row(request): """Add template raw""" @@ -178,7 +178,8 @@ return {'row_id': config.add_row()} -@view_config(name='set-template-row-order.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='set-template-row-order.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def set_template_row_order(request): """Set template rows order""" @@ -188,7 +189,8 @@ return {'status': 'success'} -@view_config(name='delete-template-row.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='delete-template-row.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def delete_template_row(request): """Delete template row""" @@ -201,9 +203,10 @@ # Slots views # -@viewlet_config(name='add-template-slot.menu', context=IPortalTemplate, layer=IAdminLayer, - view=PortalTemplateLayoutView, manager=IToolbarAddingMenu, - permission=MANAGE_TEMPLATE_PERMISSION, weight=2) +@viewlet_config(name='add-template-slot.menu', + context=IPortalTemplate, layer=IAdminLayer, view=PortalTemplateLayoutView, + manager=IToolbarAddingMenu, weight=2, + permission=MANAGE_TEMPLATE_PERMISSION) class PortalTemplateSlotAddMenu(ToolbarMenuItem): """Portal template slot add menu""" @@ -213,9 +216,11 @@ modal_target = True -@pagelet_config(name='add-template-slot.html', context=IPortalTemplate, layer=IPyAMSLayer, +@pagelet_config(name='add-template-slot.html', + context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) -@ajax_config(name='add-template-slot.json', context=IPortalTemplate, layer=IPyAMSLayer, +@ajax_config(name='add-template-slot.json', + context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, base=AJAXAddForm) class PortalTemplateSlotAddForm(AdminDialogAddForm): """Portal template slot add form""" @@ -274,11 +279,12 @@ else: if not 0 < row_id <= config.rows: translate = event.form.request.localizer.translate - event.form.widgets.errors += (Invalid(translate(_("Row ID must be between 1 and {0}!")).format( - config.rows)),) + event.form.widgets.errors += (Invalid(translate( + _("Row ID must be between 1 and {0}!")).format(config.rows)),) -@view_config(name='set-template-slot-order.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='set-template-slot-order.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def set_template_slot_order(request): """Set template slots order""" @@ -290,7 +296,8 @@ return {'status': 'success'} -@view_config(name='get-slots-width.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='get-slots-width.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def get_template_slots_width(request): """Get template slots width""" @@ -298,7 +305,8 @@ return config.get_slots_width(request.params.get('device')) -@view_config(name='set-slot-width.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='set-slot-width.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def set_template_slot_width(request): """Set template slot width""" @@ -309,9 +317,11 @@ return config.get_slots_width(request.params.get('device')) -@pagelet_config(name='slot-properties.html', context=IPortalTemplate, layer=IPyAMSLayer, +@pagelet_config(name='slot-properties.html', + context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) -@ajax_config(name='slot-properties.json', context=IPortalTemplate, layer=IPyAMSLayer) +@ajax_config(name='slot-properties.json', + context=IPortalTemplate, layer=IPyAMSLayer) class PortalTemplateSlotPropertiesEditForm(AdminDialogEditForm): """Slot properties edit form""" @@ -363,7 +373,8 @@ return super(self.__class__, self).get_ajax_output(changes) -@view_config(name='delete-template-slot.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='delete-template-slot.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def delete_template_slot(request): """Delete template slot""" @@ -376,16 +387,18 @@ # Portlet views # -@viewlet_config(name='add-template-portlet.divider', context=IPortalTemplate, layer=IAdminLayer, - view=PortalTemplateLayoutView, manager=IToolbarAddingMenu, - permission=MANAGE_TEMPLATE_PERMISSION, weight=10) +@viewlet_config(name='add-template-portlet.divider', + context=IPortalTemplate, layer=IAdminLayer, view=PortalTemplateLayoutView, + manager=IToolbarAddingMenu, weight=10, + permission=MANAGE_TEMPLATE_PERMISSION) class PortalTemplateAddMenuDivider(ToolbarMenuDivider): """Portal template menu divider""" -@viewlet_config(name='add-template-portlet.menu', context=IPortalTemplate, layer=IAdminLayer, - view=PortalTemplateLayoutView, manager=IToolbarAddingMenu, - permission=MANAGE_TEMPLATE_PERMISSION, weight=20) +@viewlet_config(name='add-template-portlet.menu', + context=IPortalTemplate, layer=IAdminLayer, view=PortalTemplateLayoutView, + manager=IToolbarAddingMenu, weight=20, + permission=MANAGE_TEMPLATE_PERMISSION) class PortalTemplatePortletAddMenu(ToolbarMenuItem): """Portal template portlet add menu""" @@ -395,9 +408,11 @@ modal_target = True -@pagelet_config(name='add-template-portlet.html', context=IPortalTemplate, layer=IPyAMSLayer, +@pagelet_config(name='add-template-portlet.html', + context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) -@ajax_config(name='add-template-portlet.json', context=IPortalTemplate, layer=IPyAMSLayer, +@ajax_config(name='add-template-portlet.json', + context=IPortalTemplate, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, base=AJAXAddForm) class PortalTemplatePortletAddForm(AdminDialogAddForm): """Portal template portlet add form""" @@ -421,8 +436,8 @@ def get_ajax_output(self, changes): configuration = IPortalPortletsConfiguration(self.context) settings = configuration.get_portlet_configuration(changes['portlet_id']).settings - previewer = self.request.registry.queryMultiAdapter((self.context, self.request, self, settings), - IPortletPreviewer) + previewer = self.request.registry.queryMultiAdapter( + (self.context, self.request, self, settings), IPortletPreviewer) if previewer is not None: previewer.update() changes['preview'] = previewer.render() @@ -433,7 +448,8 @@ } -@view_config(name='drag-template-portlet.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='drag-template-portlet.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def drag_template_portlet(request): """Drag portlet icon to slot""" @@ -443,8 +459,8 @@ slot_name = request.params.get('slot_name') changes = tmpl_config.add_portlet(portlet_name, slot_name) settings = portlets_config.get_portlet_configuration(changes['portlet_id']).settings - previewer = request.registry.queryMultiAdapter((request.context, request, request, settings), - IPortletPreviewer) + previewer = request.registry.queryMultiAdapter( + (request.context, request, request, settings), IPortletPreviewer) if previewer is not None: previewer.update() changes['preview'] = previewer.render() @@ -456,7 +472,8 @@ } -@view_config(name='set-template-portlet-order.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='set-template-portlet-order.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def set_template_portlet_order(request): """Set template portlet order""" @@ -480,11 +497,12 @@ request = self.request request.registry.notify(PageletCreatedEvent(self)) portlet_id = int(request.params.get('{0}widgets.portlet_id'.format(self.prefix))) - portlet_config = IPortalPortletsConfiguration(self.context).get_portlet_configuration(portlet_id) + portlet_config = IPortalPortletsConfiguration(self.context) \ + .get_portlet_configuration(portlet_id) if portlet_config is None: raise NotFound() - editor = self.request.registry.queryMultiAdapter((portlet_config.editor_settings, request), - IPagelet, name='properties.html') + editor = self.request.registry.queryMultiAdapter( + (portlet_config.editor_settings, request), IPagelet, name='properties.html') if editor is None: raise NotFound() request.registry.notify(PageletCreatedEvent(editor)) @@ -503,7 +521,8 @@ request.registry.notify(PageletCreatedEvent(self)) # load portlet config portlet_id = int(request.params.get('{0}widgets.portlet_id'.format(self.prefix))) - portlet_config = IPortalPortletsConfiguration(self.context).get_portlet_configuration(portlet_id) + portlet_config = IPortalPortletsConfiguration(self.context) \ + .get_portlet_configuration(portlet_id) if portlet_config is None: raise NotFound() # check inheritance @@ -552,7 +571,8 @@ return changes -@view_config(name='delete-template-portlet.json', context=IPortalTemplate, request_type=IPyAMSLayer, +@view_config(name='delete-template-portlet.json', + context=IPortalTemplate, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) def delete_template_portlet(request): """Delete template portlet""" diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/page.py --- a/src/pyams_portal/zmi/page.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/page.py Thu Aug 05 09:27:48 2021 +0200 @@ -95,6 +95,11 @@ def getContent(self): return IPortalPage(self.context) + @property + def template_css_class(self): + template = IPortalPage(self.context).local_template + return template.css_class if template is not None else '' + def updateWidgets(self, prefix=None): super().updateWidgets(prefix) shared_template = self.widgets.get('shared_template') @@ -116,7 +121,12 @@ content.use_local_template = False elif template_mode == TEMPLATE_LOCAL_MODE: content.use_local_template = True - return {IPortalPage: ('inherit_parent', 'use_local_template', 'shared_template')} + template = IPortalPage(self.context).local_template + if template is not None: + template.css_class = self.request.params.get('template_css_class', '') + return { + IPortalPage: ('inherit_parent', 'use_local_template', 'shared_template') + } def get_ajax_output(self, changes): output = super(self.__class__, self).get_ajax_output(changes) @@ -137,23 +147,27 @@ form = event.form if not form.getContent().can_inherit: data = event.data - if (form.request.params.get('template_mode') == TEMPLATE_SHARED_MODE) and not data.get('shared_template'): + if (form.request.params.get('template_mode') == TEMPLATE_SHARED_MODE) and \ + not data.get('shared_template'): form.widgets.errors += (Invalid(_("You must select which shared template to use!")),) -@adapter_config(context=(Interface, IPyAMSLayer, PortalContextTemplatePropertiesEditForm), provides=IPageHeader) +@adapter_config(context=(Interface, IPyAMSLayer, PortalContextTemplatePropertiesEditForm), + provides=IPageHeader) class PortalContextPropertiesEditFormHeaderAdapter(PropertiesEditFormHeaderAdapter): """Portal context template properties edit form header adapter""" icon_class = 'fa fa-fw fa-columns' -@adapter_config(context=(IPortalContext, IAdminLayer, PortalContextTemplatePropertiesEditForm), provides=IFormHelp) +@adapter_config(context=(IPortalContext, IAdminLayer, PortalContextTemplatePropertiesEditForm), + provides=IFormHelp) class PortalContextPropertiesEditFormHelpAdapter(FormHelp): """Portal context properties edit form help adapter""" message = _("If you choose a shared template, you can only adjust settings of " - "each portlet individually but can't change portlets list or page configuration.\n" + "each portlet individually but can't change portlets list or page " + "configuration.\n" "If you use a local template, you can define a whole custom " "configuration but the template definition can't be reused anywhere...""") message_format = 'text' @@ -163,8 +177,10 @@ # Portal context template configuration # -@viewlet_config(name='template-config.menu', context=IPortalContext, layer=IAdminLayer, - manager=IPortalContextTemplatePropertiesMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=50) +@viewlet_config(name='template-config.menu', + context=IPortalContext, layer=IAdminLayer, + manager=IPortalContextTemplatePropertiesMenu, weight=50, + permission=MANAGE_TEMPLATE_PERMISSION) class PortalContextTemplateConfigMenu(MenuItem): """Portal context template configuration menu""" @@ -186,7 +202,8 @@ return super(PortalContextTemplateConfigMenu, self).get_url() -@pagelet_config(name='template-config.html', context=IPortalContext, layer=IPyAMSLayer, +@pagelet_config(name='template-config.html', + context=IPortalContext, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) class PortalContextTemplateLayoutView(PortalTemplateLayoutView): """Portal context template configuration view""" @@ -201,7 +218,8 @@ return self.request.has_permission(MANAGE_TEMPLATE_PERMISSION) -@adapter_config(context=(IPortalContext, IAdminLayer, PortalContextTemplateLayoutView), provides=IPageHeader) +@adapter_config(context=(IPortalContext, IAdminLayer, PortalContextTemplateLayoutView), + provides=IPageHeader) class PortalContextTemplateLayoutHeaderAdapter(PortalTemplateHeaderAdapter): """Portal context template configuration header adapter""" @@ -210,7 +228,8 @@ # Template management views # -@view_config(name='get-slots-width.json', context=IPortalContext, request_type=IPyAMSLayer, +@view_config(name='get-slots-width.json', + context=IPortalContext, request_type=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION, renderer='json', xhr=True) def get_template_slots_width(request): """Get template slots width""" @@ -218,13 +237,15 @@ return config.get_slots_width(request.params.get('device')) -@view_config(name='portlet-properties.html', context=IPortalContext, request_type=IPyAMSLayer, +@view_config(name='portlet-properties.html', + context=IPortalContext, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION) class PortalContextTemplatePortletEditForm(PortalTemplatePortletEditForm): """Portal context template portlet edit form""" -@view_config(name='portlet-properties.json', context=IPortalContext, request_type=IPyAMSLayer, +@view_config(name='portlet-properties.json', + context=IPortalContext, request_type=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True) class PortalContextTemplatePortletAJAXEditForm(PortalTemplatePortletAJAXEditForm): """Portal context template portlet edit form, JSON renderer""" diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/portlet.py --- a/src/pyams_portal/zmi/portlet.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/portlet.py Thu Aug 05 09:27:48 2021 +0200 @@ -10,25 +10,17 @@ # FOR A PARTICULAR PURPOSE. # -__docformat__ = 'restructuredtext' - - -# import standard library - from pyramid.decorator import reify from z3c.form import field from z3c.form.interfaces import INPUT_MODE -from zope.interface import implementer, Interface +from zope.interface import Interface, implementer -# import packages from pyams_form.form import ajax_config from pyams_form.help import FormHelp -# import interfaces -from pyams_form.interfaces.form import IFormManager, IInnerTabForm, IFormHelp +from pyams_form.interfaces.form import IFormHelp, IFormManager, IInnerTabForm from pyams_pagelet.pagelet import pagelet_config -from pyams_portal import _ -from pyams_portal.interfaces import IPortlet, IPortalTemplate, IPortalPage, MANAGE_TEMPLATE_PERMISSION, \ - IPortletSettings, IPortletRendererSettings, LOCAL_TEMPLATE_NAME +from pyams_portal.interfaces import IPortalPage, IPortalTemplate, IPortlet, \ + IPortletRendererSettings, IPortletSettings, LOCAL_TEMPLATE_NAME, MANAGE_TEMPLATE_PERMISSION from pyams_portal.zmi.widget import PortletRendererFieldWidget from pyams_skin.event import get_json_widget_refresh_event from pyams_skin.layer import IPyAMSLayer @@ -40,6 +32,11 @@ from pyams_zmi.layer import IAdminLayer +__docformat__ = 'restructuredtext' + +from pyams_portal import _ + + @template_config(template='templates/portlet.pt', layer=IPyAMSLayer) class PortletSettingsEditor(AdminDialogEditForm): """Portlet settings edit form""" @@ -53,10 +50,11 @@ if not IPortalTemplate.providedBy(parent): parent = IPortalPage(parent).template if parent.name == LOCAL_TEMPLATE_NAME: - return translate(_("Local portal template - {0}")).format(translate(self.portlet.label)) + return translate(_("Local portal template - {0}")).format( + translate(self.portlet.label)) else: - return translate(_("« {0} » portal template - {1}")).format(parent.name, - translate(self.portlet.label)) + return translate(_("« {0} » portal template - {1}")).format( + parent.name, translate(self.portlet.label)) legend = _("Edit portlet settings") dialog_class = 'modal-large' @@ -104,11 +102,14 @@ return None return FormHelp.__new__(cls) - message = _("""WARNING: Portlet properties are saved automatically when changing inherit mode!!""") + message = _("WARNING: Portlet properties are saved " + "automatically when changing inherit mode!!") message_format = 'rest' -@adapter_config(name='properties', context=(Interface, IPyAMSLayer, PortletSettingsEditor), provides=IInnerTabForm) +@adapter_config(name='properties', + context=(Interface, IPyAMSLayer, PortletSettingsEditor), + provides=IInnerTabForm) class PortletSettingsPropertiesEditor(InnerAdminEditForm): """Portlet settings properties editor""" @@ -149,11 +150,13 @@ 'close_form': False }) output.setdefault('events', []).append( - get_json_widget_refresh_event(self.context, self.request, get_form_factory, 'renderer')) + get_json_widget_refresh_event(self.context, self.request, + get_form_factory, 'renderer')) output['smallbox'] = { 'status': 'info', - 'message': self.request.localizer.translate(_("You changed renderer selection. Don't omit to " - "update new renderer properties...")), + 'message': self.request.localizer.translate( + _("You changed renderer selection. " + "Don't omit to update new renderer properties...")), 'timeout': 5000 } return output @@ -198,22 +201,22 @@ if self.manager is not None: self.manager.update() else: - super(PortletRendererPropertiesEditForm, self).update() + super().update() def updateWidgets(self, prefix=None): if self.manager is not None: self.manager.updateWidgets(prefix) else: - super(PortletRendererPropertiesEditForm, self).updateWidgets(prefix) + super().updateWidgets(prefix) def updateActions(self): if self.manager is not None: self.manager.updateActions() else: - super(PortletRendererPropertiesEditForm, self).updateActions() + super().updateActions() def updateGroups(self): if self.manager is not None: self.manager.updateGroups() else: - super(PortletRendererPropertiesEditForm, self).updateGroups() + super().updateGroups() diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/template.py --- a/src/pyams_portal/zmi/template.py Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/template.py Thu Aug 05 09:27:48 2021 +0200 @@ -33,6 +33,7 @@ from pyams_skin.page import DefaultPageHeaderAdapter from pyams_skin.table import DefaultElementEditorAdapter from pyams_skin.viewlet.breadcrumb import BreadcrumbAdminLayerItem +from pyams_skin.viewlet.menu import MenuItem from pyams_skin.viewlet.toolbar import ToolbarAction, ToolbarMenuDivider, ToolbarMenuItem from pyams_utils.adapter import ContextRequestAdapter, adapter_config from pyams_utils.registry import get_utility, query_utility @@ -41,6 +42,7 @@ from pyams_utils.url import absolute_url from pyams_viewlet.viewlet import viewlet_config from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm +from pyams_zmi.interfaces.menu import IPropertiesMenu from pyams_zmi.layer import IAdminLayer @@ -82,9 +84,11 @@ return self.context.name context = get_parent(self.context, IPortalContext) if context is not None: - adapter = self.request.registry.queryMultiAdapter((context, self.request), ITableElementName) + adapter = self.request.registry.queryMultiAdapter((context, self.request), + ITableElementName) if adapter is not None: - return self.request.localizer.translate(_("{0} (local template)")).format(adapter.name) + return self.request.localizer.translate(_("{0} (local template)")).format( + adapter.name) return '--' @@ -112,7 +116,8 @@ # check for portal context context = get_parent(self.context, IPortalContext) if context is not None: - adapter = self.request.registry.queryMultiAdapter((context, self.request, self.view), IContentTitle) + adapter = self.request.registry.queryMultiAdapter((context, self.request, self.view), + IContentTitle) if adapter is None: adapter = IContentTitle(context, None) if adapter is not None: @@ -120,7 +125,7 @@ # -# Template views +# Template add views # @viewlet_config(name='add-portal-template.action', context=ISite, layer=IAdminLayer, @@ -144,7 +149,7 @@ legend = _("Add shared template") icon_css_class = 'fa fa-fw fa-columns' - fields = field.Fields(IPortalTemplate) + fields = field.Fields(IPortalTemplate).select('name') edit_permission = MANAGE_TEMPLATE_PERMISSION def create(self, data): @@ -168,58 +173,47 @@ # -# Template renaming form +# Template properties form # -@viewlet_config(name='rename.menu', context=IPortalTemplate, layer=IPyAMSLayer, - view=PortalTemplateLayoutView, manager=IContextActions, - permission=MANAGE_TEMPLATE_PERMISSION, weight=100) -class PortalTemplateRenameMenu(ToolbarMenuItem): - """Portal template rename menu item""" +@viewlet_config(name='properties.menu', + context=IPortalTemplate, layer=IAdminLayer, + manager=IPropertiesMenu, weight=10, + permission=MANAGE_TEMPLATE_PERMISSION) +class PortalTemplatePropertiesMenu(MenuItem): + """Portal template properties menu item""" def __new__(cls, context, request, view, manager): container = get_parent(context, IPortalTemplateContainer) if container is None: return None - return ToolbarMenuDivider.__new__(cls) + return MenuItem.__new__(cls) - label = _("Rename template...") + label = _("Template properties...") label_css_class = 'fa fa-fw fa-edit' - url = 'rename.html' + url = 'properties.html' modal_target = True -class IPortalTemplateRenameButtons(Interface): - """Portal template rename form buttons""" - - close = CloseButton(name='close', title=_("Cancel")) - rename = button.Button(name='rename', title=_("Rename template")) - +@pagelet_config(name='properties.html', + context=IPortalTemplate, layer=IPyAMSLayer, + permission=MANAGE_TEMPLATE_PERMISSION) +@ajax_config(name='properties.json', context=IPortalTemplate, layer=IPyAMSLayer) +class PortalTemplatePropertiesEditForm(AdminDialogEditForm): + """Portal template properties edit form""" -@pagelet_config(name='rename.html', context=IPortalTemplate, layer=IPyAMSLayer, - permission=MANAGE_TEMPLATE_PERMISSION) -@ajax_config(name='rename.json', context=IPortalTemplate, layer=IPyAMSLayer) -class PortalTemplateRenameForm(AdminDialogEditForm): - """Portal template rename form""" - - legend = _("Rename template") + legend = _("Template properties") icon_css_class = 'fa fa-fw fa-edit' - fields = field.Fields(IPortalTemplate).select('name') - buttons = button.Buttons(IPortalTemplateRenameButtons) + fields = field.Fields(IPortalTemplate).select('name', 'css_class') edit_permission = MANAGE_TEMPLATE_PERMISSION _renamed = False - def updateActions(self): - super(PortalTemplateRenameForm, self).updateActions() - if 'rename' in self.actions: - self.actions['rename'].addClass('btn-primary') - def update_content(self, content, data): - changes = super(PortalTemplateRenameForm, self).update_content(content, data) + changes = super().update_content(content, data) if changes: data = data.get(self, data) old_name = content.__name__ @@ -237,8 +231,7 @@ 'status': 'redirect', 'location': absolute_url(self.getContent(), self.request, 'admin#properties.html') } - else: - return super(PortalTemplateRenameForm, self).get_ajax_output(changes) + return super().get_ajax_output(changes) # diff -r 390514bce78a -r fca4100c1733 src/pyams_portal/zmi/templates/template-properties.pt --- a/src/pyams_portal/zmi/templates/template-properties.pt Fri Mar 26 16:32:52 2021 +0100 +++ b/src/pyams_portal/zmi/templates/template-properties.pt Thu Aug 05 09:27:48 2021 +0200 @@ -82,6 +82,20 @@ Use custom local template
+
+
+
+ Custom CSS class +
+
+
+ +
+
+
+