Skip to content

Commit

Permalink
Avoid downcasting of text-enabled child plugins if plugins are alread…
Browse files Browse the repository at this point in the history
…y downcasted
  • Loading branch information
fsbraun committed Jan 30, 2025
1 parent 223150b commit e2a9024
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 22 deletions.
8 changes: 5 additions & 3 deletions djangocms_text/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def get_form_class(self, request, plugins, plugin):
rendered_text = plugin_tags_to_admin_html(
text=instance.body,
context=context,
child_plugin_instances=instance.child_plugin_instances,
)
else:
rendered_text = None
Expand Down Expand Up @@ -606,7 +607,8 @@ def get_child_plugin_candidates(cls, slot, page):
]
return text_enabled_plugins

def render_plugin_icon(self, plugin):
@staticmethod
def render_plugin_icon(plugin):
icon = getattr(plugin, "text_icon", None)
if icon is None:
return
Expand Down Expand Up @@ -683,7 +685,7 @@ def render(self, context, instance, placeholder):

context.update(
{
"body": plugin_tags_to_admin_html(body, context),
"body": plugin_tags_to_admin_html(body, context, child_plugin_instances=instance.child_plugin_instances),
"placeholder": placeholder,
"object": instance,
"editor_settings": editor_settings,
Expand All @@ -696,7 +698,7 @@ def render(self, context, instance, placeholder):
body = render_dynamic_attributes(instance.body, admin_objects=False, remove_attr=True)
context.update(
{
"body": plugin_tags_to_user_html(body, context),
"body": plugin_tags_to_user_html(body, context, child_plugin_instances=instance.child_plugin_instances),
"placeholder": placeholder,
"object": instance,
}
Expand Down
32 changes: 19 additions & 13 deletions djangocms_text/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import re
from collections import OrderedDict
from functools import WRAPPER_ASSIGNMENTS, wraps
from typing import Optional

from django.template.defaultfilters import force_escape
from django.template import Context
from django.template.loader import render_to_string

from cms import __version__
from cms.models import CMSPlugin
from cms.utils.urlutils import admin_reverse

from classytags.utils import flatten_context
Expand All @@ -24,11 +27,11 @@
cms_placeholder_add_plugin = "cms_page_add_plugin"


def _render_cms_plugin(plugin, context):
def _render_cms_plugin(plugin: CMSPlugin, context):
context = flatten_context(context)
context["plugin"] = plugin

# This my fellow ckeditor enthusiasts is a hack..
# This my fellow enthusiasts is a hack..

# If I let djangoCMS render the plugin using {% render_plugin %}
# it will wrap the output in the toolbar markup which we don't want.
Expand All @@ -47,7 +50,7 @@ def _render_cms_plugin(plugin, context):
return response


def random_comment_exempt(view_func):
def random_comment_exempt(view_func: callable) -> callable:
# Borrowed from
# https://github.com/lpomfrey/django-debreach/blob/f778d77ffc417/debreach/decorators.py#L21
# This is a no-op if django-debreach is not installed
Expand All @@ -59,7 +62,7 @@ def wrapped_view(*args, **kwargs):
return wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)(wrapped_view)


def plugin_to_tag(obj, content="", admin=False):
def plugin_to_tag(obj: CMSPlugin, content: str = "", admin:bool = False):
plugin_attrs = OrderedDict(
id=obj.pk,
icon_alt=force_escape(obj.get_instance_icon_alt()),
Expand Down Expand Up @@ -92,13 +95,16 @@ def _find_plugins():
return [int(_id) for _id in _find_plugins()]


def _plugin_tags_to_html(text, output_func):
def _plugin_tags_to_html(text: str, output_func: callable, child_plugin_instances: Optional[list[CMSPlugin]]) -> str:
"""
Convert plugin object 'tags' into the form for public site.
context is the template context to use, placeholder is the placeholder name
"""
plugins_by_id = get_plugins_from_text(text)
if child_plugin_instances is not None:
plugins_by_id = {plugin.pk: plugin for plugin in child_plugin_instances}
else:
plugins_by_id = get_plugins_from_text(text)

def _render_tag(m):
try:
Expand All @@ -115,29 +121,29 @@ def _render_tag(m):
return OBJ_ADMIN_RE.sub(_render_tag, text)


def plugin_tags_to_user_html(text, context):
def plugin_tags_to_user_html(text: str, context: Context, child_plugin_instances: list[CMSPlugin]) -> str:
def _render_plugin(obj, match):
return _render_cms_plugin(obj, context)

return _plugin_tags_to_html(text, output_func=_render_plugin)
return _plugin_tags_to_html(text, output_func=_render_plugin, child_plugin_instances=child_plugin_instances)


def plugin_tags_to_admin_html(text, context):
def plugin_tags_to_admin_html(text: str, context: Context, child_plugin_instances: list[CMSPlugin]) -> str:
def _render_plugin(obj, match):
plugin_content = _render_cms_plugin(obj, context)
return plugin_to_tag(obj, content=plugin_content, admin=True)

return _plugin_tags_to_html(text, output_func=_render_plugin)
return _plugin_tags_to_html(text, output_func=_render_plugin, child_plugin_instances=child_plugin_instances)


def plugin_tags_to_db(text):
def plugin_tags_to_db(text: str) -> str:
def _strip_plugin_content(obj, match):
return plugin_to_tag(obj).strip()

return _plugin_tags_to_html(text, output_func=_strip_plugin_content)
return _plugin_tags_to_html(text, output_func=_strip_plugin_content, child_plugin_instances=None)


def replace_plugin_tags(text, id_dict, regex=OBJ_ADMIN_RE):
def replace_plugin_tags(text:str , id_dict, regex: str = OBJ_ADMIN_RE) -> str:
from cms.models import CMSPlugin

plugins_by_id = CMSPlugin.objects.in_bulk(id_dict.values())
Expand Down
2 changes: 1 addition & 1 deletion private/js/cms.editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CMSEditor {
this.CMS.$(window).on('cms-content-refresh', () => {
if (document.querySelector('template.cms-plugin')) {
// django CMS core does not wrap newly inserted inline editable fields
this.CMS.API.Helpers.reloadBrowser();
// this.CMS.API.Helpers.reloadBrowser();
} else {
this._resetInlineEditors();
}
Expand Down
11 changes: 6 additions & 5 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def _replace_plugin_contents(self, text, new_plugin_content):
def _do_replace(obj, match):
return plugin_to_tag(obj, content=new_plugin_content)

return _plugin_tags_to_html(text, output_func=_do_replace)
return _plugin_tags_to_html(text, output_func=_do_replace, child_plugin_instances=None)

def add_plugin_to_text(self, text_plugin, plugin):
text_plugin.body = f"{text_plugin.body} {plugin_to_tag(plugin)}"
Expand Down Expand Up @@ -488,6 +488,7 @@ def test_change_form_has_rendered_plugin_content(self):
text_with_rendered_plugins = plugin_tags_to_admin_html(
text=text_plugin.body,
context=context,
child_plugin_instances=None,
)

endpoint = self.get_change_plugin_uri(text_plugin)
Expand Down Expand Up @@ -575,7 +576,7 @@ def test_user_cant_edit_child_plugins_directly(self):
text_plugin.body,
new_plugin_content='<img src="">',
)

print(overridden_text)
endpoint = self.get_change_plugin_uri(text_plugin)
response = self.client.post(endpoint, {"body": overridden_text})
text_plugin.refresh_from_db()
Expand Down Expand Up @@ -1144,7 +1145,7 @@ def test_get_child_plugin_candidates_whitelist(self, get_text_enabled_plugins_mo

with patch("djangocms_text.settings.TEXT_CHILDREN_WHITELIST", ["PluginA"]):
candidates = TextPlugin.get_child_plugin_candidates(slot, page)
self.assertEqual(candidates, [plugin_a])
self.assertEqual(list(candidates), [plugin_a])

@patch("cms.plugin_pool.plugin_pool.get_text_enabled_plugins")
def test_get_child_plugin_candidates_blacklist(self, get_text_enabled_plugins_mock):
Expand All @@ -1157,7 +1158,7 @@ def test_get_child_plugin_candidates_blacklist(self, get_text_enabled_plugins_mo

with patch("djangocms_text.settings.TEXT_CHILDREN_BLACKLIST", ["PluginA"]):
candidates = TextPlugin.get_child_plugin_candidates(slot, page)
self.assertEqual(candidates, [plugin_b])
self.assertEqual(list(candidates), [plugin_b])

@patch("cms.plugin_pool.plugin_pool.get_text_enabled_plugins")
def test_get_child_plugin_candidates_no_whitelist_blacklist(self, get_text_enabled_plugins_mock):
Expand All @@ -1171,4 +1172,4 @@ def test_get_child_plugin_candidates_no_whitelist_blacklist(self, get_text_enabl
with patch("djangocms_text.settings.TEXT_CHILDREN_WHITELIST", None):
with patch("djangocms_text.settings.TEXT_CHILDREN_BLACKLIST", []):
candidates = TextPlugin.get_child_plugin_candidates(slot, page)
self.assertEqual(candidates, [plugin_a, plugin_b])
self.assertEqual(list(candidates), [plugin_a, plugin_b])

0 comments on commit e2a9024

Please sign in to comment.