--- a/src/pyams_portal/zmi/template.py Mon Nov 23 17:20:28 2020 +0100
+++ b/src/pyams_portal/zmi/template.py Tue Nov 24 10:43:30 2020 +0100
@@ -10,41 +10,41 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
-
-# import standard library
-
-# import interfaces
-from pyams_portal.interfaces import IPortalTemplateContainer, IPortalTemplate, IPortalContext, \
- MANAGE_TEMPLATE_PERMISSION, LOCAL_TEMPLATE_NAME
-from pyams_skin.interfaces import IPageHeader, IContentTitle
-from pyams_skin.interfaces.container import ITableElementName, ITableElementEditor
-from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager, IBreadcrumbItem
-from pyams_skin.layer import IPyAMSLayer
-from pyams_zmi.layer import IAdminLayer
+from pyramid.events import subscriber
+from z3c.form import button, field
from z3c.form.interfaces import IDataExtractedEvent
from zope.component.interfaces import ISite
+from zope.copy import copy
+from zope.interface import Interface, Invalid
-# import packages
from pyams_form.form import AJAXAddForm, ajax_config
+from pyams_form.schema import CloseButton
from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.interfaces import IPortalContext, IPortalTemplate, IPortalTemplateContainer, \
+ LOCAL_TEMPLATE_NAME, MANAGE_TEMPLATE_PERMISSION
from pyams_portal.template import PortalTemplate
from pyams_portal.zmi.container import PortalTemplateContainerTable
+from pyams_portal.zmi.layout import PortalTemplateLayoutView
+from pyams_skin.interfaces import IContentTitle, IPageHeader
+from pyams_skin.interfaces.container import ITableElementEditor, ITableElementName
+from pyams_skin.interfaces.viewlet import IBreadcrumbItem, IContextActions, \
+ IWidgetTitleViewletManager
+from pyams_skin.layer import IPyAMSLayer
from pyams_skin.page import DefaultPageHeaderAdapter
from pyams_skin.table import DefaultElementEditorAdapter
from pyams_skin.viewlet.breadcrumb import BreadcrumbAdminLayerItem
-from pyams_skin.viewlet.toolbar import ToolbarAction
-from pyams_utils.adapter import adapter_config, ContextRequestAdapter
-from pyams_utils.registry import query_utility
+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
from pyams_utils.traversing import get_parent
from pyams_utils.unicode import translate_string
from pyams_utils.url import absolute_url
from pyams_viewlet.viewlet import viewlet_config
-from pyams_zmi.form import AdminDialogAddForm
-from pyramid.events import subscriber
-from z3c.form import field
-from zope.interface import Interface, Invalid
+from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.layer import IAdminLayer
+
+
+__docformat__ = 'restructuredtext'
from pyams_portal import _
@@ -162,6 +162,160 @@
def handle_new_template_data_extraction(event):
"""Handle new template form data extraction"""
container = query_utility(IPortalTemplateContainer)
- name = event.data.get('name')
+ name = translate_string(event.data.get('name', ''), spaces='-')
if name in container:
event.form.widgets.errors += (Invalid(_("Specified name is already used!")),)
+
+
+#
+# Template renaming 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"""
+
+ def __new__(cls, context, request, view, manager):
+ container = get_parent(context, IPortalTemplateContainer)
+ if container is None:
+ return None
+ return ToolbarMenuDivider.__new__(cls)
+
+ label = _("Rename template...")
+ label_css_class = 'fa fa-fw fa-edit'
+
+ url = 'rename.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='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")
+ icon_css_class = 'fa fa-fw fa-edit'
+
+ fields = field.Fields(IPortalTemplate).select('name')
+ buttons = button.Buttons(IPortalTemplateRenameButtons)
+
+ 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)
+ if changes:
+ data = data.get(self, data)
+ old_name = content.__name__
+ new_name = translate_string(data.get('name'), spaces='-')
+ if old_name != new_name:
+ parent = content.__parent__
+ parent[new_name] = content
+ del parent[old_name]
+ self._renamed = True
+ return changes
+
+ def get_ajax_output(self, changes):
+ if self._renamed:
+ return {
+ 'status': 'redirect',
+ 'location': absolute_url(self.getContent(), self.request, 'admin#properties.html')
+ }
+ else:
+ return super(PortalTemplateRenameForm, self).get_ajax_output(changes)
+
+
+#
+# Template duplication form
+#
+
+@viewlet_config(name='duplication.menu', context=IPortalTemplate, layer=IPyAMSLayer,
+ view=PortalTemplateLayoutView, manager=IContextActions,
+ permission=MANAGE_TEMPLATE_PERMISSION, weight=110)
+class PortalTemplateDuplicationMenu(ToolbarMenuItem):
+ """Portal template duplication menu item"""
+
+ def __new__(cls, context, request, view, manager):
+ container = get_parent(context, IPortalTemplateContainer)
+ if container is None:
+ return None
+ return ToolbarMenuDivider.__new__(cls)
+
+ label = _("Duplicate template...")
+ label_css_class = 'fa fa-fw fa-files-o'
+
+ url = 'duplicate.html'
+ modal_target = True
+
+
+class IPortalTemplateDuplicationButtons(Interface):
+ """Portal template duplication form buttons"""
+
+ close = CloseButton(name='close', title=_("Cancel"))
+ duplicate = button.Button(name='duplicate', title=_("Duplicate this template"))
+
+
+@pagelet_config(name='duplicate.html', context=IPortalTemplate, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='duplicate.json', context=IPortalTemplate, layer=IPyAMSLayer, base=AJAXAddForm)
+class PortalTemplateDuplicationForm(AdminDialogAddForm):
+ """Portal template duplicate form"""
+
+ legend = _("Duplicate template")
+ icon_css_class = 'fa fa-fw fa-files-o'
+
+ fields = field.Fields(IPortalTemplate)
+ buttons = button.Buttons(IPortalTemplateDuplicationButtons)
+
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+ def updateWidgets(self, prefix=None):
+ super(PortalTemplateDuplicationForm, self).updateWidgets(prefix)
+ if 'name' in self.widgets:
+ self.widgets['name'].label = _("New template name")
+
+ def updateActions(self):
+ super(PortalTemplateDuplicationForm, self).updateActions()
+ if 'duplicate' in self.actions:
+ self.actions['duplicate'].addClass('btn-primary')
+
+ def create(self, data):
+ return copy(self.context)
+
+ def add(self, template):
+ container = get_utility(IPortalTemplateContainer)
+ container[translate_string(template.name, spaces='-')] = template
+
+ def nextURL(self):
+ return absolute_url(self.request.root, self.request, 'admin#portal-templates.html')
+
+ def get_ajax_output(self, changes):
+ return {
+ 'status': 'redirect',
+ 'location': self.nextURL()
+ }
+
+
+@subscriber(IDataExtractedEvent, form_selector=PortalTemplateDuplicationForm)
+def handle_new_template_data_extraction(event):
+ """Handle new template form data extraction"""
+ container = get_utility(IPortalTemplateContainer)
+ name = translate_string(event.data.get('name'), spaces='-')
+ if name in container:
+ event.form.widgets.errors += (Invalid(_("Specified name is already used!")),)