--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_portal/generations/__init__.py Tue Nov 24 10:43:30 2020 +0100
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+import logging
+from importlib import import_module
+
+from pyams_portal.interfaces import IPortalTemplateContainer
+from pyams_portal.template import PortalTemplateContainer
+from pyams_utils.interfaces.site import ISiteGenerations
+from pyams_utils.registry import utility_config
+from pyams_utils.site import check_required_utilities
+
+
+logger = logging.getLogger("PyAMS (portal)")
+
+
+REQUIRED_UTILITIES = ((IPortalTemplateContainer, '', PortalTemplateContainer, 'Portal templates'), )
+
+
+@utility_config(name='PyAMS portal', provides=ISiteGenerations)
+class PortalGenerationsChecker(object):
+ """PyAMS portal package generations checker"""
+
+ order = 70
+ generation = 2
+
+ def evolve(self, site, current=None):
+ """Check for required utilities, tables and tools"""
+ if not current:
+ current = 1
+ check_required_utilities(site, REQUIRED_UTILITIES)
+ for generation in range(current, self.generation):
+ module_name = 'pyams_portal.generations.evolve{}'.format(generation)
+ module = import_module(module_name)
+ module.evolve(site)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_portal/generations/evolve1.py Tue Nov 24 10:43:30 2020 +0100
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2008-2018 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+from pyams_content.component.paragraph import IParagraphContainer
+from pyams_content.shared.logo.interfaces import ILogosParagraph
+from pyams_i18n.interfaces import II18n
+from pyams_portal.interfaces import IPortalContext, IPortalPage, IPortalTemplate
+from pyams_utils.container import find_objects_providing
+from pyams_utils.interfaces.intids import IUniqueID
+from pyams_utils.registry import get_local_registry, get_utilities_for, set_local_registry
+
+
+def evolve(site):
+ """Evolve 2: rename resource annotations"""
+ old_registry = get_local_registry()
+ try:
+ registry = site.getSiteManager()
+ set_local_registry(registry)
+ # Update templates registry
+ renames = {}
+ for name, template in get_utilities_for(IPortalTemplate):
+ new_name = IUniqueID(template).oid
+ renames[name] = new_name
+ registry.unregisterUtility(template, IPortalTemplate, name=name)
+ registry.registerUtility(template, IPortalTemplate, name=new_name)
+ # Update templates references
+ for context in find_objects_providing(site, IPortalContext):
+ page = IPortalPage(context)
+ page._shared_template = renames.get(page._shared_template)
+ print("Upgrading template name for « {} »".format(context))
+ finally:
+ set_local_registry(old_registry)
Binary file src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.mo has changed
--- a/src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po Mon Nov 23 17:20:28 2020 +0100
+++ b/src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po Tue Nov 24 10:43:30 2020 +0100
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2019-09-24 11:54+0200\n"
+"POT-Creation-Date: 2020-11-24 09:57+0100\n"
"PO-Revision-Date: 2015-05-12 12:10+0200\n"
"Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
"Language-Team: French <traduc@traduc.org>\n"
@@ -16,7 +16,7 @@
"Generated-By: Lingua 3.10.dev0\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: src/pyams_portal/portlet.py:177
+#: src/pyams_portal/portlet.py:180
msgid "Renderer:"
msgstr "Mode de rendu :"
@@ -201,7 +201,7 @@
msgid "Template used for this page"
msgstr "Modèle de présentation utilisé pour cette page"
-#: src/pyams_portal/template.py:81
+#: src/pyams_portal/template.py:84
msgid "Portal template"
msgstr "Modèle de présentation"
@@ -277,76 +277,56 @@
"Vous avez choisi un nouveau mode de rendu, pensez à vérifier son "
"paramétrage !"
-#: src/pyams_portal/zmi/layout.py:81
+#: src/pyams_portal/zmi/layout.py:75
msgid "Properties"
msgstr "Propriétés"
-#: src/pyams_portal/zmi/layout.py:182
+#: src/pyams_portal/zmi/layout.py:168
msgid "Add row..."
msgstr "Ajouter une ligne"
-#: src/pyams_portal/zmi/layout.py:224
+#: src/pyams_portal/zmi/layout.py:210
msgid "Add slot..."
msgstr "Ajouter un panneau"
-#: src/pyams_portal/zmi/layout.py:242
+#: src/pyams_portal/zmi/layout.py:228
msgid "Add slot"
msgstr "Ajouter un panneau"
-#: src/pyams_portal/zmi/layout.py:405
+#: src/pyams_portal/zmi/layout.py:392
msgid "Add portlet..."
msgstr "Ajouter un composant"
-#: src/pyams_portal/zmi/layout.py:423
+#: src/pyams_portal/zmi/layout.py:410
msgid "Add portlet"
msgstr "Ajouter un composant"
-#: src/pyams_portal/zmi/layout.py:600
-msgid "Duplicate template..."
-msgstr "Dupliquer le modèle"
-
-#: src/pyams_portal/zmi/layout.py:620
-msgid "Duplicate template"
-msgstr "Dupliquer le modèle"
-
-#: src/pyams_portal/zmi/layout.py:71
+#: src/pyams_portal/zmi/layout.py:65
msgid "Template management"
msgstr "Ce modèle"
-#: src/pyams_portal/zmi/layout.py:108
+#: src/pyams_portal/zmi/layout.py:102
msgid "Template configuration"
msgstr "Configuration d'un modèle"
-#: src/pyams_portal/zmi/layout.py:610
-msgid "Cancel"
-msgstr "Annuler"
-
-#: src/pyams_portal/zmi/layout.py:611
-msgid "Duplicate this template"
-msgstr "Dupliquer ce modèle"
-
-#: src/pyams_portal/zmi/layout.py:631
-msgid "New template name"
-msgstr "Nom du nouveau modèle"
-
-#: src/pyams_portal/zmi/layout.py:100
+#: src/pyams_portal/zmi/layout.py:94
msgid "Local template configuration"
msgstr "Configuration d'un modèle local"
-#: src/pyams_portal/zmi/layout.py:151
+#: src/pyams_portal/zmi/layout.py:145
msgid "{{ missing portlet }}"
msgstr "{{ composant indisponible }}"
-#: src/pyams_portal/zmi/layout.py:281 src/pyams_portal/zmi/layout.py:661
-#: src/pyams_portal/zmi/template.py:167
+#: src/pyams_portal/zmi/layout.py:267 src/pyams_portal/zmi/template.py:167
+#: src/pyams_portal/zmi/template.py:316
msgid "Specified name is already used!"
msgstr "Le nom indiqué est déjà utilisé !"
-#: src/pyams_portal/zmi/layout.py:103
+#: src/pyams_portal/zmi/layout.py:97
msgid "Inherited local template configuration"
msgstr "Configuration d'un modèle local hérité"
-#: src/pyams_portal/zmi/layout.py:142
+#: src/pyams_portal/zmi/layout.py:136
#, python-format
msgid ""
"Add component: {0}<br />Drag and drop button to page template to position "
@@ -355,27 +335,27 @@
"Ajouter un composant : <strong>{0}</strong><br />Faire un glisser/déposer du "
"bouton dans le modèle de présentation pour positionner le nouveau composant."
-#: src/pyams_portal/zmi/layout.py:240 src/pyams_portal/zmi/layout.py:337
-#: src/pyams_portal/zmi/layout.py:421 src/pyams_portal/zmi/template.py:111
+#: src/pyams_portal/zmi/layout.py:226 src/pyams_portal/zmi/layout.py:323
+#: src/pyams_portal/zmi/layout.py:408 src/pyams_portal/zmi/template.py:111
#, python-format
msgid "« {0} » portal template"
msgstr "Modèle de présentation « {0} »"
-#: src/pyams_portal/zmi/layout.py:342
+#: src/pyams_portal/zmi/layout.py:328
#, python-format
msgid "Edit « {0} » slot properties"
msgstr "Propriétés du panneau « {0} »"
-#: src/pyams_portal/zmi/layout.py:287
+#: src/pyams_portal/zmi/layout.py:273
msgid "Row ID must be an integer value!"
msgstr "Le numéro de ligne doit être un nombre entier !"
-#: src/pyams_portal/zmi/layout.py:106
+#: src/pyams_portal/zmi/layout.py:100
#, python-format
msgid "Shared template configuration ({0})"
msgstr "Configuration d'un modèle partagé ({0})"
-#: src/pyams_portal/zmi/layout.py:291
+#: src/pyams_portal/zmi/layout.py:277
#, python-format
msgid "Row ID must be between 1 and {0}!"
msgstr "Le numéro de ligne doit être compris entre 1 et {0}"
@@ -388,10 +368,38 @@
msgid "Add shared template"
msgstr "Ajout d'un modèle de présentation"
+#: src/pyams_portal/zmi/template.py:186
+msgid "Rename template..."
+msgstr "Renommer le modèle"
+
+#: src/pyams_portal/zmi/template.py:206 src/pyams_portal/zmi/template.py:197
+msgid "Rename template"
+msgstr "Renommer le modèle"
+
+#: src/pyams_portal/zmi/template.py:255
+msgid "Duplicate template..."
+msgstr "Dupliquer le modèle"
+
+#: src/pyams_portal/zmi/template.py:275
+msgid "Duplicate template"
+msgstr "Dupliquer le modèle"
+
#: src/pyams_portal/zmi/template.py:60
msgid "Presentation template"
msgstr "Présentation"
+#: src/pyams_portal/zmi/template.py:196 src/pyams_portal/zmi/template.py:265
+msgid "Cancel"
+msgstr "Annuler"
+
+#: src/pyams_portal/zmi/template.py:266
+msgid "Duplicate this template"
+msgstr "Dupliquer ce modèle"
+
+#: src/pyams_portal/zmi/template.py:286
+msgid "New template name"
+msgstr "Nom du nouveau modèle"
+
#: src/pyams_portal/zmi/template.py:87
#, python-format
msgid "{0} (local template)"
@@ -467,56 +475,52 @@
msgstr "Type de périphérique sélectionné :"
#: src/pyams_portal/zmi/templates/layout.pt:62
-msgid "Current device"
-msgstr "Périphérique actuel"
-
-#: src/pyams_portal/zmi/templates/layout.pt:63
msgid "Extra small device (phone)"
msgstr "Très petits périphériques (téléphone)"
-#: src/pyams_portal/zmi/templates/layout.pt:64
+#: src/pyams_portal/zmi/templates/layout.pt:63
msgid "Small device (tablet)"
msgstr "Petits périphériques (tablette)"
-#: src/pyams_portal/zmi/templates/layout.pt:65
+#: src/pyams_portal/zmi/templates/layout.pt:64
msgid "Medium desktop device (> 970px)"
msgstr "Écrans de taille moyenne (> 970 px)"
-#: src/pyams_portal/zmi/templates/layout.pt:66
+#: src/pyams_portal/zmi/templates/layout.pt:65
msgid "Large desktop device (> 1170px)"
msgstr "Écrans de grande taille (> 1170 px)"
-#: src/pyams_portal/zmi/templates/layout.pt:115
+#: src/pyams_portal/zmi/templates/layout.pt:114
msgid "Reduce/restore portlet"
msgstr "Réduire/restaurer le composant"
-#: src/pyams_portal/zmi/templates/layout.pt:120
+#: src/pyams_portal/zmi/templates/layout.pt:119
msgid "Edit portlet properties"
msgstr "Propriétés"
-#: src/pyams_portal/zmi/templates/layout.pt:122
+#: src/pyams_portal/zmi/templates/layout.pt:121
msgid "Portlet settings are not those of original template"
msgstr ""
"Les propriétés du composant ne sont pas celles héritées du modèle de "
"présentation"
-#: src/pyams_portal/zmi/templates/layout.pt:140
+#: src/pyams_portal/zmi/templates/layout.pt:139
msgid "Delete row..."
msgstr "Supprimer la ligne"
-#: src/pyams_portal/zmi/templates/layout.pt:149
+#: src/pyams_portal/zmi/templates/layout.pt:148
msgid "Edit slot properties..."
msgstr "Propriétés"
-#: src/pyams_portal/zmi/templates/layout.pt:156
+#: src/pyams_portal/zmi/templates/layout.pt:155
msgid "Delete slot..."
msgstr "Supprimer le panneau"
-#: src/pyams_portal/zmi/templates/layout.pt:164
+#: src/pyams_portal/zmi/templates/layout.pt:163
msgid "Edit portlet properties..."
msgstr "Propriétés"
-#: src/pyams_portal/zmi/templates/layout.pt:172
+#: src/pyams_portal/zmi/templates/layout.pt:171
msgid "Delete portlet..."
msgstr "Supprimer le composant"
@@ -628,6 +632,9 @@
msgid "Responsive image renderer"
msgstr "Image responsive (par défaut)"
+#~ msgid "Current device"
+#~ msgstr "Périphérique actuel"
+
#~ msgid "Double spacer with horizontal ruler"
#~ msgstr "Espace double avec trait horizontal"
--- a/src/pyams_portal/locales/pyams_portal.pot Mon Nov 23 17:20:28 2020 +0100
+++ b/src/pyams_portal/locales/pyams_portal.pot Tue Nov 24 10:43:30 2020 +0100
@@ -1,12 +1,12 @@
#
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2019-09-24 11:54+0200\n"
+"POT-Creation-Date: 2020-11-24 09:57+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,7 +16,7 @@
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 3.10.dev0\n"
-#: ./src/pyams_portal/portlet.py:177
+#: ./src/pyams_portal/portlet.py:180
msgid "Renderer:"
msgstr ""
@@ -185,7 +185,7 @@
msgid "Template used for this page"
msgstr ""
-#: ./src/pyams_portal/template.py:81
+#: ./src/pyams_portal/template.py:84
msgid "Portal template"
msgstr ""
@@ -257,103 +257,83 @@
"properties..."
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:81
+#: ./src/pyams_portal/zmi/layout.py:75
msgid "Properties"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:182
+#: ./src/pyams_portal/zmi/layout.py:168
msgid "Add row..."
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:224
+#: ./src/pyams_portal/zmi/layout.py:210
msgid "Add slot..."
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:242
+#: ./src/pyams_portal/zmi/layout.py:228
msgid "Add slot"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:405
+#: ./src/pyams_portal/zmi/layout.py:392
msgid "Add portlet..."
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:423
+#: ./src/pyams_portal/zmi/layout.py:410
msgid "Add portlet"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:600
-msgid "Duplicate template..."
-msgstr ""
-
-#: ./src/pyams_portal/zmi/layout.py:620
-msgid "Duplicate template"
-msgstr ""
-
-#: ./src/pyams_portal/zmi/layout.py:71
+#: ./src/pyams_portal/zmi/layout.py:65
msgid "Template management"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:108
+#: ./src/pyams_portal/zmi/layout.py:102
msgid "Template configuration"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:610
-msgid "Cancel"
-msgstr ""
-
-#: ./src/pyams_portal/zmi/layout.py:611
-msgid "Duplicate this template"
-msgstr ""
-
-#: ./src/pyams_portal/zmi/layout.py:631
-msgid "New template name"
-msgstr ""
-
-#: ./src/pyams_portal/zmi/layout.py:100
+#: ./src/pyams_portal/zmi/layout.py:94
msgid "Local template configuration"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:151
+#: ./src/pyams_portal/zmi/layout.py:145
msgid "{{ missing portlet }}"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:281 ./src/pyams_portal/zmi/layout.py:661
-#: ./src/pyams_portal/zmi/template.py:167
+#: ./src/pyams_portal/zmi/layout.py:267 ./src/pyams_portal/zmi/template.py:167
+#: ./src/pyams_portal/zmi/template.py:316
msgid "Specified name is already used!"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:103
+#: ./src/pyams_portal/zmi/layout.py:97
msgid "Inherited local template configuration"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:142
+#: ./src/pyams_portal/zmi/layout.py:136
#, python-format
msgid ""
"Add component: {0}<br />Drag and drop button to page template to position new"
" row"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:240 ./src/pyams_portal/zmi/layout.py:337
-#: ./src/pyams_portal/zmi/layout.py:421 ./src/pyams_portal/zmi/template.py:111
+#: ./src/pyams_portal/zmi/layout.py:226 ./src/pyams_portal/zmi/layout.py:323
+#: ./src/pyams_portal/zmi/layout.py:408 ./src/pyams_portal/zmi/template.py:111
#, python-format
msgid "« {0} » portal template"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:342
+#: ./src/pyams_portal/zmi/layout.py:328
#, python-format
msgid "Edit « {0} » slot properties"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:287
+#: ./src/pyams_portal/zmi/layout.py:273
msgid "Row ID must be an integer value!"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:106
+#: ./src/pyams_portal/zmi/layout.py:100
#, python-format
msgid "Shared template configuration ({0})"
msgstr ""
-#: ./src/pyams_portal/zmi/layout.py:291
+#: ./src/pyams_portal/zmi/layout.py:277
#, python-format
msgid "Row ID must be between 1 and {0}!"
msgstr ""
@@ -366,10 +346,40 @@
msgid "Add shared template"
msgstr ""
+#: ./src/pyams_portal/zmi/template.py:186
+msgid "Rename template..."
+msgstr ""
+
+#: ./src/pyams_portal/zmi/template.py:206
+#: ./src/pyams_portal/zmi/template.py:197
+msgid "Rename template"
+msgstr ""
+
+#: ./src/pyams_portal/zmi/template.py:255
+msgid "Duplicate template..."
+msgstr ""
+
+#: ./src/pyams_portal/zmi/template.py:275
+msgid "Duplicate template"
+msgstr ""
+
#: ./src/pyams_portal/zmi/template.py:60
msgid "Presentation template"
msgstr ""
+#: ./src/pyams_portal/zmi/template.py:196
+#: ./src/pyams_portal/zmi/template.py:265
+msgid "Cancel"
+msgstr ""
+
+#: ./src/pyams_portal/zmi/template.py:266
+msgid "Duplicate this template"
+msgstr ""
+
+#: ./src/pyams_portal/zmi/template.py:286
+msgid "New template name"
+msgstr ""
+
#: ./src/pyams_portal/zmi/template.py:87
#, python-format
msgid "{0} (local template)"
@@ -432,54 +442,50 @@
msgstr ""
#: ./src/pyams_portal/zmi/templates/layout.pt:62
-msgid "Current device"
+msgid "Extra small device (phone)"
msgstr ""
#: ./src/pyams_portal/zmi/templates/layout.pt:63
-msgid "Extra small device (phone)"
+msgid "Small device (tablet)"
msgstr ""
#: ./src/pyams_portal/zmi/templates/layout.pt:64
-msgid "Small device (tablet)"
+msgid "Medium desktop device (> 970px)"
msgstr ""
#: ./src/pyams_portal/zmi/templates/layout.pt:65
-msgid "Medium desktop device (> 970px)"
-msgstr ""
-
-#: ./src/pyams_portal/zmi/templates/layout.pt:66
msgid "Large desktop device (> 1170px)"
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:115
+#: ./src/pyams_portal/zmi/templates/layout.pt:114
msgid "Reduce/restore portlet"
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:120
+#: ./src/pyams_portal/zmi/templates/layout.pt:119
msgid "Edit portlet properties"
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:122
+#: ./src/pyams_portal/zmi/templates/layout.pt:121
msgid "Portlet settings are not those of original template"
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:140
+#: ./src/pyams_portal/zmi/templates/layout.pt:139
msgid "Delete row..."
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:149
+#: ./src/pyams_portal/zmi/templates/layout.pt:148
msgid "Edit slot properties..."
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:156
+#: ./src/pyams_portal/zmi/templates/layout.pt:155
msgid "Delete slot..."
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:164
+#: ./src/pyams_portal/zmi/templates/layout.pt:163
msgid "Edit portlet properties..."
msgstr ""
-#: ./src/pyams_portal/zmi/templates/layout.pt:172
+#: ./src/pyams_portal/zmi/templates/layout.pt:171
msgid "Delete portlet..."
msgstr ""
--- a/src/pyams_portal/site.py Mon Nov 23 17:20:28 2020 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#
-# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-
-__docformat__ = 'restructuredtext'
-
-
-# import standard library
-
-# import interfaces
-from pyams_portal.interfaces import IPortalTemplateContainer
-from pyams_utils.interfaces.site import ISiteGenerations
-
-# import packages
-from pyams_portal.template import PortalTemplateContainer
-from pyams_utils.registry import utility_config
-from pyams_utils.site import check_required_utilities
-
-
-REQUIRED_UTILITIES = ((IPortalTemplateContainer, '', PortalTemplateContainer, 'Portal templates'), )
-
-
-@utility_config(name='PyAMS portal', provides=ISiteGenerations)
-class PortalGenerationsChecker(object):
- """Portal generations checker"""
-
- order = 70
- generation = 1
-
- def evolve(self, site, current=None):
- """Check for required utilities"""
- check_required_utilities(site, REQUIRED_UTILITIES)
--- a/src/pyams_portal/template.py Mon Nov 23 17:20:28 2020 +0100
+++ b/src/pyams_portal/template.py Tue Nov 24 10:43:30 2020 +0100
@@ -10,33 +10,35 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
from persistent import Persistent
from persistent.list import PersistentList
from persistent.mapping import PersistentMapping
from pyramid.events import subscriber
from pyramid.threadlocal import get_current_registry
-from zope.componentvocabulary.vocabulary import UtilityVocabulary
from zope.container.contained import Contained
from zope.container.folder import Folder
from zope.interface import implementer
-from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
+from zope.intid.interfaces import IIntIdAddedEvent, IIntIdRemovedEvent
from zope.location import locate
from zope.schema.fieldproperty import FieldProperty
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
from zope.traversing.interfaces import ITraversable
-from pyams_portal.interfaces import IPortalPortletsConfiguration, IPortalTemplate, IPortalTemplateConfiguration, \
- IPortalTemplateContainer, IPortalTemplateContainerConfiguration, IPortlet, IPortletConfiguration, \
- PORTLETS_CONFIGURATION_KEY, TEMPLATE_CONFIGURATION_KEY, TEMPLATE_CONTAINER_CONFIGURATION_KEY
+from pyams_portal.interfaces import IPortalPortletsConfiguration, IPortalTemplate, \
+ IPortalTemplateConfiguration, IPortalTemplateContainer, IPortalTemplateContainerConfiguration, \
+ IPortlet, IPortletConfiguration, PORTLETS_CONFIGURATION_KEY, TEMPLATE_CONFIGURATION_KEY, \
+ TEMPLATE_CONTAINER_CONFIGURATION_KEY
from pyams_portal.slot import SlotConfiguration
from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
from pyams_utils.factory import factory_config
-from pyams_utils.registry import get_local_registry, get_utility
+from pyams_utils.interfaces.intids import IUniqueID
+from pyams_utils.registry import get_local_registry, get_utilities_for, get_utility
from pyams_utils.request import check_request
from pyams_utils.vocabulary import vocabulary_config
+
+__docformat__ = 'restructuredtext'
+
from pyams_portal import _
@@ -65,7 +67,8 @@
@adapter_config(context=IPortalTemplateContainer, provides=IPortalTemplateContainerConfiguration)
def portal_template_container_configuration_adapter(context):
"""Portal template container configuration factory"""
- return get_annotation_adapter(context, TEMPLATE_CONTAINER_CONFIGURATION_KEY, IPortalTemplateContainerConfiguration)
+ return get_annotation_adapter(context, TEMPLATE_CONTAINER_CONFIGURATION_KEY,
+ IPortalTemplateContainerConfiguration)
#
@@ -81,28 +84,35 @@
content_name = _("Portal template")
-@subscriber(IObjectAddedEvent, context_selector=IPortalTemplate)
+@subscriber(IIntIdAddedEvent, context_selector=IPortalTemplate)
def handle_added_template(event):
"""Register shared template"""
+ template = event.object
registry = get_local_registry()
- if (registry is not None) and IPortalTemplateContainer.providedBy(event.newParent):
- registry.registerUtility(event.object, IPortalTemplate, name=event.object.name)
+ if (registry is not None) and IPortalTemplateContainer.providedBy(template.__parent__):
+ registry.registerUtility(template, IPortalTemplate,
+ name=IUniqueID(template).oid)
-@subscriber(IObjectRemovedEvent, context_selector=IPortalTemplate)
+@subscriber(IIntIdRemovedEvent, context_selector=IPortalTemplate)
def handle_removed_template(event):
"""Unregister removed template"""
+ template = event.object
registry = get_local_registry()
- if (registry is not None) and IPortalTemplateContainer.providedBy(event.oldParent):
- registry.unregisterUtility(event.object, IPortalTemplate, name=event.object.name)
+ if (registry is not None) and IPortalTemplateContainer.providedBy(template.__parent__):
+ registry.unregisterUtility(template, IPortalTemplate,
+ name=IUniqueID(template).oid)
@vocabulary_config(name='PyAMS portal templates')
-class PortalTemplatesVocabulary(UtilityVocabulary):
+class PortalTemplatesVocabulary(SimpleVocabulary):
"""Portal templates vocabulary"""
- interface = IPortalTemplate
- nameOnly = True
+ def __init__(self, context):
+ terms = sorted([SimpleTerm(name, title=util.name)
+ for (name, util) in get_utilities_for(IPortalTemplate)],
+ key=lambda x: x.title)
+ super(PortalTemplatesVocabulary, self).__init__(terms)
#
--- a/src/pyams_portal/zmi/layout.py Mon Nov 23 17:20:28 2020 +0100
+++ b/src/pyams_portal/zmi/layout.py Tue Nov 24 10:43:30 2020 +0100
@@ -13,7 +13,6 @@
__docformat__ = 'restructuredtext'
-# import standard library
import json
from pyramid.decorator import reify
@@ -21,35 +20,28 @@
from pyramid.exceptions import NotFound
from pyramid.view import view_config
from transaction.interfaces import ITransactionManager
-from z3c.form import button, field
+from z3c.form import field
from z3c.form.interfaces import HIDDEN_MODE, IDataExtractedEvent
-from zope.copy import copy
-from zope.interface import Interface, Invalid, implementer
+from zope.interface import Invalid, implementer
-# import packages
from pyams_cache.beaker import get_cache
from pyams_form.form import AJAXAddForm, AJAXEditForm, ajax_config
-from pyams_form.schema import CloseButton
-# import interfaces
from pyams_pagelet.interfaces import IPagelet, PageletCreatedEvent
from pyams_pagelet.pagelet import pagelet_config
-from pyams_portal import _
-from pyams_portal.interfaces import IPortalContext, IPortalPage, IPortalPortletsConfiguration, IPortalTemplate, \
- IPortalTemplateConfiguration, IPortalTemplateContainer, IPortalTemplateContainerConfiguration, IPortlet, \
- IPortletAddingInfo, IPortletPreviewer, ISlot, ISlotConfiguration, LOCAL_TEMPLATE_NAME, MANAGE_TEMPLATE_PERMISSION
+from pyams_portal.interfaces import IPortalContext, IPortalPage, IPortalPortletsConfiguration, \
+ IPortalTemplate, IPortalTemplateConfiguration, IPortalTemplateContainer, \
+ IPortalTemplateContainerConfiguration, IPortlet, IPortletAddingInfo, IPortletPreviewer, \
+ ISlot, ISlotConfiguration, LOCAL_TEMPLATE_NAME, MANAGE_TEMPLATE_PERMISSION
from pyams_portal.portlet import PORTLETS_CACHE_NAME, PORTLETS_CACHE_REGION
-from pyams_portal.zmi.template import PortalTemplateHeaderAdapter
-from pyams_skin.interfaces import IInnerPage, IPageHeader
-from pyams_skin.interfaces.viewlet import IContextActions, IMenuHeader, IToolbarAddingMenu
+from pyams_skin.interfaces import IInnerPage
+from pyams_skin.interfaces.viewlet import IMenuHeader, IToolbarAddingMenu
from pyams_skin.layer import IPyAMSLayer
from pyams_skin.viewlet.menu import MenuItem
from pyams_skin.viewlet.toolbar import JsToolbarMenuItem, ToolbarMenuDivider, ToolbarMenuItem
from pyams_template.template import template_config
from pyams_utils.adapter import adapter_config
-from pyams_utils.registry import get_utility, query_utility
+from pyams_utils.registry import 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.manager import viewletmanager_config
from pyams_viewlet.viewlet import viewlet_config
from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
@@ -57,6 +49,8 @@
from pyams_zmi.layer import IAdminLayer
from pyams_zmi.view import AdminView
+from pyams_portal import _
+
@adapter_config(context=(IPortalTemplate, IContentManagementMenu), provides=IMenuHeader)
class PortalTemplateMenuHeader(object):
@@ -161,14 +155,6 @@
return ''
-@adapter_config(context=(IPortalTemplate, IAdminLayer, Interface), provides=IPageHeader)
-class PortalTemplateLayoutHeaderAdapter(PortalTemplateHeaderAdapter):
- """Portal template configuration header adapter"""
-
- back_url = '/admin#portal-templates.html'
- back_target = None
-
-
#
# Rows views
#
@@ -573,94 +559,3 @@
config = IPortalTemplateConfiguration(request.context)
config.delete_portlet(int(request.params.get('portlet_id')))
return {'status': 'success'}
-
-
-#
-# Portal template duplication form
-#
-
-@viewlet_config(name='duplication.divider', context=IPortalTemplate, layer=IPyAMSLayer,
- view=PortalTemplateLayoutView, manager=IContextActions, permission=MANAGE_TEMPLATE_PERMISSION, weight=99)
-class PortalTemplateDuplicationMenuDivider(ToolbarMenuDivider):
- """Portal template duplication menu divider"""
-
- def __new__(cls, context, request, view, manager):
- container = get_parent(context, IPortalTemplateContainer)
- if container is None:
- return None
- return ToolbarMenuDivider.__new__(cls)
-
-
-@viewlet_config(name='duplication.menu', context=IPortalTemplate, layer=IPyAMSLayer,
- view=PortalTemplateLayoutView, manager=IContextActions, permission=MANAGE_TEMPLATE_PERMISSION, weight=100)
-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!")),)
--- 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!")),)