Skip to content

Adds docs for creating custom control panel #1909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: 6.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
420759b
adds docs for creating custom control panel
rohnsha0 Mar 13, 2025
f1b16b2
Merge branch '6.0' into rohnsha0-control-panel
rohnsha0 Mar 14, 2025
277d4f2
Merge branch '6.0' into rohnsha0-control-panel
rohnsha0 Mar 14, 2025
ac4c3ce
Merge branch '6.0' into rohnsha0-control-panel
stevepiercy Mar 14, 2025
10d99f4
Merge branch '6.0' into rohnsha0-control-panel
stevepiercy Mar 15, 2025
b36a926
Merge remote-tracking branch 'origin/rohnsha0-control-panel' into roh…
stevepiercy Mar 15, 2025
f221c11
Move to developer-guide and rename
stevepiercy Mar 15, 2025
4f87d25
- Flatten hierarchy
stevepiercy Mar 15, 2025
228a0d3
Tidy up meta stuff
stevepiercy Mar 15, 2025
5ad2633
Merge branch '6.0' into rohnsha0-control-panel
stevepiercy Mar 16, 2025
9fcd80f
Merge branch '6.0' into rohnsha0-control-panel
stevepiercy Mar 23, 2025
81e2079
Merge branch '6.0' into rohnsha0-control-panel
rohnsha0 Apr 5, 2025
816824a
Add documentation for REST API compatible control panels in Volto
rohnsha0 Apr 5, 2025
ac9fc7d
Apply suggestions from code review
rohnsha0 Apr 6, 2025
50cb6b7
Merge branch '6.0' into rohnsha0-control-panel
rohnsha0 Apr 6, 2025
27489e4
Apply suggestions from code review
rohnsha0 Apr 7, 2025
d1175d5
Add instructions for restarting Plone after manual control panel setup
rohnsha0 Apr 14, 2025
b3fd198
Merge branch '6.0' into rohnsha0-control-panel
rohnsha0 Apr 14, 2025
2a65a79
Simplify MyST markup, convert inline literals to use code blocks with…
stevepiercy Apr 15, 2025
e1b4aac
Refactor section headers in control panel documentation for consistency
rohnsha0 Apr 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 0 additions & 54 deletions docs/backend/control-panels.md

This file was deleted.

1 change: 0 additions & 1 deletion docs/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ annotations
behaviors
configuration-registry
content-types/index
control-panels
fields
global-utils
indexing
Expand Down
327 changes: 327 additions & 0 deletions docs/developer-guide/create-control-panel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
---
myst:
html_meta:
"description": "How to add a Control Panel"
"property=og:description": "How to add a Control Panel"
"property=og:title": "Control panels"
"keywords": "Plone, Add, Control Panel"
---

(backend-controlpanels-label)=

# Create a control panel

There are two approaches to create a control panel for your Plone add-on:

