src/pyams_portal/zmi/template.py
changeset 279 c025abc00397
parent 180 b3a4f4e2c959
child 289 fca4100c1733
--- 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!")),)