Skip to content

Commit d65ed70

Browse files
committed
Refactor components
1 parent 3793655 commit d65ed70

File tree

18 files changed

+213
-220
lines changed

18 files changed

+213
-220
lines changed

djangocms_frontend/apps.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ class DjangocmsFrontendConfig(apps.AppConfig):
77
verbose_name = "django CMS Frontend"
88

99
def ready(self):
10-
from .component_pool import setup
10+
from . import plugin_tag
1111

1212
register(check_settings)
13-
setup()
13+
plugin_tag.setup()
1414

1515

1616
def check_settings(*args, **kwargs):

djangocms_frontend/cms_plugins.py

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,23 @@
1-
from cms.constants import SLUG_REGEXP
2-
from cms.plugin_base import CMSPluginBase
3-
from django.utils.encoding import force_str
1+
from cms.plugin_pool import plugin_pool
42

5-
from djangocms_frontend.helpers import get_related
3+
from .component_pool import components
4+
from .ui_plugin_base import CMSUIPluginBase
65

7-
if hasattr(CMSPluginBase, "edit_field"):
8-
# FrontendEditable functionality already implemented in core?
9-
FrontendEditableAdminMixin = object
10-
else:
11-
# If not use our own version of the plugin-enabled mixin
12-
from .helpers import FrontendEditableAdminMixin
136

7+
class CMSUIPlugin(CMSUIPluginBase):
8+
pass
149

15-
class CMSUIPlugin(FrontendEditableAdminMixin, CMSPluginBase):
16-
render_template = "djangocms_frontend/html_container.html"
17-
change_form_template = "djangocms_frontend/admin/base.html"
1810

19-
def __str__(self):
20-
return force_str(super().__str__())
11+
# Loop through the values in the components' registry
12+
for _, plugin, slot_plugins in components._registry.values():
13+
# Add the plugin to the global namespace
14+
globals()[plugin.__name__] = plugin
15+
# Register the plugin with the plugin pool
16+
plugin_pool.register_plugin(plugin)
2117

22-
def render(self, context, instance, placeholder):
23-
for key, value in instance.config.items():
24-
if isinstance(value, dict) and set(value.keys()) == {"pk", "model"}:
25-
if key not in instance.__dir__(): # hasattr would return the value in the config dict
26-
setattr(instance.__class__, key, get_related(key))
27-
return super().render(context, instance, placeholder)
28-
29-
def get_plugin_urls(self):
30-
from django.urls import re_path
31-
32-
info = f"{self.model._meta.app_label}_{self.model._meta.model_name}"
33-
34-
def pat(regex, fn):
35-
return re_path(regex, fn, name=f"{info}_{fn.__name__}")
36-
37-
return [
38-
pat(r'edit-field/(%s)/([a-z\-]+)/$' % SLUG_REGEXP, self.edit_field),
39-
]
40-
41-
def _get_object_for_single_field(self, object_id, language):
42-
from .models import FrontendUIItem
43-
44-
return FrontendUIItem.objects.get(pk=object_id)
18+
# Loop through the slot plugins associated with the current plugin
19+
for slot_plugin in slot_plugins:
20+
# Add the slot plugin to the global namespace
21+
globals()[slot_plugin.__name__] = slot_plugin
22+
# Register the slot plugin with the plugin pool
23+
plugin_pool.register_plugin(slot_plugin)

djangocms_frontend/contrib/component/components.py renamed to djangocms_frontend/component_base.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from django.utils.translation import gettext_lazy as _
1010
from entangled.forms import EntangledModelForm
1111

12+
from .ui_plugin_base import CMSUIPluginBase
13+
1214

