# HG changeset patch # User Thierry Florac # Date 1606211010 -3600 # Node ID c025abc00397b33ffe9580ffba658f9d861b73ba # Parent e4b77545c2f140b0cc5d18aecf770005b03257a8 Updated templates registration and added option to rename templates diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/generations/__init__.py --- /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 +# 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) diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/generations/evolve1.py --- /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 +# 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) diff -r e4b77545c2f1 -r c025abc00397 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 e4b77545c2f1 -r c025abc00397 src/pyams_portal/locales/fr/LC_MESSAGES/pyams_portal.po --- 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 \n" "Language-Team: French \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}
Drag and drop button to page template to position " @@ -355,27 +335,27 @@ "Ajouter un composant : {0}
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" diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/locales/pyams_portal.pot --- 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 , 2019. +# FIRST AUTHOR , 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 \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}
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 "" diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/site.py --- 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 -# 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) diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/template.py --- 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) # diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/zmi/layout.py --- 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!")),) diff -r e4b77545c2f1 -r c025abc00397 src/pyams_portal/zmi/template.py --- 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!")),)