- [`plonecli`](https://pypi.org/project/plonecli/)
- manual


## `plonecli`

To add a control panel to your add-on, you can use [`plonecli`](https://pypi.org/project/plonecli/) as follows.

```shell
plonecli add controlpanel
```

This creates the control panel Python file in the control panel's folder where you can define your control panel schema fields.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where should the reader go from here? I'm lost. Help! In other words, write documentation as if you didn't have years of experience with Plone.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, after running the plonecli command to create controlpanel. It creates control panel with a basic field (schema.TextLine) for the CLassic and Volto both, like an angle 😇😎

So, ig the reader will now be required to know the schemas, then Fieldset if needed to group fields together

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, we need to direct the reader to those parts of the documentation.

And now that I think about it, we should have only one method. plonecli automatically performs all the steps in the manual process for the reader, so it's silly to say that there are two processes. The steps are identical, and the only difference is automation of performing them. In any case, the reader will need to modify their add-on's schema, as plonecli has no way to read the developer's mind. Yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, we can simply add a note that plonecli does everything done in the manual process... since, the manual process exposes developers to some minor technicalities of the thing



## Manual

To manually create a control panel, go through the following steps.

- Define the settings interface and form.
- Register the control panel view in ZCML.
- Add the control panel to the Plone control panel listing.
- Set default values in the registry.


### Define the settings interface and form

Create a Python module, {file}`mypackage/controlpanel/settings.py`, that defines your control panel's settings interface and form class as follows.

```python
# mypackage/controlpanel/settings.py
from zope import schema
from zope.interface import Interface
from plone.app.registry.browser.controlpanel import RegistryEditForm, ControlPanelFormWrapper
from plone.z3cform import layout

class IMyControlPanelSettings(Interface):
"""Schema for the control panel form."""

my_setting = schema.TextLine(
title=u'My Setting',
description=u'Enter the value for my setting',
required=False,
default=u''
)

my_choice = schema.Choice(
title=u'My Choice',
description=u'Select a value for my choice',
required=False,
default=u'value3',
values=['value1', 'value2', 'value3']
)

class MyControlPanelForm(RegistryEditForm):
"""Control panel form."""

schema = IMyControlPanelSettings
schema_prefix = "my.addon"
label = u"My Addon Settings"

# Wrap the form with plone.z3cform's ControlPanelFormWrapper to get the Plone
# control panel look and feel
MyControlPanelView = layout.wrap_form(MyControlPanelForm, ControlPanelFormWrapper)
```


### Register the control panel view

Create a file {file}`mypackage/controlpanel/configure.zcml` with the following content to register the control panel view in ZCML.

```xml
<!-- mypackage/controlpanel/configure.zcml -->
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="mypackage">

<browser:page
name="my-controlpanel"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
class=".settings.MyControlPanelView"
permission="cmf.ManagePortal"
/>

</configure>
```

Make sure to include the above file in your package's main {file}`mypackage/configure.zcml` as shown by the highlighted line below.

{emphasize-lines="9"}
```xml
<!-- mypackage/configure.zcml -->
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="mypackage">

<!-- Other configuration -->

<include package=".controlpanel" />

</configure>
```

### Add the control panel entry

Create a {file}`mypackage/profiles/default/controlpanel.xml` in your package's GenericSetup profile with the following content to add your control panel to the Plone control panel listing.

```xml
<!-- mypackage/profiles/default/controlpanel.xml -->
<?xml version="1.0"?>
<object name="portal_controlpanel">
<configlet
title="My Addon Settings"
action_id="my-controlpanel"
appId="my.addon"
category="plone-general"
condition_expr=""
icon_expr="string:puzzle"
url_expr="string:${portal_url}/@@my-controlpanel"
visible="True">
<permission>Manage portal</permission>
</configlet>
</object>
```

The category attribute can be one of the following values.
These values correspond to the groups in Site Setup.

`plone-general`
: General settings

`plone-content`
: Content-related settings

`plone-users`
: Users and groups settings

`plone-security`
: Security settings

`plone-advanced`
: Advanced settings


### Set default values in the registry

Define default values for your settings in {file}`mypackage/profiles/default/registry.xml`.

```xml
<!-- mypackage/profiles/default/registry.xml -->
<?xml version="1.0"?>
<registry>
<records interface="mypackage.controlpanel.settings.IMyControlPanelSettings"
prefix="my.addon">
<value key="my_setting">default value</value>
<value key="my_choice">value3</value>
</records>
</registry>
```


### Access your settings in code

You can access your settings in Python code as follows.

```python
from plone.registry.interfaces import IRegistry
from zope.component import getUtility

registry = getUtility(IRegistry)
settings = registry.forInterface(IMyControlPanelSettings, prefix="my.addon")

# Now you can access the settings
my_setting_value = settings.my_setting
my_choice_value = settings.my_choice
```


## Register a control panel

To manually register a view as a control panel, add the following registration to your {file}`/profiles/default/controlpanel.xml`.

```xml
<?xml version="1.0"?>
<object
name="portal_control-panel"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="lmu.behavior">
<configlet
title="Some Control Panel"
action_id="collective.example.some_control-panel"
appId="collective.example"
category="Products"
condition_expr=""
url_expr="string:${portal_url}/@@some_view"
icon_expr=""
visible="True"
i18n:attributes="title">
<permission>Manage portal</permission>
</configlet>
</object>
```


## Use `FieldSet` to group fields
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this also part of the manual process only or both manual and plonecli?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stevepiercy this needs to be added in any way you use to create the controlpanel, as these include the fields (to be grouped) that needs to be shown in the settings (of the controlpanel)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, please see other comments regarding the flow and structure of this chapter. I think we're close to finished with those enhancements.


For complex control panels, you can group fields together as in the following example.

```python
from plone.supermodel import model

class IMyControlPanelSettings(Interface):

model.fieldset(
'advanced',
label=u"Advanced Settings",
fields=['advanced_setting1', 'advanced_setting2']
)

# Basic settings
my_setting = schema.TextLine(
title=u'My Setting',
description=u'Enter the value for my setting',
required=False
)

# Advanced settings
advanced_setting1 = schema.TextLine(
title=u'Advanced Setting 1',
required=False
)

advanced_setting2 = schema.Bool(
title=u'Advanced Setting 2',
default=False
)
```


## Common schema fields

The following is a list of commonly used schema field types.

`schema.TextLine`
: For single-line text

`schema.Text`
: For multi-line text

`schema.Bool`
: For boolean values

`schema.Int`
: For integer values

`schema.Float`
: For floating-point values

`schema.Choice`
: For selection from a list of values

`schema.Datetime`
: For date and time values

`schema.List`
: For list of values


## Modify control panel fields

When you modify the fields in your control panel settings interface, the changes won't be automatically reflected in existing sites.
You'll need to perform one or more of the following steps.

- Run the appropriate upgrade steps.
- Reinstall your add-on.
- Test with a fresh site installation.


## Troubleshooting

If your control panel doesn't appear or doesn't work as expected:

- Verify that all ZCML is properly registered
- Check for errors in the Plone error log
- Ensure your GenericSetup profiles are correctly installed
- Validate that the interface path in registry.xml matches your actual Python path


## Example file structure

Below is a complete example file structure for a basic add-on with a control panel.

```
mypackage/
├── __init__.py
├── configure.zcml
├── controlpanel/
│ ├── __init__.py
│ ├── configure.zcml
│ └── settings.py
└── profiles/
└── default/
├── controlpanel.xml
├── metadata.xml
└── registry.xml
```

```{seealso}
See the chapter {ref}`training:controlpanel-label` from the Mastering Plone 6 Training.
```
1 change: 1 addition & 0 deletions docs/developer-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ You can help consolidate all of development documentation here, even if it is to
```{toctree}
:maxdepth: 2

create-control-panel
create-a-distribution
standardize-python-project-configuration
```