1315
def _get_mixin_classes(mixins: list, suffix: str = "") -> list[type]:
1416
"""Find and import mixin classes from a list of mixin strings"""
@@ -112,8 +114,6 @@ def plugin_model_factory(cls) -> type:
112114
@classmethod
113115
def plugin_factory(cls) -> type:
114116
if cls._plugin is None:
115-
from djangocms_frontend.cms_plugins import CMSUIPlugin
116-
117117
mixins = getattr(cls._component_meta, "mixins", [])
118118
slots = cls.get_slot_plugins()
119119
mixins = _get_mixin_classes(mixins)
@@ -123,16 +123,18 @@ def plugin_factory(cls) -> type:
123123
(
124124
*mixins,
125125
*cls._plugin_mixins,
126-
CMSUIPlugin,
126+
CMSUIPluginBase,
127127
),
128128
{
129129
"name": getattr(cls._component_meta, "name", cls.__name__),
130-
"module": getattr(cls._component_meta, "module", _("Component")),
130+
"module": getattr(cls._component_meta, "module", _("Components")),
131131
"model": cls.plugin_model_factory(),
132132
"form": cls.admin_form_factory(),
133133
"allow_children": getattr(cls._component_meta, "allow_children", False) or slots,
134+
"require_parent": getattr(cls._component_meta, "require_parent", False),
134135
"child_classes": getattr(cls._component_meta, "child_classes", []) + list(slots.keys()),
135-
"render_template": getattr(cls._component_meta, "render_template", CMSUIPlugin.render_template),
136+
"parent_classes": getattr(cls._component_meta, "parent_classes", []),
137+
"render_template": getattr(cls._component_meta, "render_template", CMSUIPluginBase.render_template),
136138
"fieldsets": getattr(cls, "fieldsets", cls._generate_fieldset()),
137139
"change_form_template": "djangocms_frontend/admin/base.html",
138140
"slots": slots,
@@ -182,9 +184,7 @@ def get_registration(cls) -> tuple[type, type, list[type]]:
182184
@classmethod
183185
@property
184186
def _component_meta(cls) -> typing.Optional[type]:
185-
if hasattr(cls, "Meta"):
186-
return cls.Meta
187-
return None
187+
return getattr(cls, "Meta", None)
188188

189189
@classmethod
190190
def _generate_fieldset(cls) -> list[tuple[typing.Optional[str], dict]]:
@@ -194,20 +194,9 @@ def get_short_description(self) -> str:
194194
return self.config.get("title", "")
195195

196196
def save_model(self, request, obj, form: forms.Form, change: bool) -> None:
197-
"""Auto-createas slot plugins upon creation of component plugin instance"""
198-
from djangocms_frontend.cms_plugins import CMSUIPlugin
197+
"""Auto-creates slot plugins upon creation of component plugin instance"""
199198

200-
super(CMSUIPlugin, self).save_model(request, obj, form, change)
199+
super(CMSUIPluginBase, self).save_model(request, obj, form, change)
201200
if not change:
202201
for slot in self.slots.keys():
203202
add_plugin(obj.placeholder, slot, obj.language, target=obj)
204-
205-
206-
class ComponentLinkMixin:
207-
from djangocms_frontend.contrib.link.cms_plugins import LinkPluginMixin
208-
from djangocms_frontend.contrib.link.forms import AbstractLinkForm
209-
from djangocms_frontend.contrib.link.helpers import GetLinkMixin
210-
211-
_base_form = AbstractLinkForm
212-
_model_mixins = [GetLinkMixin]
213-
_plugin_mixins = [LinkPluginMixin]
Lines changed: 16 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,25 @@
1-
import copy
2-
import importlib
31
import warnings
42

5-
from cms.plugin_pool import plugin_pool
6-
from cms.templatetags.cms_tags import render_plugin
7-
from django.conf import settings
8-
from django.contrib.admin.sites import site as admin_site
9-
from django.template import engines
10-
from django.template.library import SimpleNode
11-
from django.template.loader import get_template
3+
from django.utils.module_loading import autodiscover_modules
124

13-
django_engine = engines["django"]
145

15-
plugin_tag_pool = {}
6+
class Components:
7+
_registry: dict = {}
8+
_discovered: bool = False
169

10+
def register(self, component):
11+
if component.__name__ in self._registry:
12+
warnings.warn(f"Component {component.__name__} already registered", stacklevel=2)
13+
return component
14+
self._registry[component.__name__] = component.get_registration()
15+
return component
1716

18-
IGNORED_FIELDS = (
19-
"id",
20-
"cmsplugin_ptr",
21-
"language",
22-
"plugin_type",
23-
"position",
24-
"creation_date",
25-
"ui_item",
26-
)
17+
def __getitem__(self, item):
18+
return self._registry[item]
2719

28-
allowed_plugin_types = tuple(
29-
getattr(importlib.import_module(cls.rsplit(".", 1)[0]), cls.rsplit(".", 1)[-1]) if isinstance(cls, str) else cls
30-
for cls in getattr(settings, "CMS_COMPONENT_PLUGINS", [])
31-
)
3220

21+
components = Components()
3322

34-
def _get_plugindefaults(instance):
35-
defaults = {
36-
field.name: getattr(instance, field.name)
37-
for field in instance._meta.fields
38-
if field.name not in IGNORED_FIELDS and bool(getattr(instance, field.name))
39-
}
40-
defaults["plugin_type"] = instance.__class__.__name__
41-
return defaults
42-
43-
44-
class _DummyUser:
45-
is_superuser = True
46-
is_staff = True
47-
48-
49-
class _DummyRequest:
50-
user = _DummyUser()
51-
52-
53-
def render_dummy_plugin(context, dummy_plugin):
54-
return dummy_plugin.nodelist.render(context)
55-
56-
57-
def patch_template(template):
58-
"""Patches the template to use the dummy plugin renderer instead of the real one."""
59-
copied_template = copy.deepcopy(template)
60-
patch = False
61-
for node in copied_template.template.nodelist.get_nodes_by_type(SimpleNode):
62-
if node.func == render_plugin:
63-
patch = True
64-
node.func = render_dummy_plugin
65-
return copied_template if patch else template
66-
67-
68-
def setup():
69-
global plugin_tag_pool
70-
71-
for plugin in plugin_pool.get_all_plugins():
72-
if not issubclass(plugin, allowed_plugin_types):
73-
continue
74-
tag_name = plugin.__name__.lower()
75-
if tag_name.endswith("plugin"):
76-
tag_name = tag_name[:-6]
77-
try:
78-
instance = plugin.model() # Create instance with defaults
79-
plugin_admin = plugin(admin_site=admin_site)
80-
if hasattr(instance, "initialize_from_form"):
81-
instance.initialize_from_form(plugin.form)
82-
if tag_name not in plugin_tag_pool:
83-
template = get_template(plugin_admin._get_render_template({"request": None}, instance, None))
84-
plugin_tag_pool[tag_name] = {
85-
"defaults": {
86-
**_get_plugindefaults(instance),
87-
**dict(plugin_type=plugin.__name__),
88-
},
89-
"template": patch_template(template),
90-
"class": plugin,
91-
}
92-
else: # pragma: no cover
93-
warnings.warn(
94-
f"Duplicate candidates for {{% plugin \"{tag_name}\" %}} found. "
95-
f"Only registered {plugin_tag_pool[tag_name]['class'].__name__}.", stacklevel=1)
96-
except Exception as exc: # pragma: no cover
97-
warnings.warn(f"{plugin.__name__}: \n{str(exc)}", stacklevel=1)
23+
if not components._discovered:
24+
autodiscover_modules("cms_components", register_to=components)
25+
components._discovered = True

djangocms_frontend/contrib/component/__init__.py

Whitespace-only changes.

djangocms_frontend/contrib/component/cms_plugins.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

djangocms_frontend/contrib/component/registry.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

djangocms_frontend/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,5 +256,5 @@ def edit_field(self, request, object_id, language):
256256
obj.placeholder.mark_as_dirty(obj.language, clear_cache=False)
257257
# Update the structure board by populating the data bridge
258258
return self.render_close_frame(request, obj)
259-
render(request, 'admin/cms/page/plugin/confirm_form.html', context)
259+
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
260260
return render(request, 'admin/cms/page/plugin/change_form.html', context)

0 commit comments

Comments
 (0)