Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 12 additions & 9 deletions cms/models/pluginmodel.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import annotations

import os
import re
import warnings
from datetime import date
from functools import cache
from functools import cache, cached_property

from django.core.exceptions import ObjectDoesNotExist
from django.db import connection, connections, models, router
Expand Down Expand Up @@ -202,21 +203,23 @@ def __repr__(self):
return display

def get_plugin_name(self):
from cms.plugin_pool import plugin_pool

return plugin_pool.get_plugin(self.plugin_type).name
return self.plugin_class.name

def get_short_description(self):
instance = self.get_plugin_instance()[0]
if instance is not None:
return force_str(instance)
return _("<Empty>")

def get_plugin_class(self):
@cached_property
def plugin_class(self):
from cms.plugin_pool import plugin_pool

return plugin_pool.get_plugin(self.plugin_type)

def get_plugin_class(self):
return self.plugin_class

def get_plugin_class_instance(self, admin=None):
plugin_class = self.get_plugin_class()
# needed so we have the same signature as the original ModelAdmin
Expand Down Expand Up @@ -264,7 +267,8 @@ def get_bound_plugin(self):
return self._inst

def get_plugin_info(self, children=None, parents=None):
plugin_class = self.get_plugin_class()
plugin_class = self.plugin_class

return {
'type': 'plugin',
'position': self.position,
Expand Down Expand Up @@ -449,22 +453,21 @@ def get_action_urls(self, js_compat=True):
# TODO: Remove this condition
# once the javascript files have been refactored
# to use the new naming schema (ending in _url).
data = {
return {
'edit_plugin': admin_reverse('cms_placeholder_edit_plugin', args=(self.pk,)),
'add_plugin': admin_reverse('cms_placeholder_add_plugin'),
'delete_plugin': admin_reverse('cms_placeholder_delete_plugin', args=(self.pk,)),
'move_plugin': admin_reverse('cms_placeholder_move_plugin'),
'copy_plugin': admin_reverse('cms_placeholder_copy_plugins'),
}
else:
data = {
return {
'edit_url': admin_reverse('cms_placeholder_edit_plugin', args=(self.pk,)),
'add_url': admin_reverse('cms_placeholder_add_plugin'),
'delete_url': admin_reverse('cms_placeholder_delete_plugin', args=(self.pk,)),
'move_url': admin_reverse('cms_placeholder_move_plugin'),
'copy_url': admin_reverse('cms_placeholder_copy_plugins'),
}
return data


def get_plugin_media_path(instance, filename):
Expand Down
30 changes: 13 additions & 17 deletions cms/plugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ def __new__(cls, name, bases, attrs):
return new_plugin


T = TypeVar("T", bound=Callable)


def template_slot_caching(method: T) -> T:
def template_slot_caching(method: Callable) -> Callable:
"""
Decorator that enables global caching for methods based on placeholder slots and templates.

Expand All @@ -109,12 +106,8 @@ def get_child_class_overrides(cls, slot: str, page: Optional[Page] = None, insta
pass
"""

@wraps(method)
def wrapper(*args, **kwargs):
return method(*args, **kwargs)

wrapper._template_slot_caching = True
return cast(T, wrapper)
method._template_slot_caching = True
return method


class CMSPluginBase(admin.ModelAdmin, metaclass=CMSPluginBaseMetaclass):
Expand Down Expand Up @@ -736,7 +729,7 @@ def get_child_plugin_candidates(cls, slot: str, page: Optional[Page] = None) ->
# where only text-only plugins are allowed.
from cms.plugin_pool import plugin_pool

return sorted(plugin_pool.get_all_plugins(slot, page, root_plugin=False), key=attrgetter("module", "name"))
return plugin_pool.get_all_plugins(slot, page, root_plugin=False)

@classmethod
@template_slot_caching
Expand All @@ -753,8 +746,12 @@ def get_child_classes(
installed_plugins = cls.get_child_plugin_candidates(slot, page)

if child_classes:
# Override skips check if current class is valid parent of child classes
return [plugin.__name__ for plugin in installed_plugins if plugin.__name__ in child_classes]

if only_uncached:
installed_plugins = [plugin for plugin in installed_plugins if not plugin.cache_parent_classes]

child_classes = []
plugin_type = cls.__name__

Expand All @@ -767,12 +764,11 @@ def get_child_classes(
# If there are no restrictions then the plugin
# is a valid child class.
for plugin_class in installed_plugins:
if not only_uncached or not plugin_class.cache_parent_classes:
allowed_parents = plugin_class.get_parent_classes(slot, page, instance)
if not allowed_parents or plugin_type in allowed_parents:
# Plugin has no parent restrictions or
# Current plugin (self) is a configured parent
child_classes.append(plugin_class.__name__)
allowed_parents = plugin_class.get_parent_classes(slot, page, instance)
if not allowed_parents or plugin_type in allowed_parents:
# Plugin has no parent restrictions or
# Current plugin (self) is a configured parent
child_classes.append(plugin_class.__name__)

return child_classes

Expand Down
16 changes: 9 additions & 7 deletions cms/plugin_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ def __init__(self):
self.plugins = {}
self.discovered = False
self.global_restrictions_cache = {
# Initialize the global restrictions cache for each CMS_PLACEHOLDER_CONF
# granularity that contains "parent_classes" or "child_classes" overwrites
None: {},
**{key: {} for key in get_cms_setting("PLACEHOLDER_CONF").keys()},
**{key: {} for key, value in get_cms_setting("PLACEHOLDER_CONF").items()
if "parent_classes" in value or "child_classes" in value},
}
self.global_template_restrictions = any(".htm" in (key or "") for key in self.global_restrictions_cache)

Expand All @@ -41,6 +44,8 @@ def discover_plugins(self):

autodiscover_modules("cms_plugins")
self.discovered = True
# Sort plugins by their module and name
self.plugins = dict(sorted(self.plugins.items(), key=lambda key: (key[1].module, key[1].name)))

def clear(self):
self.discovered = False
Expand Down Expand Up @@ -135,7 +140,6 @@ def get_all_plugins(
):
from cms.utils.placeholder import get_placeholder_conf

self.discover_plugins()
plugins = self.plugins.values()
template = (
lazy(page.get_template, str)() if page else None
Expand Down Expand Up @@ -185,7 +189,6 @@ def get_plugin(self, name) -> type[CMSPluginBase]:
"""
Retrieve a plugin from the cache.
"""
self.discover_plugins()
return self.plugins[name]

def get_patterns(self) -> list[URLResolver]:
Expand All @@ -210,7 +213,6 @@ def get_patterns(self) -> list[URLResolver]:
return url_patterns

def get_system_plugins(self) -> list[str]:
self.discover_plugins()
return [plugin.__name__ for plugin in self.plugins.values() if plugin.system]

@cached_property
Expand Down Expand Up @@ -240,7 +242,7 @@ def get_restrictions_cache(self, request_cache: dict, instance: CMSPluginBase, p
be recalculated for each request.

Args:
request_cache (dict): The current request cache.
request_cache (dict): The current request cache (only filled is non globally cacheable).
instance (CMSPluginBase): The plugin instance for which to retrieve the restrictions cache.
page (Optional[Page]): The page associated with the plugin instance, if any.

Expand All @@ -256,11 +258,11 @@ def get_restrictions_cache(self, request_cache: dict, instance: CMSPluginBase, p
else:
template = ""

if f"{template} {slot}" in self.global_restrictions_cache:
if template and f"{template} {slot}" in self.global_restrictions_cache:
return self.global_restrictions_cache[f"{template} {slot}"]
if template and template in self.global_restrictions_cache:
return self.global_restrictions_cache[template]
if slot in self.global_restrictions_cache:
if slot and slot in self.global_restrictions_cache:
return self.global_restrictions_cache[slot]
return self.global_restrictions_cache[None]

Expand Down
49 changes: 22 additions & 27 deletions cms/plugin_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,10 @@
logger = logging.getLogger(__name__)


def _unpack_plugins(parent_plugin: CMSPlugin) -> list[CMSPlugin]:
found_plugins = []

def _unpack_plugins(parent_plugin: CMSPlugin) -> Generator[CMSPlugin, None, None]:
yield parent_plugin
for plugin in parent_plugin.child_plugin_instances or []:
found_plugins.append(plugin)

if plugin.child_plugin_instances:
found_plugins.extend(_unpack_plugins(plugin))
return found_plugins
yield from _unpack_plugins(plugin)


class RenderedPlaceholder:
Expand Down Expand Up @@ -95,13 +90,13 @@ class BaseRenderer:

def __init__(self, request: HttpRequest):
self.request = request
self._cached_templates = {}
self._cached_plugin_classes = {}
self._placeholders_content_cache = {}
self._placeholders_by_page_cache = {}
self._rendered_placeholders = OrderedDict()
self._rendered_static_placeholders = OrderedDict()
self._rendered_plugins_by_placeholder = {}
self._plugins_with_perms = None

@cached_property
def current_page(self) -> Page:
Expand Down Expand Up @@ -129,20 +124,25 @@ def plugin_pool(self) -> PluginPool:
def request_language(self) -> str:
return get_language_from_request(self.request)

def get_plugins_with_perms(self) -> list[CMSPlugin]:
if self._plugins_with_perms is None:
registered_plugins = self.plugin_pool.registered_plugins
can_add_plugin = partial(
has_plugin_permission, user=self.request.user, permission_type="add"
)
self._plugins_with_perms = [
plugin
for plugin in registered_plugins
if can_add_plugin(plugin_type=plugin.value)
]

return self._plugins_with_perms

def get_placeholder_plugin_menu(
self, placeholder: Placeholder, page: Optional[Page] = None
):
registered_plugins = self.plugin_pool.registered_plugins
can_add_plugin = partial(
has_plugin_permission, user=self.request.user, permission_type="add"
)
plugins = [
plugin
for plugin in registered_plugins
if can_add_plugin(plugin_type=plugin.value)
]
plugin_menu = get_toolbar_plugin_struct(
plugins=plugins,
plugins=self.get_plugins_with_perms(),
slot=placeholder.slot,
page=page,
)
Expand All @@ -166,7 +166,8 @@ def get_plugin_toolbar_js(self, plugin: CMSPlugin, page: Optional[Page] = None):
)
child_classes, parent_classes = get_plugin_restrictions(
plugin=plugin,
restrictions_cache=placeholder_cache,
page=page,
restrictions_cache=placeholder_cache, # Store non-global plugin-restriction in placeholder_cache
)
content = get_plugin_toolbar_js(
plugin,
Expand Down Expand Up @@ -774,13 +775,7 @@ def get_plugins_to_render(self, *args, **kwargs):
plugins = super().get_plugins_to_render(*args, **kwargs)

for plugin in plugins:
yield plugin

if not plugin.child_plugin_instances:
continue

for plugin in _unpack_plugins(plugin):
yield plugin
yield from _unpack_plugins(plugin)

def render_placeholder(self, placeholder, language, page=None):
rendered_plugins = self.render_plugins(
Expand Down
6 changes: 3 additions & 3 deletions cms/static/cms/js/modules/cms.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -1005,15 +1005,15 @@ class Modal {
true
);
} else {
// hello ckeditor
Helpers.removeEventListener('modal-close.text-plugin');
that.close();
// Serve the data bridge:
// We have a special case here cause the CMS namespace
// can be either inside the current window or the parent
const dataBridge = body[0].querySelector('script#data-bridge');

if (dataBridge) {
// hello ckeditor
Helpers.removeEventListener('modal-close.text-plugin');
that.close();
// the dataBridge is used to access plugin information from different resources
// Do NOT move this!!!
try {
Expand Down
5 changes: 1 addition & 4 deletions cms/templatetags/cms_js_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ def bool(value):
def render_cms_structure_js(context, renderer, obj):
markup_bits = []
obj_placeholders_by_slot = rescan_placeholders_for_obj(obj)
declared_placeholders = get_declared_placeholders_for_obj(obj)
try:
lang = context["request"].toolbar.request_language
except AttributeError:
lang = None

for placeholder_node in declared_placeholders:
obj_placeholder = obj_placeholders_by_slot.get(placeholder_node.slot)

for obj_placeholder in obj_placeholders_by_slot.values():
if obj_placeholder:
placeholder_js = renderer.render_placeholder(obj_placeholder, language=lang, page=obj)
markup_bits.append(placeholder_js)
Expand Down
13 changes: 13 additions & 0 deletions cms/test_utils/util/context_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from tempfile import _exists, mkdtemp, template

from django.contrib.auth import get_user_model
from django.test.utils import override_settings
from django.utils.translation import activate, get_language

from cms.apphook_pool import apphook_pool
Expand Down Expand Up @@ -173,3 +174,15 @@ def __init__(self):
def __call__(self, *args, **kwargs):
self.call_count += 1
self.calls.append((args, kwargs))


@contextmanager
def override_placeholder_conf(CMS_PLACEHOLDER_CONF):
from cms.utils.placeholder import _clear_placeholder_conf_cache

# Call _get_placeholder_settings after changing the setting
with override_settings(CMS_PLACEHOLDER_CONF=CMS_PLACEHOLDER_CONF):
_clear_placeholder_conf_cache() # Clear cache if needed
yield
# Call _get_placeholder_settings after resetting the setting
_clear_placeholder_conf_cache() # Clear cache if needed
5 changes: 3 additions & 2 deletions cms/tests/test_page_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from cms.test_utils.util.context_managers import (
LanguageOverride,
UserLoginContext,
override_placeholder_conf,
)
from cms.toolbar.utils import get_object_edit_url
from cms.utils.compat import DJANGO_4_2, DJANGO_5_1
Expand Down Expand Up @@ -1501,7 +1502,7 @@ def test_global_limit_on_plugin_move(self):
plugin_2 = add_plugin(**data)
plugin_3 = add_plugin(**data)
with UserLoginContext(self, superuser):
with self.settings(CMS_PLACEHOLDER_CONF=self.placeholderconf):
with override_placeholder_conf(self.placeholderconf):
data = self._get_move_data(plugin_1, position=1, placeholder=target_placeholder)
endpoint = self.get_move_plugin_uri(plugin_1)
response = self.client.post(endpoint, data) # first
Expand Down Expand Up @@ -1529,7 +1530,7 @@ def test_type_limit_on_plugin_move(self):
plugin_1 = add_plugin(**data)
plugin_2 = add_plugin(**data)
with UserLoginContext(self, superuser):
with self.settings(CMS_PLACEHOLDER_CONF=self.placeholderconf):
with override_placeholder_conf(self.placeholderconf):
data = self._get_move_data(plugin_1, position=1, placeholder=target_placeholder)
endpoint = self.get_move_plugin_uri(plugin_1)
response = self.client.post(endpoint, data) # first
Expand Down
Loading