You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/backend/behaviors.md
+355-4Lines changed: 355 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,12 +11,363 @@ myst:
11
11
12
12
# Behaviors
13
13
14
+
In Plone, behaviors are a way to add reusable functionality to content objects without modifying the objects themselves.
15
+
Behaviors are essentially small chunks of code that can be plugged onto content types to provide new features or capabilities.
16
+
17
+
A Plone behavior could be used to
18
+
19
+
- add a set of form fields (on standard add and edit forms),
20
+
- add logic as part of the adapter,
21
+
- enable a particular event handler,
22
+
- enable one or more views, viewlets, or other UI components,
23
+
- do anything else which may be expressed in code via an adapter or marker interface.
24
+
25
+
Behaviors can be added to content types on an as-needed basis, allowing for a high degree of flexibility and customization.
26
+
27
+
Plone already provides lots of behaviors for the built-in content types.
28
+
29
+
Other behaviors are implemented as add-on products, which can be installed and configured through the Plone control panel.
30
+
Once a behavior has been installed, it can be applied to any content type by selecting it in the {guilabel}`Content Types` control panel.
31
+
This allows items of this content type to gain the additional functionality provided by the behavior.
32
+
33
+
A key feature of behaviors is that they allow encapsulating functionality so that it can be reused for multiple content types without needing to reimplement it.
34
+
Overall, behaviors are an important part of the Plone content management system and allow for powerful customization and extensibility of content objects.
35
+
36
+
37
+
## Built-in behaviors
38
+
39
+
To view a complete list of built-in behaviors, browse to {guilabel}`Content Types` control panel, then click {guilabel}`Page` (or any other content type), then {guilabel}`Behaviors`.
40
+
41
+
| short name | Title | Desription |
42
+
|---|---|---|
43
+
|`plone.allowdiscussion`| Allow discussion | Allow discussion on this item |
44
+
|`plone.basic`| Basic metadata | Adds title and description fields. |
45
+
|`volto.blocks`| Blocks | Enables Volto Blocks support |
|`plone.eventcontact`| Event Contact | Contact extension for Events. |
54
+
|`plone.eventlocation`| Event Location | Location extension for Events. |
55
+
|`plone.eventrecurrence`| Event Recurrence | Recurrence extension for Events. |
56
+
|`plone.excludefromnavigation`| Exclude From navigation | Allow items to be excluded from navigation |
57
+
|`plone.constraintypes`| Folder Addable Constrains | Restrict the content types that can be added to folderish content |
58
+
|`plone.textindexer`| Full-Text Indexing | Enables the enhanced full-text indexing for a content type. If a field in the schema is marked with the `searchable` directive, its content gets added to the `SearchableText` index in the catalog |
59
+
|`volto.head_title`| Head title field | Adds Head title field |
60
+
|`plone.leadimage`| Lead Image | Adds image and image caption fields |
61
+
|`plone.locking`| Locking | Locking support for dexterity |
62
+
|`plone.translatable`| Multilingual Support | Make this content type multilingual aware. Multilingual support must be installed. |
63
+
| `plone.namefromfilename` | Name from file name | Automatically generate short URL name for content based on its primary field file name
64
+
| `plone.namefromtitle` | Name from title | Automatically generate short URL name for content based on its initial title
65
+
|`plone.navigationroot`| Navigation root | Make all items of this type a navigation root |
66
+
|`volto.navtitle`| Navigation title | Navigation title used in sections, menus and doormats |
67
+
|`plone.nextpreviousenabled`| Next previous navigation | Enable next previous navigation for all items of this type |
68
+
|`plone.nextprevioustoggle`| Next previous navigation toggle | Allow items to have next previous navigation enabled |
69
+
|`plone.ownership`| Ownership | Adds creator, contributor, and rights fields. |
70
+
|`volto.preview_image`| Preview Image | Preview image for listings |
71
+
|`volto.preview_image_link`| Preview Image Link | Preview image for listings based on links |
72
+
|`plone.relateditems`| Related items | Adds the ability to assign related items |
|`plone.shortname`| Short name | Gives the ability to rename an item from its edit form. |
75
+
|`plone.tableofcontents`| Table of contents | Adds a table of contents |
76
+
| `plone.thumb_icon` | Thumbs and icon handling | Options to suppress thumbs and/or icons and to override thumb size in listings, tables etc.
77
+
|`plone.versioning`| Versioning | Versioning support with CMFEditions |
78
+
79
+
```{todo}
80
+
For each behavior in the table above, one may view the source code of the checkbox (its `name` attribute) to view its Short Name.
81
+
An issue has been created to better expose these in the user interface.
82
+
[Control panel for Content Type > Behaviors short names not displayed to user Products.CMFPlone#3706](https://github.com/plone/Products.CMFPlone/issues/3706)
83
+
```
84
+
85
+
## Adding or removing a behavior from a content type
86
+
87
+
There are two ways to add or remove a behavior on a content type:
88
+
89
+
- Through the web using the {guilabel}`Content Types` control panel.
90
+
- Using a custom add-on `GenericSetup` profile.
91
+
92
+
93
+
### Through the web
94
+
95
+
1. Go to the {guilabel}`Site Setup` and chose the {guilabel}`Content Types` control panel.
96
+
2. Select the content type to which you want to add or remove a behavior.
97
+
3. Then click on the {guilabel}`Behaviors` tab of the settings of the content type.
98
+
4. A list of all available behaviors appears.
99
+
Select or deselect the checkbox of the behavior you want to add to or remove from the type.
100
+
5. Save the form by clicking on the {guilabel}`Save` button at the bottom of the page.
101
+
102
+
103
+
### Using a `GenericSetup` profile
104
+
105
+
Given you already have a custom add-on with a `profiles/default` directory, and you created a custom behavior named `mybehavior.subtitle`.
106
+
107
+
If you want to enable a behavior on an existing content type, create a new directory `types` under `profiles/default`.
108
+
In the `types` directory, create a file named the same as the content type you want to change.
109
+
In the example here, you want to add a behavior to the built-in `Event` content type.
110
+
Create a file named `Event.xml`.
111
+
It is a {term}`Factory Type Information` (FTI) definition.
112
+
You need to change only the behavior's configuration.
113
+
All other parts can be ignored.
114
+
The file `Event.xml` contains the following.
115
+
116
+
```xml
117
+
<?xml version="1.0"?>
118
+
<object
119
+
i18n:domain="plone"
120
+
meta_type="Dexterity FTI"
121
+
name="Event"
122
+
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
123
+
<propertyname="behaviors"purge="false">
124
+
<elementvalue="myproject.subtitle" />
125
+
</property>
126
+
</object>
127
+
```
128
+
129
+
After you apply the profile (or uninstall and install the custom add-on), the behavior is effective on the `Event` content type.
130
+
131
+
132
+
## Custom behaviors
133
+
134
+
There are two types of behaviors:
135
+
136
+
Schema-only behaviors
137
+
: These behaviors have only a schema with fields.
138
+
139
+
Full behaviors
140
+
: A Python class containing the logic of the behavior, an interface or schema defining the contract of the behavior, and a marker interface applicable to a content type.
141
+
142
+
143
+
### Create a schema-only behavior
144
+
145
+
Given you want to add a field `subtitle` to some existing content types of your custom add-on.
146
+
147
+
You need to create a file `subtitle.py` in your add-on:
148
+
149
+
```python
150
+
from plone.autoform.interfaces import IFormFieldProvider
151
+
from plone.supermodel import model
152
+
from zope import schema
153
+
from zope.interface import provider
154
+
155
+
156
+
@provider(IFormFieldProvider)
157
+
classISubtitleBehavior(model.Schema):
158
+
"""Subtitle behavior."""
159
+
160
+
subtitle = schema.Text(
161
+
title="Subtitle",
162
+
description="A title to be displayed below the title",
163
+
default="",
164
+
required=False,
165
+
)
166
+
```
167
+
168
+
You need to add a ZCML snippet to the `configure.zcml` next to `subtitle.py`:
169
+
170
+
```xml
171
+
<plone:behavior
172
+
name="myproject.subtitle"
173
+
provides=".subtitle.ISubtitleBehavior"
174
+
title="Subtitle"
175
+
/>
176
+
```
177
+
178
+
After a restart of Plone, the behavior can be added to the content type in the {guilabel}`Content Types` control panel.
179
+
The add and edit forms contain a new field `Subtitle`.
180
+
181
+
This field is not displayed in most views.
182
+
To display the entered data in this field, you need to modify the page template by adding the field `context.subtitle`.
183
+
184
+
### Creating a behavior with an adapter and factory
185
+
186
+
Given you want to display a price with different content types.
187
+
The price is stored as the net value on the type as a floating point number.
188
+
For display, you need at several places the value added tax (VAT) and the gross value.
189
+
190
+
You create a schema with the net value for the form and attributes for the calculated values.
191
+
You create an adapter to calculate the VAT and gross values.
192
+
You need a marker interface to distinguish between context and adapter.
193
+
194
+
Add Python code in the file `price.py`:
195
+
196
+
```python
197
+
from plone.autoform.interfaces import IFormFieldProvider
After a restart of Plone, the behavior can be added to the content type in the {guilabel}`Content Types` control panel.
260
+
The add and edit forms contain a new field `Price (net)`.
261
+
262
+
This field is not displayed in most views.
263
+
To display the entered data in this field, you need to modify the page template by adding the `price_net` field as `context.price_net`.
264
+
To access the `price_vat` and `price_gross` fields from a browser view, you need to get the adapter from the context of the view:
265
+
266
+
(behavior-code-example)=
267
+
268
+
```python
269
+
from .price import IPriceBehavior
270
+
271
+
classSomeViewClass:
272
+
273
+
defvat(self):
274
+
price_for_context = IPriceBehavior(context)
275
+
return price_for_context.price_vat
276
+
277
+
defgross(self):
278
+
price_for_context = IPriceBehavior(context)
279
+
return price_for_context.price_gross
280
+
```
281
+
282
+
### Create a behavior with PloneCLI
283
+
284
+
To add a behavior to your add-on, you can use PloneCLI as follows:
285
+
286
+
```shell
287
+
plonecli add behavior
288
+
```
289
+
290
+
This will create the behavior Python file in the `behaviors` folder where you can define your behavior's schema fields, and registers the behavior in the `configure.zcml`.
291
+
292
+
293
+
### Further reading on working with behaviors
14
294
15
295
```{seealso}
16
296
See the chapter {ref}`training:behaviors1-label` from the Mastering Plone 6 Training.
17
297
```
18
298
19
-
```{todo}
20
-
Contribute to this documentation!
21
-
See issue [Backend > Behaviors needs content](https://github.com/plone/documentation/issues/1302).
22
-
```
299
+
300
+
## How behaviors work
301
+
302
+
```{note}
303
+
Skip this section if you do not want to dive deeper into the internals of behaviors.
304
+
You do not *need* to know this, but it may help if you run into problems.
305
+
```
306
+
307
+
In Plone, behaviors can be globally enabled on content types at runtime.
308
+
With add-ons, behaviors can be enabled even on a single content object or for a whole subtree in the content hierarchy.
309
+
310
+
311
+
### Interfaces and adapters
312
+
313
+
To explain interfaces and adapters, let's begin with an analogy using electrical systems.
314
+
315
+
An electrical outlet provides an interface through which electricity passes.
316
+
When you travel to another country, you may need an outlet adapter for the outlet (the interface).
317
+
For example, assume you have a device that has a plug for Schuko outlets, and in Italy there are Type L outlets.
318
+
If we were to represent the behavior of choosing the correct outlet adapter in Plone, you would do the following.
319
+
320
+
- You need an outlet adapter for your Schuko plug.
321
+
1. You look at the outlet and see it is Type L.
322
+
2. You look in your box containing different adapters and choose the correct outlet adapter to use.
323
+
3. You plug that into the wall outlet.
324
+
4. Finally, you can use your Schuko providing device on an Italian Type L outlet.
325
+
- In Python, you would call `getAdapter(context, ISchuko)` (context is here the outlet type), which would then do the following.
326
+
1. Determine the type of interface provided by the `context`.
327
+
As a result, it finds `ITypeL` interface.
328
+
2. Looks in the component registry if there is a class that adapts to `ITypeL`.
329
+
At the same time, it provides the requested `ISchuko` adapter.
330
+
3. Initializes the adapter class with the context, and returns it as the result.
331
+
4. Finally, the `ISchuko` providing adapter can be used on a `ITypeL` providing context.
332
+
333
+
This process of choosing the right adapter based on the information of the context and the requested interface implements the design pattern of an abstract factory.
334
+
335
+
Similarly, using the {ref}`behavior code example <behavior-code-example>` above:
336
+
337
+
- You would call an abstract factory with `getAdapter(context, IPriceBehavior)` to get an adapter, `price_for_context`.
338
+
Although it is an interface, it is more of a shortcut to factory usage.
339
+
- The adapter that is specific to the given content type is assigned to the variable `price_for_context`.
340
+
Now you can use `price_for_context` for whatever you like.
341
+
342
+
When a behavior is enabled for a particular object, it will be possible to adapt that object to the behavior's interface.
343
+
Otherwise, when the behavior is disabled, adaptation will fail or falls back to a more generic adapter, if any is registered.
344
+
345
+
A behavior is at least a combination of an interface (also as a form field provider); metadata such as a name, title, and description; and sometimes an adapter factory with a marker interface.
346
+
When a behavior is enabled, an interface is added to the content object to indicate its presence.
347
+
In other words, the content object now provides the interface.
348
+
349
+
Behaviors without an adapter factory can be used either as a simple marker or to provide additional form fields.
350
+
In this case, adapting a content object with this interface returns the content object itself, because adapting an object that already provides the exact same interface returns the very same object.
351
+
Based on the now-provided interface, specific views can be registered with the content type, or event handlers can be registered to respond to specific actions.
352
+
353
+
In other cases, there is also an adapter factory (usually a Python class), which will be invoked (initialized) to get an appropriate adapter when requested.
354
+
If an adapter factory is used, an explicit marker interface is required.
355
+
356
+
With an adapter factory in place, custom getters and setters for form fields can be implemented, or even new methods.
357
+
For example, calculations or the combination of data can be added.
358
+
359
+
### Registration
360
+
361
+
Behaviors are registered globally using the `<plone.behavior />` {term}`ZCML` directive.
362
+
Internally, this directive registers a named utility that provides `plone.behavior.interfaces.IBehavior`.
363
+
This utility contains combined information about the behavior, such as its name, interface, factory or marker interface, and metadata.
364
+
365
+
```{seealso}
366
+
The [README file of `plone.behavior`](https://github.com/plone/plone.behavior/blob/master/README.rst) explains the concepts and different ways to register a behavior in detail.
367
+
```
368
+
369
+
### Lookup and provide
370
+
371
+
Plone content objects have logic to look up the behaviors' names registered from their types' configuration, the {term}`Factory Type Information` (FTI).
372
+
At runtime, the logic provides the interface (or marker) from the behavior to the object.
373
+
This dynamically provided interface enables the component architecture to react to this new interface by adding additional form fields, bindings events, enabling more specific views, and more.
Copy file name to clipboardExpand all lines: docs/glossary.md
+17Lines changed: 17 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -505,4 +505,21 @@ content rule
505
505
trigger
506
506
A trigger is an event in Plone that causes the execution of defined actions.
507
507
Example triggers include object modified, user logged in, and workflow state changed.
508
+
509
+
FTI
510
+
Factory Type Information
511
+
Factory type information (FTI) is responsible for content creation in the portal.
512
+
FTI is responsible for the following:
513
+
514
+
- Which function is called when new content type is added.
515
+
- Icons available for content types.
516
+
- Creation views for content types.
517
+
- Permission and security.
518
+
- Whether discussion is enabled.
519
+
- Providing the `factory_type_information` dictionary.
520
+
This is used elsewhere in the code (often in `__init__.py` of a product) to set the initial values for a ZODB Factory Type Information object (an object in the `portal_types` tool).
521
+
522
+
```{seealso}
523
+
[`FactoryTypeInformation` class source code](https://github.com/zopefoundation/Products.CMFCore/blob/361a30e0c72a15a21f88433b8d5fc49331f36728/src/Products/CMFCore/TypesTool.py#L431)
0 commit comments