CMS_PLACEHOLDER_CONF Behavior in Django CMS 5.x
Recently updated on
The `CMS_PLACEHOLDER_CONF` setting is a dictionary whose keys correspond to the names of placeholders in your templates. Each corresponding value typically includes a "plugins" list, which specifies which plugins can be added to that placeholder. Prior to Django CMS 5.0, if one of those plugins was a container plugin, the container class's own `child_classes` attribute (in the plugin's "cms_plugins.py") determined which other plugins could be added beneath it. In other words, `CMS_PLACEHOLDER_CONF` defined which plugins were available at the root level of a placeholder, while the plugin classes themselves controlled the structure of the plugin tree below that point. A container plugin therefore defined its own rules about what other plugins could be added as its children and/or which plugins could serve as its parents. It was not necessary to include those child or parent plugins in `CMS_PLACEHOLDER_CONF`.
But as of Django CMS 5.0, the interpretation of `CMS_PLACEHOLDER_CONF` has changed. Now, the "plugins" list no longer defines only the plugin types that may be used at the root level of the placeholder. Instead it functions as a canonical list of plugin types that are permitted to be used *anywhere* in the placeholder, including as descendants of other plugins.
As a consequence, declaring a container plugin in the plugins list is no longer sufficient to make its class-defined child plugins available beneath it. Those child plugins must now also be included in the placeholder's "plugins" list. But (if the plugin's own attributes don't forbid it) this also makes those child plugins available at the placeholder's root level, which may not be desirable. In that case, a `parent_classes` restriction must now be added to the placeholder's entry in `CMS_PLACEHOLDER_CONF`. These changes must be applied for every placeholder in the configuration. Thus, migrating a project with many placeholders from CMS 4.x to 5.x may result in a `CMS_PLACEHOLDER_CONF` that is considerably larger than before.
As an example, suppose we are using the following plugin classes as container plugins:
```
class FooContainerPlugin(CMSPluginBase):
...
allow_children = True
child_classes = ["FooPlugin"]
class BarContainerPlugin(CMSPluginBase):
...
Let us further assume the existence of plugins meant to go inside the containers (e.g., `FooPlugin`). Note that `FooContainerPlugin` defines its own `child_classes` but `BarContainerPlugin` does not.
If we had the following configuration for "my_placeholder" under CMS 4.x,
CMS_PLACEHOLDER_CONF = {
"my_placeholder": {
"plugins": ["FooContainerPlugin", "BarContainerPlugin"],
"child_classes": {
"BarContainerPlugin": ["BarPlugin1", "BarPlugin2", "BarPlugin3","BarPlugin4", "BarPlugin5"],
}
},
...
}
```
then `FooPlugin` could be added to `FooContainerPlugin` despite not being explicitly listed in `CMS_PLACEHOLDER_CONF`. However, it would *only* be available as a child of `FooContainerPlugin`. It would not be available to be added at the root level of the placeholder. Similarly, the various child plugins under `BarContainerPlugin` would only be available to be added as children of `BarContainerPlugin`.
```
CMS_PLACEHOLDER_CONF = {
"my_placeholder": {
"plugins": [
"FooContainerPlugin", "FooPlugin", "BarContainerPlugin", "BarPlugin1", "BarPlugin2", "BarPlugin3","BarPlugin4", "BarPlugin5"
],
"child_classes": {
"BarContainerPlugin": ["BarPlugin1", "BarPlugin2", "BarPlugin3","BarPlugin4", "BarPlugin5"]
}
"parent_classes": {
"FooPlugin": ["FooContainerPlugin"],
"BarPlugin1": ["BarContainerPlugin"],
"BarPlugin2": ["BarContainerPlugin"],
"BarPlugin3": ["BarContainerPlugin"],
"BarPlugin4": ["BarContainerPlugin"],
"BarPlugin5": ["BarContainerPlugin"]
}
},
...
}
```
```
CMS_PLACEHOLDER_CONF = {
"my_placeholder": {
"plugins": ["FooContainerPlugin", "BarContainerPlugin"],
"child_classes": {
"FooContainerPlugin": None,
"BarContainerPlugin": ["BarPlugin1", "BarPlugin2"]
}
},
}
```
```
CMS_PLACEHOLDER_CONF = {
"my_placeholder": expand_child_classes({
"plugins": ["FooContainerPlugin", "BarContainerPlugin"],
"child_classes": {
"FooContainerPlugin": ["FooPlugin"],
"BarContainerPlugin": ["BarPlugin1", "BarPlugin2", "BarPlugin3","BarPlugin4", "BarPlugin5"],
}
}),
...
}
```
```
from django.apps import AppConfig
class CMSPatchConfig(AppConfig):
name = "my_project.apps.cms_patch"
def ready(self):
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
@classmethod
def legacy_get_child_plugin_candidates(cls, slot, page=None):
return plugin_pool.registered_plugins
CMSPluginBase.get_child_plugin_candidates = legacy_get_child_plugin_candidates
```
of value from this post, would you please take a sec and share it? It really does help.