From 98dfce6ed427bb1988d6a37a7dfdb7eeb30d4a0f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 12 May 2026 15:44:57 -0700 Subject: [PATCH 01/24] 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 02/24] 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 03/24] 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 04/24] 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 05/24] 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 06/24] 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 07/24] 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 08/24] 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 09/24] 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 10/24] 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 11/24] 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 12/24] 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 13/24] - 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 14/24] - 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 15/24] 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 16/24] 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 17/24] 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 18/24] 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 19/24] 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 20/24] 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 21/24] 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 22/24] 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 23/24] 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 24/24] 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.