From 091a89adb64fc9f21b8b7c6815c575a1d6435fc6 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 6 Nov 2024 00:42:34 -0800 Subject: [PATCH 01/42] Add Create a backend add-on section in the Developer guide. Ping @petschki - Enhance the Development Guide index - Update the term Buildout in the Glossary to point to dusty old, yet useful, docs in Plone 4. (cherry picked from commit 8142b968a75133aca0bf0a958135b7604418cfb5) --- .../create-a-backend-add-on.md | 21 +++++ docs/developer-guide/create-a-distribution.md | 5 +- docs/developer-guide/index.md | 85 +++++++++++++++++-- docs/glossary.md | 4 + docs/i18n-l10n/translating-text-strings.md | 4 +- 5 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 docs/developer-guide/create-a-backend-add-on.md diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md new file mode 100644 index 0000000000..f5249080f9 --- /dev/null +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -0,0 +1,21 @@ +--- +myst: + html_meta: + "description": "How to create a custom Plone distribution" + "property=og:description": "How to create a custom Plone distribution" + "property=og:title": "Create a Plone distribution" + "keywords": "Plone 6, distribution, plone.distribution" +--- + +(create-a-backend-add-on-label)= + +# Create a backend add-on + +This section explains how a developer can create an {term}`add-on` for the Plone backend. +You will use a framework called {term}`GenericSetup`. + +```{note} +This section for Plone 6 is in the process of being written. +Until it is complete, Plone 5 documentation is the authoritative source. +{doc}`plone5:develop/addons/components/genericsetup` (Plone 5) +``` diff --git a/docs/developer-guide/create-a-distribution.md b/docs/developer-guide/create-a-distribution.md index d58e4f3bde..b1b7cafa64 100644 --- a/docs/developer-guide/create-a-distribution.md +++ b/docs/developer-guide/create-a-distribution.md @@ -19,10 +19,9 @@ For a conceptual guide, see {doc}`/conceptual-guides/distributions`. ``` -## Create a backend add-on +## Use a backend add-on -These instructions assume that you already have created a Plone backend add-on package, -and now you want to add a distribution to it. +These instructions assume that you have already {doc}`created a Plone backend add-on package `, and now you want to add a distribution to it. A Plone distribution exists inside a Python package that can be installed by `pip`. diff --git a/docs/developer-guide/index.md b/docs/developer-guide/index.md index 797edd0d72..08497c8385 100644 --- a/docs/developer-guide/index.md +++ b/docs/developer-guide/index.md @@ -1,25 +1,94 @@ --- myst: html_meta: - "description": "Plone developer guide" - "property=og:description": "Plone developer guide" - "property=og:title": "Developer guide" - "keywords": "Plone 6, developer guide, development" + "description": "Plone development guide" + "property=og:description": "Plone development guide" + "property=og:title": "Development guide" + "keywords": "Plone 6, development guide, developer, development" --- -# Developer guide +# Development guide + +```{note} +This part of the documentation is under revision, consolidating documentation for development from its various locations into in one section. +Until then, you can use the [search feature](https://6.docs.plone.org/search.html?q=development). +You can also help with this effort, even if it is to report that something is missing, by [creating an issue](https://github.com/plone/documentation/issues/new?assignees=&labels=&projects=&template=new-issue-form.yml). +``` This part of the documentation provides information for how to develop in Plone. +This development guide points you, as a developer, to the appropriate resource. + + +## Tests + +Tests ensure that your project functions as expected, and that changes to the code base during development don't break anything. + + +### Volto + +- {doc}`Volto acceptance tests ` +- {doc}`Volto unit tests ` +- {ref}`testing-the-add-on-label` + + +### Classic UI ```{note} -This part of the documentation is under construction. -You can find documentation for development in various locations through the [search feature](https://6.docs.plone.org/search.html?q=development). -You can help consolidate all of development documentation here, even if it is to let us know what is missing, by [creating an issue](https://github.com/plone/documentation/issues/new?assignees=&labels=&projects=&template=new-issue-form.yml). +Classic UI testing for Plone 6 is in the process of being written. ``` +### Backend + +```{note} +Backend testing for Plone 6 is in the process of being written. +Until it is complete, Plone 5 documentation is the authoritative source for writing tests for the Plone backend. +``` + +- {doc}`Backend tests ` (Plone 5) + + +## Create an add-on + +- {doc}`create-a-backend-add-on` +- {doc}`/volto/development/add-ons/create-an-add-on-18` + + +## Create a Plone distribution + +{doc}`create-a-distribution` + + +## Create content types + +- {doc}`/backend/content-types/creating-content-types` +- {doc}`plone5:develop/plone/content/index` (Plone 5) + + +## Register views + +{doc}`plone5:develop/plone/views/index` (Plone 5) + + +## Register API services + +{doc}`backend/configuration-registry` + + +## {term}`ZCA` +% TODO: This is a mixture of conceptual and how-to guides. Move its parts where they belong and rewrite. +{doc}`plone5:develop/addons/components/index` (Plone 5) + + +## {term}`ZCML` +% TODO: This is a mixture of conceptual and how-to guides. Move its parts where they belong and rewrite. +{doc}`plone5:develop/addons/components/zcml` (Plone 5) + + ```{toctree} :maxdepth: 2 +:hidden: +create-a-backend-add-on create-a-distribution ``` diff --git a/docs/glossary.md b/docs/glossary.md index ed4b20897b..58c64ed397 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -25,6 +25,10 @@ Buildout [Buildout](https://github.com/buildout/buildout/) is a Python-based tool for building and assembling applications from multiple parts, based on a configuration file. It was the most common way of installing Plone 3, 4, and 5, and can still be used with Plone 6. + Usage of Buildout in Plone appears in various places in this documentation. + For a history and extended usage of Buildout, you can refer to the Plone 4 Documentation's section on [Buildout](https://4.docs.plone.org/old-reference-manuals/buildout/). + The Plone community authored this reference manual, as Buildout's own documentation is suboptimal. + CMS Content Management System diff --git a/docs/i18n-l10n/translating-text-strings.md b/docs/i18n-l10n/translating-text-strings.md index 22d307e883..d69c4ec15d 100644 --- a/docs/i18n-l10n/translating-text-strings.md +++ b/docs/i18n-l10n/translating-text-strings.md @@ -56,7 +56,7 @@ Information in the PO file headers is ignored. [`i18ndude`](https://pypi.org/project/i18ndude/) should be used to create a script which searches particular packages for translation strings. -If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your buildout to extract the messages from your code. +If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your {term}`buildout` to extract the messages from your code. After running that script, a new `domain.pot` file will be created in your `locales` directory where all the messages will be saved. @@ -273,7 +273,7 @@ This script hooks into the release process and builds the MO files for you. ### Installing i18ndude -The recommended method is to have {term}`i18ndude` installed via your [buildout](https://www.buildout.org/en/latest/). +The recommended method is to have {term}`i18ndude` installed via your buildout. Add the following to your `buildout.cfg`: From a766f5adf0c27b6868a41b5efa2c0873e4e44f43 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 6 Nov 2024 00:56:24 -0800 Subject: [PATCH 02/42] - Update meta information - Add term reference for Buildout --- docs/developer-guide/create-a-backend-add-on.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index f5249080f9..b6927f8355 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -1,10 +1,10 @@ --- myst: html_meta: - "description": "How to create a custom Plone distribution" - "property=og:description": "How to create a custom Plone distribution" - "property=og:title": "Create a Plone distribution" - "keywords": "Plone 6, distribution, plone.distribution" + "description": "How to create a backend add-on" + "property=og:description": "How to create a backend add-on" + "property=og:title": "Create a backend add-on" + "keywords": "Plone 6, backend, add-on" --- (create-a-backend-add-on-label)= @@ -12,7 +12,7 @@ myst: # Create a backend add-on This section explains how a developer can create an {term}`add-on` for the Plone backend. -You will use a framework called {term}`GenericSetup`. +You will use a framework called {term}`GenericSetup` and a tool called {term}`Buildout`. ```{note} This section for Plone 6 is in the process of being written. From 67b1ee7bad442473eedffc1f7bd0b152acadab9d Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 25 Nov 2025 15:46:28 +0100 Subject: [PATCH 03/42] create backend addon documentation --- .../create-a-backend-add-on.md | 220 +++++++++++++++++- 1 file changed, 215 insertions(+), 5 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index b6927f8355..500c777c25 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -12,10 +12,220 @@ myst: # Create a backend add-on This section explains how a developer can create an {term}`add-on` for the Plone backend. -You will use a framework called {term}`GenericSetup` and a tool called {term}`Buildout`. -```{note} -This section for Plone 6 is in the process of being written. -Until it is complete, Plone 5 documentation is the authoritative source. -{doc}`plone5:develop/addons/components/genericsetup` (Plone 5) +## System requirements + +Follow the section {ref}`create-project-cookieplone-system-requirements` to set up your system. + +## Generate the add-on project with `cookieplone` + +To develop an add-on for backend and/or Classic-UI, run the following command to generate your add-on project using the `backend_addon` Cookieplone template. +See {doc}`plone:install/create-project-cookieplone` for details of the latter scenario. +The following output assumes the former scenario. + +```shell +uvx cookieplone backend_addon +``` + +```console +> uvx cookieplone backend_addon +╭──────────────────────────────── cookieplone ─────────────────────────────────╮ +│ │ +│ ******* │ +│ *************** │ +│ *** *** │ +│ *** *** *** │ +│ *** ***** *** │ +│ *** *** *** │ +│ *** *** *** │ +│ *** ***** *** │ +│ *** *** *** *** │ +│ *** ***** *** │ +│ *** *** *** │ +│ *** *** │ +│ *************** │ +│ ******* │ +│ │ +╰──────────────────────────────────────────────────────────────────────────────╯ +You've downloaded /Users//.cookiecutters/cookieplone-templates before. Is +it okay to delete and re-download it? [y/n] (y): +╭──────────────────────────────── Plone Addon ─────────────────────────────────╮ +│ │ +│ Creating a new Plone Addon │ +│ │ +│ Sanity check results: │ +│ │ +│ - Cookieplone: ✓ │ +│ - uv: ✓ │ +│ - git: ✓ │ +│ │ +╰──────────────────────────────────────────────────────────────────────────────╯ + [1/10] Addon Title (Addon): + [2/10] A short description of your addon (A new addon for Plone): + [3/10] Author (Plone Community): + [4/10] Author E-mail (collective@plone.org): + [5/10] GitHub Username or Organization (collective): + [6/10] Should we use prerelease versions? (No): + [7/10] Plone Version (6.1.3): + [8/10] Python package name (collective.addon): + [9/10] Support headless Plone? + 1 - Yes + 2 - No + Choose from [1/2] (1): + [10/10] Would you like to add a documentation scaffold to your project? + 1 - Yes + 2 - No + Choose from [1/2] (1): + -> Remove files used in classic UI setup + -> Create namespace packages + -> Format code + -> Initialize Git repository + -> Generate documentation scaffold +╭────────────────────────── New addon was generated ───────────────────────────╮ +│ │ +│ Addon │ +│ │ +│ Now, enter the repository, start coding, and push to your organization. │ +│ │ +│ Sorry for the convenience, │ +│ The Plone Community. │ +│ │ +│ https://plone.org/ │ +╰──────────────────────────────────────────────────────────────────────────────╯ +``` + +Cookieplone creates a folder with the name of the add-on, in this example, `collective.addon`. + +You can now continue to add subtemplates to your addon {ref}`create-a-backend-add-on-add-subtemplate-label` + + +## Generate the add-on project with `plonecli` + +Run the following command to create an addon project with `plonecli` + +```shell +uvx plonecli create addon +``` + +```console +> uvx plonecli create addon collective.addon +RUN: bobtemplates.plone:addon -O collective.addon + +Welcome to mr.bob interactive mode. Before we generate directory structure, +some questions need to be answered. + +Answer with a question mark to display help. +Values in square brackets at the end of the questions show the default value if +there is no answer. + + +--> Package description [An add-on for Plone]: + +--> Plone version [6.0.0]: + +--> Python version for virtualenv [python3]: + +--> Do you want me to activate VS Code support? (y/n) [y]: + + + +isort-apply: successful: +isort-apply: install_deps> python -I -m pip install isort -c constraints.txt +isort-apply: commands[0]> isort /Users//Development/collective.addon/src +/Users//Development/collective.addon/setup.py +Fixing /Users//Development/collective.addon/src/collective/addon/testing.py +Fixing /Users//Development/collective.addon/src/collective/addon/tests/test_setup.py + isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds) + congratulations :) (2.59 seconds) + + +Identified `/` as project root containing a file system root. +Sources to be formatted: "Users//Development/collective.addon/src", + "Users//Development/collective.addon/setup.py" +src/collective/__init__.py wasn't modified on disk since last run. +src/collective/addon/browser/__init__.py wasn't modified on disk since last run. +src/collective/addon/locales/__init__.py wasn't modified on disk since last run. +src/collective/addon/tests/__init__.py wasn't modified on disk since last run. +src/collective/addon/interfaces.py already well formatted, good job. +reformatted src/collective/addon/__init__.py +reformatted src/collective/addon/setuphandlers.py +reformatted src/collective/addon/testing.py +reformatted setup.py +reformatted src/collective/addon/locales/update.py +reformatted src/collective/addon/tests/test_setup.py + +All done! ✨ 🍰 ✨ +6 files reformatted, 5 files left unchanged. + +black-enforce: successful: +black-enforce: install_deps> python -I -m pip install black -c constraints.txt +black-enforce: commands[0]> black -v src setup.py + black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds) + congratulations :) (2.61 seconds) + + +git init is disabled! +Generated file structure at /Users//Development/collective.addon/collective.addon +``` + +Plonecli creates a folder with the name of the add-on, in this example, `collective.addon`. + +You can now continue to add subtemplates to your addon {ref}`create-a-backend-add-on-add-subtemplate-label` + + +(create-a-backend-add-on-add-subtemplate-label)= + +## Add `plonecli` subtemplate to an addon + +The generated addon contains a {file}`bobtemplates.cfg` file which lets you add several subtemplates with `plonecli`. + +Run the following command to list the available subtemplates. + +```shell +uvx plonecli -l ``` + +```shell +> uvx plonecli -l +Available mr.bob templates: + - addon + - behavior + - content_type + - controlpanel + - form + - indexer + - mockup_pattern + - portlet + - restapi_service + - site_initialization + - subscriber + - svelte_app + - theme + - theme_barceloneta + - theme_basic + - upgrade_step + - view + - viewlet + - vocabulary + - buildout + ``` + + All templates below `addon` can be added to your newly created addon with: + + ```shell + uvx ploncli add + ``` + +Currently documented subtemplates: + +- behavior: {ref}`backend-behaviors-label` +- content_type: {ref}`creating-content-types-label` +- controlpanel: {ref}`control-panels-label` +- form: {reg}`forms-label` +- mockup_pattern: {ref}`mockup-and-patternslib-label` +- theme_barceloneta: {ref}`create-a-theme-add-on-label` + + +The addon also contains a {term}`GenericSetup` default install/uninstall profile. + +See {ref}`genericsetup-label` for more information. From 0cad2b031aa2250f4cd2a3bca2ab29cbd6c00894 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 25 Nov 2025 15:46:49 +0100 Subject: [PATCH 04/42] create genericsetup documentation --- docs/backend/generic-setup.md | 1763 +++++++++++++++++++++++++++++++++ docs/backend/index.md | 1 + 2 files changed, 1764 insertions(+) create mode 100644 docs/backend/generic-setup.md diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md new file mode 100644 index 0000000000..8d0b34d78c --- /dev/null +++ b/docs/backend/generic-setup.md @@ -0,0 +1,1763 @@ +--- +myst: + html_meta: + "description": "How does GenericSetup work" + "property=og:description": "How does GenericSetup work" + "property=og:title": "GenericSetup" + "keywords": "Plone 6, backend, GenericSetup" +--- + +(genericsetup-label)= + +# GenericSetup + +GenericSetup is a framework to modify the Plone site during add-on package installation and uninstallation. + +It provides XML-based rules to change the site settings. + +```{todo} +remove archetypes example code everywhere +``` + + +## Introduction + +GenericSetup is an XML-based way to import and export Plone site configurations. + +It is mainly used to prepare the Plone site for add-on packages, by: + +- registering Registry entries (resources, configuration, etc) +- setting various properties, +- registering portlets, +- registering portal_catalog search query indexes, +- providing upgrade steps for addon version upgrades +- ... etc ... + +GenericSetup is mostly used to apply an add-on's specific changes to the site configuration and to enable specific behaviors when the add-on installer is run. + +GenericSetup XML files are usually in a `profiles/default` folder inside the add-on package. + +All run-time through-the-web ({term}`TTW`) configurable items (for example viewlets order through `/@@manage-viewlets` page) are made repeatable using GenericSetup profile files. + +You can always change the configuration options through Plone or using the Management Interface, and then you export the resulting profile as an XML file, using the *Export* tab in `portal_setup` accessible from the Management Interface. + +Directly editing XML profile files does not change anything on the site, even after Zope restart. +This is because run-time TTW configurable items are stored in the database. + +If you edit profile files, you need to either reimport the edited files using the `portal_setup` tool or fully rerun the add-on package installer in Plone control panel. + +This import will read XML files and change the Plone database accordingly. + +```{note} +Difference between ZCML and GenericSetup + +ZCML changes affect loaded Python code in **all** sites inside Zope whereas GenericSetup XML files affect only one Plone site and its database. +GenericSetup XML files are always database changes. + +Relationship between ZCML and site-specific behavior is usually done using {doc}`layers `. +ZCML directives, like viewlets and views, are registered to be active only on a certain layer using `layer` attribute. +When GenericSetup XML is imported through `portal_setup`, or the add-on package installer is run for a Plone site, the layer is activated for the particular site only, enabling all views registered for this layer. +``` + +```{note} +The `metadata.xml` file (add-on dependency and version information) is read during Plone start-up. +If this file has problems, your add-on might not appear in the installer control panel. +``` + + +- [GenericSetup package page](https://pypi.python.org/pypi/Products.GenericSetup). +- [GenericSetup source code](https://github.com/zopefoundation/Products.GenericSetup). + +## Creating A Profile + +You use `` directive in your add-on package's `configure.zcml`. +The name for the default profile executed by the Plone add-on installer is `default`. + +If you need different profiles, for example for unit testing, you can declare them here. + +XML files for the `default` profile go in the `profiles/default` folder inside your add-on package. + +```xml + + + + + +``` + +### Multiple Profiles + +When you have more than one profile in your add-on package, the add-ons control panel needs to decide which one to use when you install it. + +Since Plone 5.1, when there is a `default` profile, it is always used as the installation profile, regardless of other profile names. + +Exception: when this `default` profile is marked in an `INonInstallable` utility, it is ignored and Plone falls back to using the first from the alphabetical sorting. + +```{note} +In Plone 5.0 and lower, +the profiles are sorted alphabetically by id, +and the first one is chosen. +If you have profiles `base` and `default`, +the `base` profile is installed. +It is recommended to let `default` be the alphabetically first profile. +``` + +## Add-on-specific Issues + +Add-on packages may contain: + +- A default GenericSetup XML profile which is automatically run when the package is installed using the quick-installer. + The profile name is usually `default`. +- Other profiles which the user may install using the `portal_setup` *Import* tab, or which can be manually enabled for unit tests. +- An "Import various" step, which runs Python code every time the GenericSetup XML profile is installed. + See {ref}`custominstall`. +- A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or higher. + See note at {ref}`custominstall`. + +For more information about custom import steps, see: + +- + +## Listing Available Profiles + +Example: + +``` +# List all profiles know to the Plone instance. +setup_tool = self.portal.portal_setup + +profiles = setup_tool.listProfileInfo() +for profile in profiles: + print str(profile) +``` + +Sample results: + +```python +{'product': 'PluggableAuthService', + 'description': 'Content for an empty PAS (plugins registry only).', + 'for': , + 'title': 'Empty PAS Content Profile', + 'version': 'PluggableAuthService-1.5.3', + 'path': 'profiles/empty', + 'type': 1, + 'id': 'PluggableAuthService:empty'} +{'product': 'Products.CMFPlone', + 'description': u'Profile for a default Plone.', + 'for': , + 'title': u'Plone Site', + 'version': u'3.1.7', + 'path': u'/home/moo/sits/parts/plone/CMFPlone/profiles/default', + 'type': 1, + 'id': u'Products.CMFPlone:plone'} +... +``` + +## Installing A Profile + +This is usually unit test specific question how to enable certain add-ons for unit testing. + +### plone.app.testing + +See [Product and profile installation](http://docs.plone.org/external/plone.app.testing/docs/source/README.html#product-and-profile-installation). + +### Manually + +You might want to install profiles manually if they need to be enabled only for certain tests. + +The profile name is in the format `profile-${package_name}:${profile id}` + +Unit testing example: + +``` +# Run the extended profile of the "your.addonpackage" package. +setup_tool.runAllImportStepsFromProfile('profile-your.addonpackage:extended') +``` + +```{note} +Since Products.GenericSetup 1.8.0 the `profile-` part is optional. +The code can handle both. +``` + +## Missing Upgrade Procedure + +In the Add-ons control panel you may see a warning that your add-on package is [missing an upgrade procedure](http://stackoverflow.com/questions/15316583/how-to-define-a-procedure-to-upgrade-an-add-on). + +This means you need to write some {ref}`genericsetup-upgrade-steps-label`. + + +## Uninstall Profile + +When you deactivate an add-on in the control panel, Plone looks for a profile with the name `uninstall` and applies it. + +```{note} +If there is no `uninstall` profile, a warning is displayed before installing the add-on. +If you do activate the add-on, no deactivate button will be shown. +``` + + +## Dependencies + +GenericSetup profile can contain dependencies to other add-on package installers and profiles. + +For example, if you want to declare a dependency to the *your.addonpackage* package, that it is automatically installed when your add-on is installed, +you can use the declaration below. + +This way you can be sure that all layers, portlets and other features which require database changes are usable from *your.addonpackage* when it is run. + +`metadata.xml`: + +```xml + + + 1000 + + profile-your.addonpackage:default + + +``` + +*your.addonpackage* declares the profile in its configure.zcml: + +```xml + +``` + +```{warning} +Unlike other GenericSetup XML files, `metadata.xml` is read on the start-up and this read is cached. +Always restart Plone after editing `metadata.xml`. + +If your `metadata.xml` file contains syntax errors or dependencies to a missing or non-existent package (e.g. due to a typo in a name) your add-on will disappear from the installation control panel. +``` + +```{note} +For some old add-ons in the `Products.*` Python namespace, you must not include the full package name in the dependencies. + +This is true when this add-on has registered its profile in Python instead of zcml, and there it has used only part of its package name. + +In most cases you *do* need to use the full `Products.xxx` name. +``` + +To declare a dependency on the `simple` profile of `Products.PluggableAuthService`: + +```xml + + + 1000 + + + profile-PluggableAuthService:simple + + +``` + +## Metadata version numbers + +Some old add-on packages may have a `metadata.xml` without version number, but this is considered bad practice. + +What should the version number in your `metadata.xml` be? + +This mostly matters when you are adding upgrade steps, see also the [Upgrade steps] section. + +Upgrade steps have a sort order in which they are executed. This used to be alphabetical sorting. + +When you had eleven upgrade steps, marked from 1 through 11, alphabetical sorting meant this order: 1, 10, 11, 2, 3, etc. + +If you are seeing this, then you are using an old version of GenericSetup. + +You want numerical sorting here, which is correctly done currently. Versions with dots work fine too. + +They get ordered just like they would when used for packages on PyPI. + +Best practice for all versions of GenericSetup is this: + +- Start with 1000. + This avoids problems with ancient GenericSetup that used alphabetical sorting. +- Simply increase the version by 1 each time you need a new metadata version. + For example: 1001, 1002, etc. +- If your add-on package version number changes, but your profile stays the same and no upgrade step is needed, you should **not** change the metadata version. + There is no need. +- If you make changes for a new major release, you should increase the metadata version significantly. + This leaves room for small metadata version increases on a maintenance branch. + Example: + You have branch master with version 1025. + You make backwards incompatible changes and you increase the version to 2000. + You create a maintenance branch where the next metadata version will be 1026. + +(genericsetup-custom-installer-code-label)= + +## Custom Installer Code (`setuphandlers.py`) + +Besides out-of-the-box XML steps which provide both install and uninstall, +GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled. + +This is not a very straightforward process, though. + +````{note} +An easier way may be possible for you. +GenericSetup 1.8.2 has an option to point to a function to run before or after applying all import steps for your profile. + +If you do not need to support older versions, this is the easiest way. + +In `configure.zcml`: + +``` + + + + + +``` + +In `setuphandlers.py`: + +```python +def run_before(context): + # This is run before running the first import step of + # the default profile. context is portal_setup. + # If you need the same context as you would get in + # an import step, like setup_various below, do this: + profile_id = 'profile-your.addonpackage:default' + good_old_context = context._getImportContext(profile_id) + ... + +def run_after(context): + # This is run after running the last import step of + # the default profile. context is portal_setup. + ... +``` + +The best practice is to create a `setuphandlers.py` file which contains a function `setup_various()` which runs the required Python code +to make changes to Plone site object. + +This function is registered as a custom `genericsetup:importStep` in XML. + +```{note} +When you write a custom `importStep`, remember to write uninstallation code as well. +``` + +However, the trick is that all GenericSetup import steps, including your custom step, are run for *every* add-on package when they are installed. + +If your need to run code which is **specific to your add-on install only** you need to use a marker text file which is checked by the GenericSetup context. + +Also you need to register this custom import step in `configure.zcml`: + +```xml + + + + + +``` + +You can run other steps before yours by using the `depends` directive. + +For instance, if your import step depends on a content type to be installed first, you must use: + +```xml + + + + + + + +``` + +`setuphandlers.py` example + +```python + +def run_custom_code(site): + """Run custom add-on package installation code to modify Plone + site object and others + + @param site: Plone site + """ + +def setup_various(context): + """ + @param context: Products.GenericSetup.context.DirectoryImportContext instance + """ + + # We check from our GenericSetup context whether we are running + # add-on installation for your package or any other + if context.readDataFile('your.addonpackage.marker.txt') is None: + # Not your add-on + return + + portal = context.getSite() + + run_custom_code(portal) +``` + +And add a dummy text file +`your.addonpackage/your/addonpackage/profiles/default/your.addonpackage.marker.txt`: + +This text file can contain any content - it just needs to be present + + + +## Overriding Import Step Order + +If you need to override the order of import steps in a package that is not yours, +it might work if you [use an overrides.zcml](http://plone.293351.n2.nabble.com/Overriding-import-step-order-td2189638.html). + +### Controlling The Import Step Execution Order + +If you need to control the execution order of one of your own custom import steps, you can do this in your import step definition in zcml. + +To make sure the catalog and typeinfo steps are run before your own step, use this code: + +```xml + + + + + + + + +``` + +```{note} +The name that you need, is usually the name of the related xml file, but with the `.xml` stripped. +For the `catalog.xml` the import step name is `catalog`. +But there are exceptions. + +For the `types.xml` and the `types` directory, the import step name is `typeinfo`. + +See {ref}`genericsetup-generic-setup-files-label` for a list. +``` + + +(genericsetup-upgrade-steps-label)- + +## Upgrade Steps + +You can define upgrade steps to run code when someone upgrades your package from version *x* to *y*. + +As an example, let's say that the new version of your.addonpackage defines a *price* field on a content type *MyType* to be a string, +but previously (version 1.1 and earlier) it was a float. + +Code that uses this field and assumes it to be a float will break after the upgrade, you'd like to automatically convert existing values for the field to string. + +You could do this in a script, but having a GenericSetup upgrade step means non-technical people can do it as well. + +Once you have the script, it's code can be to put in an upgrade step. + +### Increment Profile Version + +First increase the number of the version in the `profiles/default/metadata.xml`. +This version number should be an integer. + +Package version are different because they add sense like the status of the add-on: is it stable, is it in development, in beta, which branch is it. + +A profile version indicates only that you have to migrate data in the database. + +### Add Upgrade Step + +Next we add an upgrade step: + +```xml + + + + + +``` + +- You can use a wildcard character for *source* to indicate an upgrade for any previous version. + Since Products.GenericSetup 1.7.6 this works fine. + To run the upgrade step only when upgrading from a specific version, use that version's number. +- The optional `sortkey` can be used to indicate the order in which upgrade steps from the same source to destination are run. + +### Add Upgrade Code + +The code for the upgrade method itself is best placed in a *upgrades.py* module: + +```python +from plone import api +import logging + +PROFILE_ID = 'profile-your.addonpackage:default' + + +def convert_price_to_string(context, logger=None): + """Method to convert float Price fields to string. + + When called from the import_various method, 'context' is + the plone site and 'logger' is the portal_setup logger. + + But this method will be used as upgrade step, in which case 'context' + will be portal_setup and 'logger' will be None.""" + + if logger is None: + # Called as upgrade step: define our own logger. + logger = logging.getLogger('your.addonpackage') + + # Run the catalog.xml step as that may have defined new metadata + # columns. We could instead add to + # the registration of our import step in zcml, but doing it in + # code makes this method usable as upgrade step as well. + # Remove these lines when you have no catalog.xml file. + setup = api.portal.get_tool('portal_setup') + setup.runImportStepFromProfile(PROFILE_ID, 'catalog') + + catalog = api.portal.get_tool('portal_catalog') + brains = catalog(portal_type='MyType') + count = 0 + for brain in brains: + current_price = brain.getPrice + if type(current_price) != type('a string'): + obj = brain.getObject() + obj.setPrice(str(current_price)) + obj.reindexObject() + count += 1 + + setup.runImportStepFromProfile(PROFILE_ID, 'catalog') + logger.info('%s fields converted.' % count) +``` + +Other examples of using generic setup to run import steps are below. + +If you want to call `types.xml` use `typeinfo`: + +```python +setup.runImportStepFromProfile(PROFILE_ID, 'typeinfo') +``` + +If you want to call `workflow.xml` use `workflow`: + +```python +setup.runImportStepFromProfile(PROFILE_ID, 'workflow') +``` + +The ids of the various default import steps are defined in several places. + +Some of the most used ones are here: + +- +- + +After restarting Zope, your upgrade step should be visible in the Management Interface: +the `portal_setup` tool has a tab `Upgrades`. + +Select your package profile to see which upgrade steps Zope knows about for your add-on. + +### upgradeDepends + +In an upgrade step you can apply a specific import step from your profile: + +```xml + +``` + +You can apply multiple steps, separated by a space: + +```xml + +``` + +You can apply steps from a different profile: + +```xml + +``` + +You can apply a complete profile: + +```xml + +``` + +### Combining Upgrade Steps + +You can create many upgrade steps under one migration. + +This is useful when you want to have the ability to re-run some parts of the migration and make your code more re-useable (for example cook css resource of your theme). + +Here is an example of many upgrade steps you can have to achieve on a site policy: + +```xml + + + + + + + + + + + + + +``` + + +## Best Practices + +### The `purge` attribute + +When importing items such as property sheets, make sure not to override other profile settings: set the `purge` attribute to False. + +This will *add* the listed items to the property instead of resetting the property. + +Example: + +```xml + + + + +``` + +### The `remove` Attribute + +The `remove` attribute can be used to remove an item. + +```xml + +``` + +There are dangers: + +- Some importers do not support the `remove` keyword. + They ignore it and add the item blindly. + This should be regarded as a bug in the importer. + Please report it. +- Some importers check the truth value of the attribute, some just check the presence. + `remove="false"` may mean the item stays and may mean it gets removed. + Best is to either use `remove="true"` or leave the entire keyword away. + +### Only Use The Configuration That You Need + +When you export your site's configuration, it will include things that you don't need. + +For example, if you only need to change the 'Allow anonymous to view about' property, this is what your `propertiestool.xml` should look like: + +```xml + + + + True + + + Duck Test + Action: test a duck + ... + +``` + +```{note} +In the portal_actions tool, in the Management Interface, you will see an i18n domain specified for each action. +``` + +- `catalog.xml`: no i18n needed +- `componentregistry.xml`: no i18n needed +- `contenttyperegistry.xml`: no i18n needed +- `controlpanel.xml`: use **your own** domain. + +Example: + +``` + + + + Manage portal + + +``` + +- `diff_tool.xml`: no i18n needed +- `factorytool.xml`: no i18n needed +- `metadata.xml`: no i18n needed +- `portlets.xml`: use the **plone** domain. +- `properties.xml`: no i18n needed +- `propertiestool.xml`: no i18n needed +- `rolemap.xml`: no i18n needed +- `skins.xml`: no i18n needed +- `toolset.xml`: no i18n needed +- `types`: use **your own** domain +- `viewlets.xml`: no i18n needed +- `workflows`: use the **plone** domain + + +(genericsetup-generic-setup-files-label)- + +## Generic Setup Files + + +### actions.xml + +Install actions in the `portal_actions` tool. + +Example: + +```xml + + + + + Check in + + string:${object_url}/@@content-checkin + string:${portal_url}/++resource++checkout.png + python:path('object/@@iterate_control').checkin_allowed() + + + + True + + + +``` + +These actions are used in various parts of Plone. + +These are the object categories in standard Plone: + +`document_actions` + +: Document actions, like rss and print. + +`site_actions` + +: Site actions, like sitemap, accessibility, contact. + +`object` + +: Object tabs, like contents, sharing tab. + +`object_buttons` + +: Object buttons, like delete, rename. + +`portal_tabs` + +: Portal tabs, like Home. + +`user` + +: User actions, like preferences, login, join. + +For adding controlpanel actions, see [controlpanel.xml] instead. + +The objects support `insert-before` and `insert-after` for inserting the action object before or after another action object. + +For removing, use `remove="true"` (or `True`). + +Uninstall example: + +```xml + + + + + + +``` + +```{note} +You can use your own i18n domain. +``` + +### browserlayer.xml + +This registers a specific browser layer, which allows components to be available only when your add-on package is installed. + +```xml + + + + +``` + +For removing, use `remove="true"` (or `True`). + +Uninstall example: + +```xml + + + + +``` + +### componentregistry.xml + + +Setup items in the local component registry of the Plone Site. +The items can be adapters, subscribers or utilities. + +This can also be done in zcml, which puts it in the global registry that is defined at startup. + +The difference is, when you put it in xml, the item is only added to a specific Plone Site when you install the package in the add-ons control panel. + +Both have their uses. + +Example: + +```xml + + + + + + + + + +``` + +```{note} +A subscriber can either have a handler or a factory, not both. +A factory must have a provides and may have a name. +A subscriber will fail with a provides. +``` + +```{note} +If something does not get added, its provider is probably blacklisted. +This list is defined by `Products.GenericSetup.interfaces.IComponentsHandlerBlacklist` utilities. +In standard Plone 5, these interfaces are blacklisted as providers: + +- `Products.GenericSetup.interfaces.IComponentsHandlerBlacklist` +- `plone.portlets.interfaces.IPortletManager` +- `plone.portlets.interfaces.IPortletManagerRenderer` +- `plone.portlets.interfaces.IPortletType` +``` + +Uninstall example: + +```xml + + + + + + + + + + + + +``` + +```{note} +The presence of the `remove` keyword is enough. +Even if it is empty or contains `false` as value, the item is removed. +``` + + +### contentrules.xml + +TODO + + +### controlpanel.xml + +```xml + + + + + Manage portal + + + +``` + +This creates an action in the Site Setup control panel in Plone. +Actions are bundled in categories. + +- `Plone` (Plone Configuration) +- `plone-advanced` (Advanced) +- `plone-content` (Content) +- `plone-general` (General) +- `plone-security` (Security) +- `plone-users` (Users) +- `Products` (Add-on Configuration) + +Any other categories are not displayed in the overview control panel. + +For add-ons, the category `Products` is recommended. + +The `action_id` must be unique over all categories. + +Only one permission is allowed. + +Uninstall example: + +```xml + + + + +``` + +```{note} +The action is removed if the `remove` keyword is `true`. +Upper or lower case does not matter. + +The action is visible if the `visible` keyword is `true`. +Upper or lower case does not matter. +``` + +```{note} +You can use your own i18n domain. +``` + +Code is in `Products.CMFPlone.exportimport.controlpanel` and `Products.CMFPlone.PloneControlPanel`. + + +### diff_tool.xml + +This is the configuration from `plone.app.contenttypes`: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +This configures how the difference between two versions of a field are shown on the history tab. + +The configuration is stored in the `portal_diff` tool. + +For Archetypes content, you need a different `difftype`: + +```xml + + + +``` + +A new `difftype` can be registered by calling `Products.CMFDiffTool.CMFDiffTool.registerDiffType`. +The `difftypes` in standard Plone 5 are: + +- `Lines Diff` +- `Compound Diff for AT types` +- `Binary Diff` +- `Field Diff` +- `List Diff` +- `HTML Diff` +- `Compound Diff for Dexterity types` + +```{note} +There is no uninstall version. +The `remove` keyword is not supported. +The `portal_diff` tool does not show configuration for portal_types that no longer exist. +``` + +Code is in `Products.CMFDiffTool.exportimport.difftool`. + + +### metadata.xml + +The `metadata.xml` file is read during Plone start-up. + +If this file has problems your add-on package might not appear in the installer control panel. + +The `metadata.xml` file contains add-on dependency and version information. + +```xml + + + 1000 + + profile-your.addonpackage:default + + +``` + +The dependencies are optional. + +There is no import step that reads this file. +The `portal_setup` tool uses this information when installing a profile. + +It installs the profiles that are listed as dependencies, before installing your own profile. + +Since `Products.GenericSetup` 1.8.0, dependency profiles that are already installed, are not installed again. + +Instead, their upgrade steps, are applied, if they have them. + +After your profile is installed, `portal_setup` stores the version number. +This is used when determining if any upgrade steps are available for your profile. + +When you search for `metadata.xml` in the documentation, you will find more information in context. + +```{note} +There is no uninstall version of `metadata.xml`. +An `uninstall` profile can have its own `metadata.xml` with a version and even profiles. +But for dependencies no `purge` or `remove` keyword is supported. +``` + + +### portal_placeful_workflow + +This handles the `portal_placeful_workflow.xml` file and the `portal_placeful_workflow` directory. + +This install or configures a placeful workflow. + +For this to work, you must install Workflow Policy Support (CMFPlacefulWorkflow) in the Add-ons control panel. This package is included in standard Plone, but does not come installed by default. + +Standard `portal_placeful_workflow.xml` from `Products.CMFPlacefulWorkflow`: + +```xml + + + + 1 + + + + + +``` + +Standard `portal_placeful_workflow/simple-publication.xml` from `Products.CMFPlacefulWorkflow`: + +```xml + + + Simple publication + + + + + + + + + + + + + + + + +``` + +Uninstall example: + +```xml + + + + +``` + +The import handler is in `Products.CMFPlacefulWorkflow.exportimport.importWorkflowPolicies`. + +### portlets.xml + +Code is in `plone.app.portlets.exportimport.portlets`. + +```{eval-rst} +.. automodule:: plone.app.portlets.exportimport.portlets + +``` + +### propertiestool.xml + +```{deprecated} 5.0 +Most properties are now handled in the configuration registry and can be configured in `registry.xml`. +``` + +In `propertiestool.xml` you can change all values of the `portal_properties` tool. +Example: + +```xml + + + + False + TinyMCE + + +``` + +Uninstall example: + +```xml + + + + + + + + +``` + +### pluginregistry.xml + +This configures PAS plugin orderings and active plugins. +It isn't part of Plone itself, it is used by other frameworks and can be used in Plone with a little extra configuration. + +First, you need a monkey patch in your `` __init__.py` `` to point the importer at where Plone keeps its PAS plugins. + +```python +from Products.PluginRegistry import exportimport +from Products.PluginRegistry.interfaces import IPluginRegistry + + +def getRegistry(site): + return IPluginRegistry(site.acl_users.plugins) + +exportimport._getRegistry = getRegistry +``` + +Secondly, code to handle the import step needs to be activated in Plone: + +```xml + +``` + +Now you can use `pluginregistry.xml` in your generic setup profiles: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### registry.xml + +This edits the configuration registry. + +```{note} +The name of this import step is `plone.app.registry`, **not** `registry`. +``` + +Example for adding all records of an interface: + +```xml + + + + +``` + +Example for adding an individual record: + +```xml + + + + + Timeout + 0 + + 100 + + +``` + +Uninstall example: + +```xml + + + + + +``` + +The item is removed if the `remove` keyword is `true`. +Upper or lower case does not matter. + +Existing values of lists are purged by default. +The values are not purged if the `purge` keyword is `false`. + +Upper or lower case does not matter. + +For more examples, see the [plone.app.registry documentation](https://pypi.python.org/pypi/plone.app.registry#using-genericsetup-to-manipulate-the-registry). + +Code is in `plone.app.registry.exportimport.handler`. + +### repositorytool.xml + +This handles the versioning policy of content. + +The default configuration in Plone is: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +Code is in `Products.CMFEditions.exportimport.repository`. + +### rolemap.xml + +In `rolemap.xml` you define new roles and grant permissions. +Both are optional. + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +The roles above are the standard roles in Plone 5. +In your profile you only need to list other roles. + +The permission must already exist on the Zope level, otherwise you get an error when installing your profile: + +``` +ValueError: The permission Pass the bridge is invalid. +``` + +A permission is created on the Zope level when it is used in code. +See {doc}`Creating permissions `. + +When a role in a permission does not exist, it is silently ignored. +The roles listed in a permission are not added. + +They replace all existing roles. + +With `acquire="true"` (or `True`, `yes`, `1`) roles are also acquired from the Zope root. + +````{note} +There is no uninstall version for `rolemap.xml`. +`purge` and `remove` are not supported. +You can set different values for a permission if this makes sense in your case. +This will reset the permission to the same settings as on the Zope level: + +```xml + +``` +```` + +```{eval-rst} +.. automodule:: Products.GenericSetup.rolemap + :members: importRolemap RolemapImportConfigurator + +``` + +### sharing.xml + +The sharing.xml file let you add custom roles to the sharing tab. +For reference, visit: {ref}`security-label`. + + +### typeinfo + +This handles the `types.xml` file and the `types` directory. + +```{note} +The name of this import step is `typeinfo`, **not** `types`. +``` + +Partial example from `plone.app.contenttypes`: + +```xml + + + + + + + + + + + + +``` + +This adds content types in the `portal_types` tool. +The `meta_type` can be: + +- `Dexterity FTI` for Dexterity content. + This is probably what you want. +- `Factory-based Type Information with dynamic views` for Archetypes content and for the Plone Site itself +- `Factory-based Type Information` for Archetypes content that does not need dynamic views, + the ability to choose a view in the `display` menu. + +The `types.xml` should be accompanied by a `types` folder with details information on the new types. +If you are editing an already existing type, then `types.xml` is not needed: +a file in the `types` folder is enough. + +If the object name in `types.xml` is `Collection` then you must add a file `types/Collection.xml`. +This file is in `plone.app.contenttypes`: + +```xml + + + + + Collection + Collection + True + False + + False + plone.app.contenttypes.addCollection + plone.app.contenttypes.content.Collection + + + plone.app.contenttypes.schema:collection.xml + + + + + + + + + + + + + + listing_view + False + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +For comparison, here is the `types.xml` from `plone.app.collection` which has an old style Archetypes Collection: + +```xml + + + + + + +``` + +And here is the `types/Collection.xml` from `plone.app.collection`: + +```xml + + + Collection + Collection of searchable information + + Collection + plone.app.collection + addCollection + standard_view + True + True + + False + standard_view + + + + + + + + + + + + + + + + + + +``` + +Uninstall example: + +```xml + + + + +``` + +````{note} +The `remove` keyword is supported for actions. +`remove=""` is enough, but recommended is to use `remove="true"`. + +The `view_methods` property is a list that is always imported fresh. +Elements that are not in the list, are removed. +If you only want to add an element and want to keep any existing elements, +you can tell it not to purge: + +```xml + + + +``` + +This does not work for the `allowed_content_types`: they are always purged. +```` + +```{note} +You can use your own i18n domain. +``` + +### viewlets.xml + +```{eval-rst} +.. automodule:: plone.app.viewletmanager.exportimport.storage + +``` + +### workflows.xml + +This handles the `workflows.xml` file and the `workflows` directory. + +Example from `Products/CMFPlone/profiles/default/workflows.xml` in Plone 5.0: + +```xml + + + Contains workflow definitions for your portal + + + + + + + + + + + + + + + + +``` + +This adds six workflows in the `portal_workflow` tool. +It sets the default workflow to `simple_publication_workflow`. + +It sets several types to not use any workflow. + +Next to this, the `workflows` directory is checked. +This contains sub directories with the same name as the workflows. +Each sub directory contains a file `definition.xml` with the definition for this workflow. + +See {ref}`workflows-label`. diff --git a/docs/backend/index.md b/docs/backend/index.md index 3e7dd32784..74d38c60f8 100644 --- a/docs/backend/index.md +++ b/docs/backend/index.md @@ -23,6 +23,7 @@ configuration-registry content-types/index control-panels fields +generic-setup global-utils indexing upgrading/index From 042ff329e84729794f2e9d2aeac3e764ce4a5255 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 25 Nov 2025 16:13:23 +0100 Subject: [PATCH 05/42] remove note --- docs/backend/generic-setup.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 8d0b34d78c..82b929ce5b 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -306,14 +306,6 @@ Best practice for all versions of GenericSetup is this: Besides out-of-the-box XML steps which provide both install and uninstall, GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled. -This is not a very straightforward process, though. - -````{note} -An easier way may be possible for you. -GenericSetup 1.8.2 has an option to point to a function to run before or after applying all import steps for your profile. - -If you do not need to support older versions, this is the easiest way. - In `configure.zcml`: ``` From aad7b719fa724ff10f7998e1595a7d2b9f99da04 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 25 Nov 2025 22:09:01 -0800 Subject: [PATCH 06/42] Use file markup --- docs/i18n-l10n/translating-text-strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/i18n-l10n/translating-text-strings.md b/docs/i18n-l10n/translating-text-strings.md index d69c4ec15d..dae336c297 100644 --- a/docs/i18n-l10n/translating-text-strings.md +++ b/docs/i18n-l10n/translating-text-strings.md @@ -56,7 +56,7 @@ Information in the PO file headers is ignored. [`i18ndude`](https://pypi.org/project/i18ndude/) should be used to create a script which searches particular packages for translation strings. -If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your {term}`buildout` to extract the messages from your code. +If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script {file}`update.sh` inside your package and a script {file}`update_locale` in your {term}`buildout` to extract the messages from your code. After running that script, a new `domain.pot` file will be created in your `locales` directory where all the messages will be saved. From df7340f418ce6fe02b0725bf775c120a1407bc5d Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 27 Nov 2025 13:23:02 -0800 Subject: [PATCH 07/42] Revert changes in i18n and glossary to keep PR focused on GenericSetup --- docs/glossary.md | 4 ---- docs/i18n-l10n/translating-text-strings.md | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/glossary.md b/docs/glossary.md index 65391effaa..a6c7623508 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -25,10 +25,6 @@ Buildout [Buildout](https://github.com/buildout/buildout/) is a Python-based tool for building and assembling applications from multiple parts, based on a configuration file. It was the most common way of installing Plone 3, 4, and 5, and can still be used with Plone 6. - Usage of Buildout in Plone appears in various places in this documentation. - For a history and extended usage of Buildout, you can refer to the Plone 4 Documentation's section on [Buildout](https://4.docs.plone.org/old-reference-manuals/buildout/). - The Plone community authored this reference manual, as Buildout's own documentation is suboptimal. - CMS Content Management System diff --git a/docs/i18n-l10n/translating-text-strings.md b/docs/i18n-l10n/translating-text-strings.md index dae336c297..85c69cdda9 100644 --- a/docs/i18n-l10n/translating-text-strings.md +++ b/docs/i18n-l10n/translating-text-strings.md @@ -56,7 +56,7 @@ Information in the PO file headers is ignored. [`i18ndude`](https://pypi.org/project/i18ndude/) should be used to create a script which searches particular packages for translation strings. -If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script {file}`update.sh` inside your package and a script {file}`update_locale` in your {term}`buildout` to extract the messages from your code. +If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your {term}`buildout` to extract the messages from your code. After running that script, a new `domain.pot` file will be created in your `locales` directory where all the messages will be saved. @@ -273,7 +273,7 @@ This script hooks into the release process and builds the MO files for you. ### Installing i18ndude -The recommended method is to have {term}`i18ndude` installed via your buildout. +The recommended method is to have {term}`i18ndude` installed via your [buildout](https://www.buildout.org/en/latest/). Add the following to your `buildout.cfg`: From 6af80fc79094889180ba9a61c93163625021f737 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 27 Nov 2025 13:24:31 -0800 Subject: [PATCH 08/42] Revert changes in i18n and glossary to keep PR focused on GenericSetup --- docs/i18n-l10n/translating-text-strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/i18n-l10n/translating-text-strings.md b/docs/i18n-l10n/translating-text-strings.md index 85c69cdda9..22d307e883 100644 --- a/docs/i18n-l10n/translating-text-strings.md +++ b/docs/i18n-l10n/translating-text-strings.md @@ -56,7 +56,7 @@ Information in the PO file headers is ignored. [`i18ndude`](https://pypi.org/project/i18ndude/) should be used to create a script which searches particular packages for translation strings. -If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your {term}`buildout` to extract the messages from your code. +If you have created your add-on using [bobtemplates.plone](https://pypi.org/project/bobtemplates.plone/), then you will already have a script `update.sh` inside your package and a script `update_locale` in your buildout to extract the messages from your code. After running that script, a new `domain.pot` file will be created in your `locales` directory where all the messages will be saved. From 2675e11a0208718373cfe26abf51af05e657435c Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 1 Dec 2025 15:14:01 +0100 Subject: [PATCH 09/42] Update docs/developer-guide/create-a-backend-add-on.md Co-authored-by: Mikel Larreategi --- docs/developer-guide/create-a-backend-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index 500c777c25..81f823021a 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -213,7 +213,7 @@ Available mr.bob templates: All templates below `addon` can be added to your newly created addon with: ```shell - uvx ploncli add + uvx plonecli add ``` Currently documented subtemplates: From 6e92377e352849a187edf74aef21c73562a2eba7 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 1 Dec 2025 15:32:09 +0100 Subject: [PATCH 10/42] remove plonecli addon creation and note for monorepo "make" command --- .../create-a-backend-add-on.md | 78 +------------------ 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index 81f823021a..ecba0a728a 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -99,80 +99,6 @@ Cookieplone creates a folder with the name of the add-on, in this example, `coll You can now continue to add subtemplates to your addon {ref}`create-a-backend-add-on-add-subtemplate-label` -## Generate the add-on project with `plonecli` - -Run the following command to create an addon project with `plonecli` - -```shell -uvx plonecli create addon -``` - -```console -> uvx plonecli create addon collective.addon -RUN: bobtemplates.plone:addon -O collective.addon - -Welcome to mr.bob interactive mode. Before we generate directory structure, -some questions need to be answered. - -Answer with a question mark to display help. -Values in square brackets at the end of the questions show the default value if -there is no answer. - - ---> Package description [An add-on for Plone]: - ---> Plone version [6.0.0]: - ---> Python version for virtualenv [python3]: - ---> Do you want me to activate VS Code support? (y/n) [y]: - - - -isort-apply: successful: -isort-apply: install_deps> python -I -m pip install isort -c constraints.txt -isort-apply: commands[0]> isort /Users//Development/collective.addon/src -/Users//Development/collective.addon/setup.py -Fixing /Users//Development/collective.addon/src/collective/addon/testing.py -Fixing /Users//Development/collective.addon/src/collective/addon/tests/test_setup.py - isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds) - congratulations :) (2.59 seconds) - - -Identified `/` as project root containing a file system root. -Sources to be formatted: "Users//Development/collective.addon/src", - "Users//Development/collective.addon/setup.py" -src/collective/__init__.py wasn't modified on disk since last run. -src/collective/addon/browser/__init__.py wasn't modified on disk since last run. -src/collective/addon/locales/__init__.py wasn't modified on disk since last run. -src/collective/addon/tests/__init__.py wasn't modified on disk since last run. -src/collective/addon/interfaces.py already well formatted, good job. -reformatted src/collective/addon/__init__.py -reformatted src/collective/addon/setuphandlers.py -reformatted src/collective/addon/testing.py -reformatted setup.py -reformatted src/collective/addon/locales/update.py -reformatted src/collective/addon/tests/test_setup.py - -All done! ✨ 🍰 ✨ -6 files reformatted, 5 files left unchanged. - -black-enforce: successful: -black-enforce: install_deps> python -I -m pip install black -c constraints.txt -black-enforce: commands[0]> black -v src setup.py - black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds) - congratulations :) (2.61 seconds) - - -git init is disabled! -Generated file structure at /Users//Development/collective.addon/collective.addon -``` - -Plonecli creates a folder with the name of the add-on, in this example, `collective.addon`. - -You can now continue to add subtemplates to your addon {ref}`create-a-backend-add-on-add-subtemplate-label` - - (create-a-backend-add-on-add-subtemplate-label)= ## Add `plonecli` subtemplate to an addon @@ -216,6 +142,10 @@ Available mr.bob templates: uvx plonecli add ``` +```{note} +When you have created a project with `cookieplone`, then you can simply do `make add ` inside the {file}`backend` folder. +``` + Currently documented subtemplates: - behavior: {ref}`backend-behaviors-label` From 0a4cace009b260c20d5daf854c59cfdcaedaf8bb Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Mon, 1 Dec 2025 16:12:40 +0100 Subject: [PATCH 11/42] only mention "make" command for "cookieplone" generated addons --- docs/developer-guide/create-a-backend-add-on.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index ecba0a728a..b9ae4c51d3 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -139,13 +139,9 @@ Available mr.bob templates: All templates below `addon` can be added to your newly created addon with: ```shell - uvx plonecli add + make add ``` -```{note} -When you have created a project with `cookieplone`, then you can simply do `make add ` inside the {file}`backend` folder. -``` - Currently documented subtemplates: - behavior: {ref}`backend-behaviors-label` From 132f1be9e0c842949c32667d3e7d81d46c4f40cb Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Tue, 9 Dec 2025 08:03:54 +0100 Subject: [PATCH 12/42] Update docs/developer-guide/create-a-backend-add-on.md Co-authored-by: Mikel Larreategi --- docs/developer-guide/create-a-backend-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index b9ae4c51d3..0a40767686 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -103,7 +103,7 @@ You can now continue to add subtemplates to your addon {ref}`create-a-backend-ad ## Add `plonecli` subtemplate to an addon -The generated addon contains a {file}`bobtemplates.cfg` file which lets you add several subtemplates with `plonecli`. +The generated addon contains a {file}`bobtemplate.cfg` file which lets you add several subtemplates with `plonecli`. Run the following command to list the available subtemplates. From bb795bcb59a426a031cca92b63db876327982190 Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Thu, 15 Jan 2026 12:20:01 +0100 Subject: [PATCH 13/42] explain how to create a backend-addon with plonecli only. --- .../create-a-backend-add-on.md | 134 ++++++++---------- 1 file changed, 63 insertions(+), 71 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index 0a40767686..ef19c7190a 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -17,86 +17,78 @@ This section explains how a developer can create an {term}`add-on` for the Plone Follow the section {ref}`create-project-cookieplone-system-requirements` to set up your system. -## Generate the add-on project with `cookieplone` +## Generate the add-on project with `plonecli` -To develop an add-on for backend and/or Classic-UI, run the following command to generate your add-on project using the `backend_addon` Cookieplone template. -See {doc}`plone:install/create-project-cookieplone` for details of the latter scenario. -The following output assumes the former scenario. +Choose your desired local development base folder and run the following command to create an addon project with `plonecli` ```shell -uvx cookieplone backend_addon +uvx plonecli create addon ``` ```console -> uvx cookieplone backend_addon -╭──────────────────────────────── cookieplone ─────────────────────────────────╮ -│ │ -│ ******* │ -│ *************** │ -│ *** *** │ -│ *** *** *** │ -│ *** ***** *** │ -│ *** *** *** │ -│ *** *** *** │ -│ *** ***** *** │ -│ *** *** *** *** │ -│ *** ***** *** │ -│ *** *** *** │ -│ *** *** │ -│ *************** │ -│ ******* │ -│ │ -╰──────────────────────────────────────────────────────────────────────────────╯ -You've downloaded /Users//.cookiecutters/cookieplone-templates before. Is -it okay to delete and re-download it? [y/n] (y): -╭──────────────────────────────── Plone Addon ─────────────────────────────────╮ -│ │ -│ Creating a new Plone Addon │ -│ │ -│ Sanity check results: │ -│ │ -│ - Cookieplone: ✓ │ -│ - uv: ✓ │ -│ - git: ✓ │ -│ │ -╰──────────────────────────────────────────────────────────────────────────────╯ - [1/10] Addon Title (Addon): - [2/10] A short description of your addon (A new addon for Plone): - [3/10] Author (Plone Community): - [4/10] Author E-mail (collective@plone.org): - [5/10] GitHub Username or Organization (collective): - [6/10] Should we use prerelease versions? (No): - [7/10] Plone Version (6.1.3): - [8/10] Python package name (collective.addon): - [9/10] Support headless Plone? - 1 - Yes - 2 - No - Choose from [1/2] (1): - [10/10] Would you like to add a documentation scaffold to your project? - 1 - Yes - 2 - No - Choose from [1/2] (1): - -> Remove files used in classic UI setup - -> Create namespace packages - -> Format code - -> Initialize Git repository - -> Generate documentation scaffold -╭────────────────────────── New addon was generated ───────────────────────────╮ -│ │ -│ Addon │ -│ │ -│ Now, enter the repository, start coding, and push to your organization. │ -│ │ -│ Sorry for the convenience, │ -│ The Plone Community. │ -│ │ -│ https://plone.org/ │ -╰──────────────────────────────────────────────────────────────────────────────╯ +> uvx plonecli create addon collective.addon +RUN: bobtemplates.plone:addon -O collective.addon + +Welcome to mr.bob interactive mode. Before we generate directory structure, +some questions need to be answered. + +Answer with a question mark to display help. +Values in square brackets at the end of the questions show the default value if +there is no answer. + + +--> Package description [An add-on for Plone]: + +--> Plone version [6.0.0]: + +--> Python version for virtualenv [python3]: + +--> Do you want me to activate VS Code support? (y/n) [y]: + + + +isort-apply: successful: +isort-apply: install_deps> python -I -m pip install isort -c constraints.txt +isort-apply: commands[0]> isort /Users//Development/collective.addon/src +/Users//Development/collective.addon/setup.py +Fixing /Users//Development/collective.addon/src/collective/addon/testing.py +Fixing /Users//Development/collective.addon/src/collective/addon/tests/test_setup.py + isort-apply: OK (2.57=setup[1.94]+cmd[0.63] seconds) + congratulations :) (2.59 seconds) + + +Identified `/` as project root containing a file system root. +Sources to be formatted: "Users//Development/collective.addon/src", + "Users//Development/collective.addon/setup.py" +src/collective/__init__.py wasn't modified on disk since last run. +src/collective/addon/browser/__init__.py wasn't modified on disk since last run. +src/collective/addon/locales/__init__.py wasn't modified on disk since last run. +src/collective/addon/tests/__init__.py wasn't modified on disk since last run. +src/collective/addon/interfaces.py already well formatted, good job. +reformatted src/collective/addon/__init__.py +reformatted src/collective/addon/setuphandlers.py +reformatted src/collective/addon/testing.py +reformatted setup.py +reformatted src/collective/addon/locales/update.py +reformatted src/collective/addon/tests/test_setup.py + +All done! ✨ 🍰 ✨ +6 files reformatted, 5 files left unchanged. + +black-enforce: successful: +black-enforce: install_deps> python -I -m pip install black -c constraints.txt +black-enforce: commands[0]> black -v src setup.py + black-enforce: OK (2.60=setup[2.12]+cmd[0.48] seconds) + congratulations :) (2.61 seconds) + + +git init is disabled! +Generated file structure at /Users//Development/collective.addon/collective.addon ``` -Cookieplone creates a folder with the name of the add-on, in this example, `collective.addon`. +Plonecli creates a folder with the name of the add-on, in this example, `collective.addon`. -You can now continue to add subtemplates to your addon {ref}`create-a-backend-add-on-add-subtemplate-label` +You can now continue to add subtemplates to your addon. (create-a-backend-add-on-add-subtemplate-label)= From 3db83f599fd6b7cb09641ae4a894fcf68d529bce Mon Sep 17 00:00:00 2001 From: Peter Mathis Date: Thu, 15 Jan 2026 12:23:20 +0100 Subject: [PATCH 14/42] minor command fix --- docs/developer-guide/create-a-backend-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index ef19c7190a..e51e1d9f02 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -131,7 +131,7 @@ Available mr.bob templates: All templates below `addon` can be added to your newly created addon with: ```shell - make add + uvx plonecli add ``` Currently documented subtemplates: From 096109df61858c5a27d9170e1bcf60140923326f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 9 Feb 2026 03:23:30 -0800 Subject: [PATCH 15/42] Begin editorial clean up --- docs/backend/generic-setup.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 82b929ce5b..c20b216645 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -19,25 +19,20 @@ It provides XML-based rules to change the site settings. remove archetypes example code everywhere ``` +GenericSetup is mainly used to prepare the Plone site for add-on packages, by: -## Introduction - -GenericSetup is an XML-based way to import and export Plone site configurations. - -It is mainly used to prepare the Plone site for add-on packages, by: - -- registering Registry entries (resources, configuration, etc) -- setting various properties, -- registering portlets, -- registering portal_catalog search query indexes, -- providing upgrade steps for addon version upgrades -- ... etc ... +- registering Registry entries, such as resources and configuration +- setting various properties +- registering portlets +- registering portal_catalog search query indexes +- providing upgrade steps for addon version upgrades +- and other preparations GenericSetup is mostly used to apply an add-on's specific changes to the site configuration and to enable specific behaviors when the add-on installer is run. -GenericSetup XML files are usually in a `profiles/default` folder inside the add-on package. +GenericSetup XML files are usually in a {file}`profiles/default` folder inside the add-on package. -All run-time through-the-web ({term}`TTW`) configurable items (for example viewlets order through `/@@manage-viewlets` page) are made repeatable using GenericSetup profile files. +All run-time through-the-web ({term}`TTW`) configurable items—for example, viewlet order through the `/@@manage-viewlets` page—are made repeatable using GenericSetup profile files. You can always change the configuration options through Plone or using the Management Interface, and then you export the resulting profile as an XML file, using the *Export* tab in `portal_setup` accessible from the Management Interface. From 98dfce6ed427bb1988d6a37a7dfdb7eeb30d4a0f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 15:44:57 -0700 Subject: [PATCH 16/42] Rewrite and restructure the GenericSetup documentation to have a proper introduction and overview section. Add Zope Management Interface as an alias to ZMI in the glossary and use it as a term. --- docs/backend/generic-setup.md | 45 +++++++++++---------- docs/glossary.md | 1 + styles/config/vocabularies/Plone/accept.txt | 1 + 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index c20b216645..cdbbfc10ea 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -11,54 +11,57 @@ myst: # GenericSetup -GenericSetup is a framework to modify the Plone site during add-on package installation and uninstallation. - -It provides XML-based rules to change the site settings. - ```{todo} remove archetypes example code everywhere ``` +This chapter describes how to use GenericSetup to modify the Plone site during add-on package installation and uninstallation. + + +## Usage overview + GenericSetup is mainly used to prepare the Plone site for add-on packages, by: - registering Registry entries, such as resources and configuration - setting various properties - registering portlets -- registering portal_catalog search query indexes -- providing upgrade steps for addon version upgrades +- registering `portal_catalog` search query indexes +- providing upgrade steps for add-on version upgrades +- enable specific behaviors - and other preparations -GenericSetup is mostly used to apply an add-on's specific changes to the site configuration and to enable specific behaviors when the add-on installer is run. - +GenericSetup provides XML-based rules to change the site settings. GenericSetup XML files are usually in a {file}`profiles/default` folder inside the add-on package. All run-time through-the-web ({term}`TTW`) configurable items—for example, viewlet order through the `/@@manage-viewlets` page—are made repeatable using GenericSetup profile files. -You can always change the configuration options through Plone or using the Management Interface, and then you export the resulting profile as an XML file, using the *Export* tab in `portal_setup` accessible from the Management Interface. - -Directly editing XML profile files does not change anything on the site, even after Zope restart. -This is because run-time TTW configurable items are stored in the database. +You can always change the configuration options through either Plone or the {term}`Zope Management Interface` (ZMI), and then export the resulting profile as an XML file. +To export, navigate to {menuselection}`Site Setup --> Management Interface`, then click {guilabel}`portal_setup`, and finally click the {guilabel}`Export` tab to select the steps to export. -If you edit profile files, you need to either reimport the edited files using the `portal_setup` tool or fully rerun the add-on package installer in Plone control panel. +Directly editing XML profile files does not change anything on the site, even after a Zope restart. +This is because run-time TTW configurable items are stored in the database, instead of the file system. -This import will read XML files and change the Plone database accordingly. +If you edit profile files, you need to either reimport the edited files using the `portal_setup` tool or fully rerun the add-on package installer by navigating to {menuselection}`Site Setup --> Add-ons`. +The import or rerun will read XML files and change the Plone database accordingly. ```{note} -Difference between ZCML and GenericSetup - -ZCML changes affect loaded Python code in **all** sites inside Zope whereas GenericSetup XML files affect only one Plone site and its database. +ZCML changes affect loaded Python code in all sites inside Zope, whereas GenericSetup XML files affect only one Plone site and its database. GenericSetup XML files are always database changes. -Relationship between ZCML and site-specific behavior is usually done using {doc}`layers `. -ZCML directives, like viewlets and views, are registered to be active only on a certain layer using `layer` attribute. +Creating relationships between ZCML and site-specific behavior is usually done using {doc}`layers `. +ZCML directives, like viewlets and views, are registered to be active only on a certain layer using the `layer` attribute. When GenericSetup XML is imported through `portal_setup`, or the add-on package installer is run for a Plone site, the layer is activated for the particular site only, enabling all views registered for this layer. ``` ```{note} -The `metadata.xml` file (add-on dependency and version information) is read during Plone start-up. -If this file has problems, your add-on might not appear in the installer control panel. +The {file}`metadata.xml` file, which contains add-on dependency and version information, is read during Plone start up. +If this file has problems, your add-on might not appear in the add-on installer control panel. ``` +```{seealso} +- [GenericSetup package page](https://pypi.python.org/pypi/Products.GenericSetup) +- [GenericSetup source code](https://github.com/zopefoundation/Products.GenericSetup) +``` - [GenericSetup package page](https://pypi.python.org/pypi/Products.GenericSetup). - [GenericSetup source code](https://github.com/zopefoundation/Products.GenericSetup). diff --git a/docs/glossary.md b/docs/glossary.md index 1ee4f8e3c2..a3bdbdb78b 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -159,6 +159,7 @@ Dexterity Dublin Core The Dublin Core Schema is a small set of vocabulary terms that can be used to describe web resources (video, images, web pages, etc.), as well as physical resources such as books or CDs, and objects like artworks. +Zope Management Interface ZMI The {term}`Zope` Management Interface. The ZMI is a direct interface into the backend software stack of Plone. diff --git a/styles/config/vocabularies/Plone/accept.txt b/styles/config/vocabularies/Plone/accept.txt index c0b583ac1c..a4a79779a6 100644 --- a/styles/config/vocabularies/Plone/accept.txt +++ b/styles/config/vocabularies/Plone/accept.txt @@ -61,6 +61,7 @@ transpilation transpile[drs]{0,1} [Uu]ncomment [Uu]nhide +uninstallation unregister untranspiled UUID From 0b24fa256ed0f8768dcac0749d8a82a0cb3399cb Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 15:45:47 -0700 Subject: [PATCH 17/42] Tidy up links --- docs/backend/generic-setup.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index cdbbfc10ea..ac6c12a56d 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -63,8 +63,6 @@ If this file has problems, your add-on might not appear in the add-on installer - [GenericSetup source code](https://github.com/zopefoundation/Products.GenericSetup) ``` -- [GenericSetup package page](https://pypi.python.org/pypi/Products.GenericSetup). -- [GenericSetup source code](https://github.com/zopefoundation/Products.GenericSetup). ## Creating A Profile From 0bb47fe1c45b329715c4a1824e5660cc99103043 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 15:48:22 -0700 Subject: [PATCH 18/42] Clean up Create a profile section --- docs/backend/generic-setup.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index ac6c12a56d..f5c30c65d0 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -64,14 +64,14 @@ If this file has problems, your add-on might not appear in the add-on installer ``` -## Creating A Profile +## Create a profile -You use `` directive in your add-on package's `configure.zcml`. +Use the `` directive in your add-on package's {file}`configure.zcml`. The name for the default profile executed by the Plone add-on installer is `default`. -If you need different profiles, for example for unit testing, you can declare them here. +If you need different profiles, for example, for unit testing, you can declare them here. -XML files for the `default` profile go in the `profiles/default` folder inside your add-on package. +XML files for the `default` profile go in the {file}`profiles/default` folder inside your add-on package. ```xml Date: Tue, 12 May 2026 15:49:03 -0700 Subject: [PATCH 19/42] Clean up Multiple profiles section, removing obsolete mention of 5.0 and older --- docs/backend/generic-setup.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index f5c30c65d0..c0c09cc4ad 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -90,22 +90,15 @@ XML files for the `default` profile go in the {file}`profiles/default` folder in ``` -### Multiple Profiles +### Multiple profiles -When you have more than one profile in your add-on package, the add-ons control panel needs to decide which one to use when you install it. +When you have more than one profile in your add-on package, the add-ons control panel decides which one to use when you install it. -Since Plone 5.1, when there is a `default` profile, it is always used as the installation profile, regardless of other profile names. +Since Plone 5.1, when there is a `default` profile, it's used as the installation profile, except when this `default` profile is marked in an `INonInstallable` utility. +In this case, it's ignored, and Plone falls back to using the first profile sorted alphabetically by `name`. -Exception: when this `default` profile is marked in an `INonInstallable` utility, it is ignored and Plone falls back to using the first from the alphabetical sorting. -```{note} -In Plone 5.0 and lower, -the profiles are sorted alphabetically by id, -and the first one is chosen. -If you have profiles `base` and `default`, -the `base` profile is installed. -It is recommended to let `default` be the alphabetically first profile. -``` +## Add-on specific issues ## Add-on-specific Issues From 751d60291b10163da35497a3a31dfaf8c5b8aea5 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 16:02:52 -0700 Subject: [PATCH 20/42] Clean up "Add-on specific issues" - Rename to "Add-on properties", as these aren't issues at all. - Fix incorrect cross-reference link to custom installation steps - Rename `Import various` step to the step actually used, `setup_various` - Create a seealso directive to a really old web archive. Maybe remove this link? --- docs/backend/generic-setup.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index c0c09cc4ad..131f152241 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -98,23 +98,22 @@ Since Plone 5.1, when there is a `default` profile, it's used as the installatio In this case, it's ignored, and Plone falls back to using the first profile sorted alphabetically by `name`. -## Add-on specific issues +## Add-on properties -## Add-on-specific Issues +Add-on packages may contain any of the following items. -Add-on packages may contain: +- A default GenericSetup XML profile, which is automatically run when the package is installed using the quick-installer. + The profile name is usually `default`. +- Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Export` tab, or which can be manually enabled for unit tests. +- An `setup_various` step, which runs Python code every time the GenericSetup XML profile is installed. + See {ref}`genericsetup-custom-installer-code-label`. +- A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or later. + See {ref}`genericsetup-custom-installer-code-label`. -- A default GenericSetup XML profile which is automatically run when the package is installed using the quick-installer. - The profile name is usually `default`. -- Other profiles which the user may install using the `portal_setup` *Import* tab, or which can be manually enabled for unit tests. -- An "Import various" step, which runs Python code every time the GenericSetup XML profile is installed. - See {ref}`custominstall`. -- A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or higher. - See note at {ref}`custominstall`. - -For more information about custom import steps, see: +```{seealso} +[Custom import steps](https://web.archive.org/web/20151016163743/https://plone.293351.n2.nabble.com/indexing-of-content-created-by-Generic-Setup-td4454703.html) +``` -- ## Listing Available Profiles From 72fbc5c7c68f9059bdc240860e2a1b9e27a151fc Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 16:03:50 -0700 Subject: [PATCH 21/42] Clean up "List available profiles" - Move comment from code block to narrative text. - Use python lexer in code block. --- docs/backend/generic-setup.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 131f152241..53d4499e18 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -115,17 +115,16 @@ Add-on packages may contain any of the following items. ``` -## Listing Available Profiles +## List available profiles -Example: +List all known profiles for the Plone instance. -``` -# List all profiles know to the Plone instance. +```python setup_tool = self.portal.portal_setup profiles = setup_tool.listProfileInfo() for profile in profiles: - print str(profile) + print str(profile) ``` Sample results: From 300fd17e4ad358e0d48442b87b4dfc725ec5aee2 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 16:12:18 -0700 Subject: [PATCH 22/42] Clean up "Install a profile" and its subsections - Clarify purpose of this section with definitive language instead of vague language. - `plone.app.testing` fix link to current documentation on GitHub. - Rename "Manually" section to explicit "Manual installation" - Use python lexer in code block. - Change `note` to `versionchanged` directive and clarify wording. --- docs/backend/generic-setup.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 53d4499e18..8d95b07006 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -149,30 +149,33 @@ Sample results: ... ``` -## Installing A Profile -This is usually unit test specific question how to enable certain add-ons for unit testing. +## Install a profile -### plone.app.testing +This section describes how to install a profile and enable add-ons for unit tests. -See [Product and profile installation](http://docs.plone.org/external/plone.app.testing/docs/source/README.html#product-and-profile-installation). -### Manually +### `plone.app.testing` + +See [Product and profile installation](https://github.com/plone/plone.app.testing#product-and-profile-installation). + + +### Manual installation You might want to install profiles manually if they need to be enabled only for certain tests. -The profile name is in the format `profile-${package_name}:${profile id}` +The profile name is in the format `profile-${package_name}:${profile id}`. -Unit testing example: +Run the extended profile of the `your.addonpackage` package for unit tests. -``` -# Run the extended profile of the "your.addonpackage" package. -setup_tool.runAllImportStepsFromProfile('profile-your.addonpackage:extended') +```python +setup_tool.runAllImportStepsFromProfile('your.addonpackage:extended') ``` -```{note} -Since Products.GenericSetup 1.8.0 the `profile-` part is optional. -The code can handle both. +```{versionchanged} Products.GenericSetup 1.8.0 +Since `Products.GenericSetup` 1.8.0, the `profile-` prefix is optional. +In previous versions, the prefix was required, for example, `profile-your.addonpackage:extended`. +GenericSetup supports both forms. ``` ## Missing Upgrade Procedure From def7ca5db2be9e39010c3df9ff1b65a396b8ad0f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 16:22:02 -0700 Subject: [PATCH 23/42] Add a `versionchanged` directive and clarify wording to "Multiple profiles" section. --- docs/backend/generic-setup.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 8d95b07006..fc161c783c 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -94,9 +94,14 @@ XML files for the `default` profile go in the {file}`profiles/default` folder in When you have more than one profile in your add-on package, the add-ons control panel decides which one to use when you install it. -Since Plone 5.1, when there is a `default` profile, it's used as the installation profile, except when this `default` profile is marked in an `INonInstallable` utility. +When there is a `default` profile, it's used as the installation profile, except when this `default` profile is marked in an `INonInstallable` utility. In this case, it's ignored, and Plone falls back to using the first profile sorted alphabetically by `name`. +```{versionchanged} Plone 5.1 +The profile `name` of `default` now takes precedence over the `name` of the first profile sorted alphabetically. +In previous versions, the first profile sorted alphabetically by `name` was chosen without regard to the `default` name. +``` + ## Add-on properties From df98e973d0db8aa0562f013625def43f4aa0e61b Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 16:26:25 -0700 Subject: [PATCH 24/42] Grammar, MyST syntax, and style corrections --- docs/backend/generic-setup.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index fc161c783c..15a204205d 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -183,14 +183,15 @@ In previous versions, the prefix was required, for example, `profile-your.addonp GenericSetup supports both forms. ``` -## Missing Upgrade Procedure -In the Add-ons control panel you may see a warning that your add-on package is [missing an upgrade procedure](http://stackoverflow.com/questions/15316583/how-to-define-a-procedure-to-upgrade-an-add-on). +## Missing upgrade procedure + +In the {guilabel}`Add-ons` control panel, you may see a warning that your add-on package is [missing an upgrade procedure](https://stackoverflow.com/questions/15316583/how-to-define-a-procedure-to-upgrade-an-add-on). This means you need to write some {ref}`genericsetup-upgrade-steps-label`. -## Uninstall Profile +## Uninstall profile When you deactivate an add-on in the control panel, Plone looks for a profile with the name `uninstall` and applies it. @@ -204,12 +205,10 @@ If you do activate the add-on, no deactivate button will be shown. GenericSetup profile can contain dependencies to other add-on package installers and profiles. -For example, if you want to declare a dependency to the *your.addonpackage* package, that it is automatically installed when your add-on is installed, -you can use the declaration below. - -This way you can be sure that all layers, portlets and other features which require database changes are usable from *your.addonpackage* when it is run. +For example, if you want to declare a dependency to the `your.addonpackage` package that it is automatically installed when your add-on is installed, then use the declaration below. +This way you can be sure that all layers, portlets, and other features which require database changes are usable from `your.addonpackage` when it is run. -`metadata.xml`: +Edit {file}`metadata.xml`. ```xml @@ -221,7 +220,7 @@ This way you can be sure that all layers, portlets and other features which requ ``` -*your.addonpackage* declares the profile in its configure.zcml: +`your.addonpackage` declares the profile in its {file}`configure.zcml`. ```xml @@ -261,6 +260,7 @@ To declare a dependency on the `simple` profile of `Products.PluggableAuthServic ``` + ## Metadata version numbers Some old add-on packages may have a `metadata.xml` without version number, but this is considered bad practice. From e8665230e398850208620d12d62092103e279c5c Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 01:24:12 -0700 Subject: [PATCH 25/42] Revise "Metadata version numbers" to modernize it to a best practice placed first, and remove unnecessary and obsolete information. --- docs/backend/generic-setup.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 15a204205d..3a037e0127 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -263,15 +263,20 @@ The following code example shows how to declare a dependency on the `simple` pro ## Metadata version numbers -Some old add-on packages may have a `metadata.xml` without version number, but this is considered bad practice. +The metadata `version` number in your {file}`metadata.xml` indicates the version of your add-on package. +It's used to determine whether the add-on package needs to be upgraded. -What should the version number in your `metadata.xml` be? +Upgrade steps are executed in the Python version sort order, according to [Version specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/). -This mostly matters when you are adding upgrade steps, see also the [Upgrade steps] section. - -Upgrade steps have a sort order in which they are executed. This used to be alphabetical sorting. +```{note} +Legacy add-on packages might not have a version number, or use an integer version number. +In old versions of GenericSetup, sorting was done alphabetically, not according to the Python version specifiers. +If you experience problems with upgrade steps, you might need to upgrade GenericSetup, or add a metadata `version` number to align with your package's version number. +``` -When you had eleven upgrade steps, marked from 1 through 11, alphabetical sorting meant this order: 1, 10, 11, 2, 3, etc. +```{seealso} +{ref}`genericsetup-upgrade-steps-label` +``` If you are seeing this, then you are using an old version of GenericSetup. From fc88c28327870b2d225b33577944513612ea361c Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 01:25:21 -0700 Subject: [PATCH 26/42] Revise "Metadata version numbers" to modernize it to a best practice placed first, and remove unnecessary and obsolete information. --- docs/backend/generic-setup.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 3a037e0127..8a19613d12 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -278,26 +278,6 @@ If you experience problems with upgrade steps, you might need to upgrade Generic {ref}`genericsetup-upgrade-steps-label` ``` -If you are seeing this, then you are using an old version of GenericSetup. - -You want numerical sorting here, which is correctly done currently. Versions with dots work fine too. - -They get ordered just like they would when used for packages on PyPI. - -Best practice for all versions of GenericSetup is this: - -- Start with 1000. - This avoids problems with ancient GenericSetup that used alphabetical sorting. -- Simply increase the version by 1 each time you need a new metadata version. - For example: 1001, 1002, etc. -- If your add-on package version number changes, but your profile stays the same and no upgrade step is needed, you should **not** change the metadata version. - There is no need. -- If you make changes for a new major release, you should increase the metadata version significantly. - This leaves room for small metadata version increases on a maintenance branch. - Example: - You have branch master with version 1025. - You make backwards incompatible changes and you increase the version to 2000. - You create a maintenance branch where the next metadata version will be 1026. (genericsetup-custom-installer-code-label)= From f5a5e568dceaaadaa2ebaa31da3d8f832a8d65a5 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 01:28:15 -0700 Subject: [PATCH 27/42] Revise "Custom installer code" - Improve grammar, syntax, and style - It's still way too confusing and needs technical help. --- docs/backend/generic-setup.md | 42 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 8a19613d12..3e5dfd643e 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -191,6 +191,8 @@ In the {guilabel}`Add-ons` control panel, you may see a warning that your add-on This means you need to write some {ref}`genericsetup-upgrade-steps-label`. +(genericsetup-uninstall-profile-label)= + ## Uninstall profile When you deactivate an add-on in the control panel, Plone looks for a profile with the name `uninstall` and applies it. @@ -281,14 +283,17 @@ If you experience problems with upgrade steps, you might need to upgrade Generic (genericsetup-custom-installer-code-label)= -## Custom Installer Code (`setuphandlers.py`) +## Custom installer code -Besides out-of-the-box XML steps which provide both install and uninstall, -GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled. +In addition to upgrade steps to install and uninstall add-ons, GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled. +The following examples show how to do this. -In `configure.zcml`: +First, edit {file}`configure.zcml`, adding a `pre_handler` and `post_handler` attribute to the `registerProfile` element. + +```{code-block} xml +:linenos: +:emphasize-lines: 11-13 -``` ``` -In `setuphandlers.py`: +Then edit {file}`setuphandlers.py`, adding the Python functions `run_before` and `run_after` according to the `pre_handler` and `post_handler` attributes. ```python def run_before(context): # This is run before running the first import step of - # the default profile. context is portal_setup. + # the default profile. The context is portal_setup. # If you need the same context as you would get in # an import step, like setup_various below, do this: profile_id = 'profile-your.addonpackage:default' good_old_context = context._getImportContext(profile_id) - ... + # ... def run_after(context): # This is run after running the last import step of # the default profile. context is portal_setup. ... + # the default profile. The context is portal_setup. + # ... ``` -The best practice is to create a `setuphandlers.py` file which contains a function `setup_various()` which runs the required Python code -to make changes to Plone site object. - +The best practice is to create a {file}`setuphandlers.py` file, and include a function `setup_various()` which runs the required Python code to make changes to the Plone site object. This function is registered as a custom `genericsetup:importStep` in XML. -```{note} -When you write a custom `importStep`, remember to write uninstallation code as well. -``` - However, the trick is that all GenericSetup import steps, including your custom step, are run for *every* add-on package when they are installed. -If your need to run code which is **specific to your add-on install only** you need to use a marker text file which is checked by the GenericSetup context. +If you need to run code which is **specific to your add-on install only** you need to use a marker text file which is checked by the GenericSetup context. -Also you need to register this custom import step in `configure.zcml`: +Also, you need to register this custom import step in {file}`configure.zcml`. ```xml ``` +````{tip} +When you write a custom `importStep`, remember to write uninstallation code as well. +```{seealso} +{ref}`genericsetup-uninstall-profile-label` +``` +```` + You can run other steps before yours by using the `depends` directive. For instance, if your import step depends on a content type to be installed first, you must use: From ca80a8e3c1dd2f2a4d9767e962ae9a9daf21ff33 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 01:30:13 -0700 Subject: [PATCH 28/42] - Clean up code comments - Fix reference target syntax - Replace Management Interface with ZMI - Add a TODO to fix the autodoc warning. --- docs/backend/generic-setup.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 3e5dfd643e..cdd4cc174d 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -326,8 +326,6 @@ def run_before(context): def run_after(context): # This is run after running the last import step of - # the default profile. context is portal_setup. - ... # the default profile. The context is portal_setup. # ... ``` @@ -457,7 +455,7 @@ See {ref}`genericsetup-generic-setup-files-label` for a list. ``` -(genericsetup-upgrade-steps-label)- +(genericsetup-upgrade-steps-label)= ## Upgrade Steps @@ -577,7 +575,7 @@ Some of the most used ones are here: - - -After restarting Zope, your upgrade step should be visible in the Management Interface: +After restarting Zope, your upgrade step should be visible in the ZMI: the `portal_setup` tool has a tab `Upgrades`. Select your package profile to see which upgrade steps Zope knows about for your add-on. @@ -751,7 +749,7 @@ Example: ``` ```{note} -In the portal_actions tool, in the Management Interface, you will see an i18n domain specified for each action. +In the portal_actions tool, in the ZMI, you will see an i18n domain specified for each action. ``` - `catalog.xml`: no i18n needed @@ -1491,6 +1489,9 @@ This will reset the permission to the same settings as on the Zope level: ``` ```` +% todo: fix WARNING: +% WARNING: missing attribute mentioned in :members: option: module Products.GenericSetup.rolemap, attribute importRolemap RolemapImportConfigurator [autodoc] + ```{eval-rst} .. automodule:: Products.GenericSetup.rolemap :members: importRolemap RolemapImportConfigurator From a2afebdbf9a81fa59d69f98fefa4104c9b314246 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 01:30:46 -0700 Subject: [PATCH 29/42] - Fix typos --- docs/developer-guide/create-a-backend-add-on.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developer-guide/create-a-backend-add-on.md b/docs/developer-guide/create-a-backend-add-on.md index e51e1d9f02..1a535cde1a 100644 --- a/docs/developer-guide/create-a-backend-add-on.md +++ b/docs/developer-guide/create-a-backend-add-on.md @@ -19,7 +19,7 @@ Follow the section {ref}`create-project-cookieplone-system-requirements` to set ## Generate the add-on project with `plonecli` -Choose your desired local development base folder and run the following command to create an addon project with `plonecli` +Choose your desired local development base folder and run the following command to create an add-on project with `plonecli`. ```shell uvx plonecli create addon @@ -139,7 +139,7 @@ Currently documented subtemplates: - behavior: {ref}`backend-behaviors-label` - content_type: {ref}`creating-content-types-label` - controlpanel: {ref}`control-panels-label` -- form: {reg}`forms-label` +- form: {ref}`forms-label` - mockup_pattern: {ref}`mockup-and-patternslib-label` - theme_barceloneta: {ref}`create-a-theme-add-on-label` From 9dd5dc372cda4a8a66f17dcc717e5f22bd0047bc Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 03:50:34 -0700 Subject: [PATCH 30/42] Fix reference target --- docs/backend/generic-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index cdd4cc174d..d3b67b855f 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -48,7 +48,7 @@ The import or rerun will read XML files and change the Plone database accordingl ZCML changes affect loaded Python code in all sites inside Zope, whereas GenericSetup XML files affect only one Plone site and its database. GenericSetup XML files are always database changes. -Creating relationships between ZCML and site-specific behavior is usually done using {doc}`layers `. +Creating relationships between ZCML and site-specific behavior is usually done using {doc}`layers `. ZCML directives, like viewlets and views, are registered to be active only on a certain layer using the `layer` attribute. When GenericSetup XML is imported through `portal_setup`, or the add-on package installer is run for a Plone site, the layer is activated for the particular site only, enabling all views registered for this layer. ``` From 800adccb968cb719e82388465fe4dd3b8c655ebe Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 03:51:51 -0700 Subject: [PATCH 31/42] Fix invalid XML syntax --- docs/backend/generic-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index d3b67b855f..8eb2e6aefc 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -721,7 +721,7 @@ For example, if you only need to change the 'Allow anonymous to view about' prop True - ``` From 5975f465cc3a5507d669269f709a11b67f13d6b5 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 03:56:29 -0700 Subject: [PATCH 32/42] Fix invalid reference links --- docs/backend/generic-setup.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 8eb2e6aefc..9dff8fba1d 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -787,7 +787,7 @@ Example: - `workflows`: use the **plone** domain -(genericsetup-generic-setup-files-label)- +(genericsetup-generic-setup-files-label)= ## Generic Setup Files @@ -1469,7 +1469,7 @@ ValueError: The permission Pass the bridge is invalid. ``` A permission is created on the Zope level when it is used in code. -See {doc}`Creating permissions `. +See {ref}`backend-security-permissions-label`. When a role in a permission does not exist, it is silently ignored. The roles listed in a permission are not added. @@ -1501,7 +1501,7 @@ This will reset the permission to the same settings as on the Zope level: ### sharing.xml The sharing.xml file let you add custom roles to the sharing tab. -For reference, visit: {ref}`security-label`. +For reference, visit {doc}`/backend/security`. ### typeinfo @@ -1741,4 +1741,4 @@ Next to this, the `workflows` directory is checked. This contains sub directories with the same name as the workflows. Each sub directory contains a file `definition.xml` with the definition for this workflow. -See {ref}`workflows-label`. +See {doc}`/backend/workflows`. From 0beb3e8d91e5cbd239e5612fcfb76dab5e037af7 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 04:04:54 -0700 Subject: [PATCH 33/42] Fix broken automodule member list. However, now it reveals malformed docstrings in the Products.GenericSetup.rolemap module. ``` /documentation/venv/lib/python3.13/site-packages/Products/GenericSetup/rolemap.py:docstring of Products.GenericSetup.rolemap.importRolemap:9: ERROR: Unexpected indentation. [docutils] /documentation/venv/lib/python3.13/site-packages/Products/GenericSetup/rolemap.py:docstring of Products.GenericSetup.rolemap.importRolemap:19: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] ``` --- docs/backend/generic-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 9dff8fba1d..95298a162f 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -1494,7 +1494,7 @@ This will reset the permission to the same settings as on the Zope level: ```{eval-rst} .. automodule:: Products.GenericSetup.rolemap - :members: importRolemap RolemapImportConfigurator + :members: importRolemap, RolemapImportConfigurator ``` From 8e2edeae2c484daa6cc434c106405084b6b04024 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 13 May 2026 04:05:22 -0700 Subject: [PATCH 34/42] Fix broken automodule member list. However, now it reveals malformed docstrings in the Products.GenericSetup.rolemap module. ``` /documentation/venv/lib/python3.13/site-packages/Products/GenericSetup/rolemap.py:docstring of Products.GenericSetup.rolemap.importRolemap:9: ERROR: Unexpected indentation. [docutils] /documentation/venv/lib/python3.13/site-packages/Products/GenericSetup/rolemap.py:docstring of Products.GenericSetup.rolemap.importRolemap:19: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils] ``` --- docs/backend/generic-setup.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 95298a162f..19192f5452 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -1489,13 +1489,9 @@ This will reset the permission to the same settings as on the Zope level: ``` ```` -% todo: fix WARNING: -% WARNING: missing attribute mentioned in :members: option: module Products.GenericSetup.rolemap, attribute importRolemap RolemapImportConfigurator [autodoc] - ```{eval-rst} .. automodule:: Products.GenericSetup.rolemap :members: importRolemap, RolemapImportConfigurator - ``` ### sharing.xml From eac7d663419259c38548fd7b31b21f09ffa7212c Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 21 May 2026 02:54:47 -0700 Subject: [PATCH 35/42] Apply suggestions from code review Co-authored-by: David Glick --- docs/backend/generic-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 19192f5452..39f985ef97 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -109,7 +109,7 @@ Add-on packages may contain any of the following items. - A default GenericSetup XML profile, which is automatically run when the package is installed using the quick-installer. The profile name is usually `default`. -- Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Export` tab, or which can be manually enabled for unit tests. +- Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Import` tab, or which can be manually enabled for unit tests. - An `setup_various` step, which runs Python code every time the GenericSetup XML profile is installed. See {ref}`genericsetup-custom-installer-code-label`. - A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or later. From 596bd255c902ace783713e81d7e35ca0b605038a Mon Sep 17 00:00:00 2001 From: David Glick Date: Thu, 21 May 2026 13:33:59 +0200 Subject: [PATCH 36/42] Remove references to archetypes --- docs/backend/generic-setup.md | 127 ++++------------------------------ 1 file changed, 15 insertions(+), 112 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 39f985ef97..d9172d7357 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -11,10 +11,6 @@ myst: # GenericSetup -```{todo} -remove archetypes example code everywhere -``` - This chapter describes how to use GenericSetup to modify the Plone site during add-on package installation and uninstallation. @@ -109,7 +105,7 @@ Add-on packages may contain any of the following items. - A default GenericSetup XML profile, which is automatically run when the package is installed using the quick-installer. The profile name is usually `default`. -- Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Import` tab, or which can be manually enabled for unit tests. +- Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Import` tab, or which can be manually enabled for unit tests. - An `setup_various` step, which runs Python code every time the GenericSetup XML profile is installed. See {ref}`genericsetup-custom-installer-code-label`. - A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or later. @@ -894,34 +890,23 @@ Uninstall example: ### componentregistry.xml -Setup items in the local component registry of the Plone Site. +Register {term}`Zope Component Architecture` components in the local component registry of the Plone Site. The items can be adapters, subscribers or utilities. -This can also be done in zcml, which puts it in the global registry that is defined at startup. - -The difference is, when you put it in xml, the item is only added to a specific Plone Site when you install the package in the add-ons control panel. +This can also be done in ZCML, which puts it in the global registry that is defined at startup. -Both have their uses. +The difference is, when you put it in {file}`componentregistry.xml`, the item is only added to a specific Plone Site when you install the package in the add-ons control panel. Example: ```xml - - - - - - + + + ``` @@ -947,27 +932,11 @@ Uninstall example: ```xml - - - - - - + interface="collective.solr.interfaces.ISolrConnectionManager" + factory="collective.solr.manager.SolrConnectionManager"/> ``` @@ -1088,19 +1057,10 @@ This configures how the difference between two versions of a field are shown on The configuration is stored in the `portal_diff` tool. -For Archetypes content, you need a different `difftype`: - -```xml - - - -``` - A new `difftype` can be registered by calling `Products.CMFDiffTool.CMFDiffTool.registerDiffType`. -The `difftypes` in standard Plone 5 are: +The available `difftypes` in Plone are: - `Lines Diff` -- `Compound Diff for AT types` - `Binary Diff` - `Field Diff` - `List Diff` @@ -1529,11 +1489,10 @@ Partial example from `plone.app.contenttypes`: This adds content types in the `portal_types` tool. The `meta_type` can be: -- `Dexterity FTI` for Dexterity content. +- `Dexterity FTI` for Dexterity content, including the Plone Site itself. This is probably what you want. -- `Factory-based Type Information with dynamic views` for Archetypes content and for the Plone Site itself -- `Factory-based Type Information` for Archetypes content that does not need dynamic views, - the ability to choose a view in the `display` menu. +- `Factory-based Type Information with dynamic views` for custom content types that use dynamic views (the ability to choose a view in the Display menu). +- `Factory-based Type Information` for custom content types that do not need dynamic views. The `types.xml` should be accompanied by a `types` folder with details information on the new types. If you are editing an already existing type, then `types.xml` is not needed: @@ -1605,62 +1564,6 @@ This file is in `plone.app.contenttypes`: ``` -For comparison, here is the `types.xml` from `plone.app.collection` which has an old style Archetypes Collection: - -```xml - - - - - - -``` - -And here is the `types/Collection.xml` from `plone.app.collection`: - -```xml - - - Collection - Collection of searchable information - - Collection - plone.app.collection - addCollection - standard_view - True - True - - False - standard_view - - - - - - - - - - - - - - - - - - -``` - Uninstall example: ```xml From 31321a2b51473e41aad00bf2be4249ae2cdbc86a Mon Sep 17 00:00:00 2001 From: David Glick Date: Thu, 21 May 2026 15:19:09 +0200 Subject: [PATCH 37/42] Edits after my review --- docs/backend/generic-setup.md | 131 ++++++++++++---------------------- 1 file changed, 44 insertions(+), 87 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index d9172d7357..29d2dc009d 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -110,10 +110,8 @@ Add-on packages may contain any of the following items. See {ref}`genericsetup-custom-installer-code-label`. - A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or later. See {ref}`genericsetup-custom-installer-code-label`. - -```{seealso} -[Custom import steps](https://web.archive.org/web/20151016163743/https://plone.293351.n2.nabble.com/indexing-of-content-created-by-Generic-Setup-td4454703.html) -``` +- Custom import steps to process additional XML files. + See {ref}`genericsetup-custom-import-steps`. ## List available profiles @@ -121,11 +119,12 @@ Add-on packages may contain any of the following items. List all known profiles for the Plone instance. ```python -setup_tool = self.portal.portal_setup +from plone import api +setup_tool = api.portal.get_tool("portal_setup") profiles = setup_tool.listProfileInfo() for profile in profiles: - print str(profile) + print(str(profile)) ``` Sample results: @@ -167,7 +166,7 @@ You might want to install profiles manually if they need to be enabled only for The profile name is in the format `profile-${package_name}:${profile id}`. -Run the extended profile of the `your.addonpackage` package for unit tests. +For example, this runs the `extended` profile of the `your.addonpackage` package. ```python setup_tool.runAllImportStepsFromProfile('your.addonpackage:extended') @@ -191,17 +190,17 @@ This means you need to write some {ref}`genericsetup-upgrade-steps-label`. ## Uninstall profile -When you deactivate an add-on in the control panel, Plone looks for a profile with the name `uninstall` and applies it. +When you uninstall an add-on in the control panel, Plone looks for a profile with the name `uninstall` and applies it. ```{note} If there is no `uninstall` profile, a warning is displayed before installing the add-on. -If you do activate the add-on, no deactivate button will be shown. +If you do install the add-on, no uninstall button will be shown. ``` ## Dependencies -GenericSetup profile can contain dependencies to other add-on package installers and profiles. +A GenericSetup profile can contain dependencies to other add-on package installers and profiles. For example, if you want to declare a dependency to the `your.addonpackage` package that it is automatically installed when your add-on is installed, then use the declaration below. This way you can be sure that all layers, portlets, and other features which require database changes are usable from `your.addonpackage` when it is run. @@ -267,7 +266,7 @@ It's used to determine whether the add-on package needs to be upgraded. Upgrade steps are executed in the Python version sort order, according to [Version specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/). ```{note} -Legacy add-on packages might not have a version number, or use an integer version number. +Legacy add-on packages might not have a version number. In old versions of GenericSetup, sorting was done alphabetically, not according to the Python version specifiers. If you experience problems with upgrade steps, you might need to upgrade GenericSetup, or add a metadata `version` number to align with your package's version number. ``` @@ -281,7 +280,7 @@ If you experience problems with upgrade steps, you might need to upgrade Generic ## Custom installer code -In addition to upgrade steps to install and uninstall add-ons, GenericSetup provides a way to run custom Python code when your add-on package is installed and uninstalled. +GenericSetup provides a way to run custom Python code before or after your add-on package is installed. The following examples show how to do this. First, edit {file}`configure.zcml`, adding a `pre_handler` and `post_handler` attribute to the `registerProfile` element. @@ -314,11 +313,7 @@ Then edit {file}`setuphandlers.py`, adding the Python functions `run_before` and def run_before(context): # This is run before running the first import step of # the default profile. The context is portal_setup. - # If you need the same context as you would get in - # an import step, like setup_various below, do this: - profile_id = 'profile-your.addonpackage:default' - good_old_context = context._getImportContext(profile_id) - # ... + # Add custom code here... def run_after(context): # This is run after running the last import step of @@ -326,14 +321,37 @@ def run_after(context): # ... ``` -The best practice is to create a {file}`setuphandlers.py` file, and include a function `setup_various()` which runs the required Python code to make changes to the Plone site object. -This function is registered as a custom `genericsetup:importStep` in XML. +## Custom install steps + +You can register a custom import step. +This is a Python function which will be run for _every_ GenericSetup profile. +It provides a way to extend the possible changes that can be made when a profile is installed. + +```{tip} +If you only want to run custom code when one add-on is installed, use ref`{genericsetup-custom-installer-code-label}` instead. +``` + +By convention, custom import steps are usually placed in a {file}`setuphandlers.py` file. + +```python + +def run_custom_code(site): + """Run custom add-on package installation code to modify Plone + site object and others + + @param site: Plone site + """ -However, the trick is that all GenericSetup import steps, including your custom step, are run for *every* add-on package when they are installed. +def setup_various(context): + """ + @param context: Products.GenericSetup.context.DirectoryImportContext instance + """ + portal = context.getSite() -If you need to run code which is **specific to your add-on install only** you need to use a marker text file which is checked by the GenericSetup context. + run_custom_code(portal) +``` -Also, you need to register this custom import step in {file}`configure.zcml`. +This function is registered as a custom `genericsetup:importStep` in {file}`configure.zcml`. ```xml ``` -`setuphandlers.py` example - -```python - -def run_custom_code(site): - """Run custom add-on package installation code to modify Plone - site object and others - - @param site: Plone site - """ - -def setup_various(context): - """ - @param context: Products.GenericSetup.context.DirectoryImportContext instance - """ - - # We check from our GenericSetup context whether we are running - # add-on installation for your package or any other - if context.readDataFile('your.addonpackage.marker.txt') is None: - # Not your add-on - return - - portal = context.getSite() - - run_custom_code(portal) -``` - -And add a dummy text file -`your.addonpackage/your/addonpackage/profiles/default/your.addonpackage.marker.txt`: - -This text file can contain any content - it just needs to be present - - - -## Overriding Import Step Order - -If you need to override the order of import steps in a package that is not yours, -it might work if you [use an overrides.zcml](http://plone.293351.n2.nabble.com/Overriding-import-step-order-td2189638.html). - -### Controlling The Import Step Execution Order - -If you need to control the execution order of one of your own custom import steps, you can do this in your import step definition in zcml. - -To make sure the catalog and typeinfo steps are run before your own step, use this code: - -```xml - - - - - - - - -``` - -```{note} +```{tip} The name that you need, is usually the name of the related xml file, but with the `.xml` stripped. For the `catalog.xml` the import step name is `catalog`. But there are exceptions. From c1714ad76850ab5b0b7c198735668a0532812dc5 Mon Sep 17 00:00:00 2001 From: David Glick Date: Thu, 21 May 2026 15:23:07 +0200 Subject: [PATCH 38/42] Remove rolemap automodule which is a distraction --- docs/backend/generic-setup.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 29d2dc009d..ed92d19fa6 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -1406,11 +1406,6 @@ This will reset the permission to the same settings as on the Zope level: ``` ```` -```{eval-rst} -.. automodule:: Products.GenericSetup.rolemap - :members: importRolemap, RolemapImportConfigurator -``` - ### sharing.xml The sharing.xml file let you add custom roles to the sharing tab. From 92a924062f9bf62abfdbabb758a879530783b3f8 Mon Sep 17 00:00:00 2001 From: David Glick Date: Thu, 21 May 2026 15:28:00 +0200 Subject: [PATCH 39/42] fix ref --- docs/backend/generic-setup.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index ed92d19fa6..4b2ab42694 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -321,7 +321,9 @@ def run_after(context): # ... ``` -## Custom install steps +(genericsetup-custom-import-steps)= + +## Custom import steps You can register a custom import step. This is a Python function which will be run for _every_ GenericSetup profile. From 91497a054b5a7b96d6d0185892b0ed35d67e62ae Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 24 May 2026 03:31:15 -0700 Subject: [PATCH 40/42] Remove old import various step, per https://github.com/plone/documentation/pull/2083#discussion_r3280243322 --- docs/backend/generic-setup.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 4b2ab42694..19024c405f 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -106,8 +106,6 @@ Add-on packages may contain any of the following items. - A default GenericSetup XML profile, which is automatically run when the package is installed using the quick-installer. The profile name is usually `default`. - Other profiles, which the user may install using the ZMI {guilabel}`portal_setup` under the {guilabel}`Import` tab, or which can be manually enabled for unit tests. -- An `setup_various` step, which runs Python code every time the GenericSetup XML profile is installed. - See {ref}`genericsetup-custom-installer-code-label`. - A `pre_handler` or `post_handler` when you use GenericSetup 1.8.2 or later. See {ref}`genericsetup-custom-installer-code-label`. - Custom import steps to process additional XML files. From 025e91cd8b78438997f3f96215fdc1e93365a88e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 24 May 2026 03:33:58 -0700 Subject: [PATCH 41/42] Add import per https://github.com/plone/documentation/pull/2083#discussion_r3280371012 --- docs/backend/generic-setup.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index 19024c405f..fbce5d0844 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -167,6 +167,7 @@ The profile name is in the format `profile-${package_name}:${profile id}`. For example, this runs the `extended` profile of the `your.addonpackage` package. ```python +from plone import api setup_tool.runAllImportStepsFromProfile('your.addonpackage:extended') ``` From b975c1f278fda1a315517f43f8411307e9548a1c Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 24 May 2026 03:37:47 -0700 Subject: [PATCH 42/42] Fix ref link --- docs/backend/generic-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/generic-setup.md b/docs/backend/generic-setup.md index fbce5d0844..6c4496b742 100644 --- a/docs/backend/generic-setup.md +++ b/docs/backend/generic-setup.md @@ -329,7 +329,7 @@ This is a Python function which will be run for _every_ GenericSetup profile. It provides a way to extend the possible changes that can be made when a profile is installed. ```{tip} -If you only want to run custom code when one add-on is installed, use ref`{genericsetup-custom-installer-code-label}` instead. +If you only want to run custom code when one add-on is installed, use {ref}`genericsetup-custom-installer-code-label` instead. ``` By convention, custom import steps are usually placed in a {file}`setuphandlers.py` file.