Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Delay importing models.CMSPlugin in utils. #637

Merged
merged 3 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Changelog
Unreleased
==========

* Fix `468 <https://github.com/django-cms/djangocms-text-ckeditor/issues/468>`_ via `637 <https://github.com/django-cms/djangocms-text-ckeditor/pull/637>`_: Delay importing models.CMSPlugin in utils to allow adding an HTMLField to a custom user model.


5.1.1 (2022-06-22)
==================

Expand Down
56 changes: 34 additions & 22 deletions djangocms_text_ckeditor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
from collections import OrderedDict
from functools import WRAPPER_ASSIGNMENTS, wraps

from classytags.utils import flatten_context

from django.core.files.storage import get_storage_class
from django.template.defaultfilters import force_escape
from django.template.loader import render_to_string
from django.utils.functional import LazyObject

from cms.models import CMSPlugin

from classytags.utils import flatten_context


OBJ_ADMIN_RE_PATTERN = r'<cms-plugin .*?\bid="(?P<pk>\d+)".*?>.*?</cms-plugin>'
OBJ_ADMIN_WITH_CONTENT_RE_PATTERN = r'<cms-plugin .*?\bid="(?P<pk>\d+)".*?>(?P<content>.*?)</cms-plugin>'
OBJ_ADMIN_WITH_CONTENT_RE_PATTERN = (
r'<cms-plugin .*?\bid="(?P<pk>\d+)".*?>(?P<content>.*?)</cms-plugin>'
)
OBJ_ADMIN_RE = re.compile(OBJ_ADMIN_RE_PATTERN, flags=re.DOTALL)


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

# This my fellow ckeditor enthusiasts is a hack..

Expand All @@ -34,9 +34,9 @@ def _render_cms_plugin(plugin, context):
# and thus calls context processors AND render the plugin manually with the context
# after it's been bound to a template.
response = render_to_string(
'cms/plugins/render_plugin_preview.html',
"cms/plugins/render_plugin_preview.html",
context,
request=context['request'],
request=context["request"],
)
return response

Expand All @@ -49,10 +49,11 @@ def wrapped_view(*args, **kwargs):
response = view_func(*args, **kwargs)
response._random_comment_exempt = True
return response

return wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)(wrapped_view)


def plugin_to_tag(obj, content='', admin=False):
def plugin_to_tag(obj, content="", admin=False):
plugin_attrs = OrderedDict(
id=obj.pk,
icon_alt=force_escape(obj.get_instance_icon_alt()),
Expand All @@ -62,12 +63,12 @@ def plugin_to_tag(obj, content='', admin=False):
if admin:
# Include extra attributes when rendering on the admin
plugin_class = obj.get_plugin_class()
preview = getattr(plugin_class, 'text_editor_preview', True)
preview = getattr(plugin_class, "text_editor_preview", True)
plugin_tag = (
'<cms-plugin render-plugin=%(preview)s alt="%(icon_alt)s "'
'title="%(icon_alt)s" id="%(id)d">%(content)s</cms-plugin>'
)
plugin_attrs['preview'] = 'true' if preview else 'false'
plugin_attrs["preview"] = "true" if preview else "false"
else:
plugin_tag = (
'<cms-plugin alt="%(icon_alt)s "'
Expand All @@ -79,10 +80,11 @@ def plugin_to_tag(obj, content='', admin=False):
def plugin_tags_to_id_list(text, regex=OBJ_ADMIN_RE):
def _find_plugins():
for tag in regex.finditer(text):
plugin_id = tag.groupdict().get('pk')
plugin_id = tag.groupdict().get("pk")

if plugin_id:
yield plugin_id

return [int(_id) for _id in _find_plugins()]


Expand All @@ -96,59 +98,67 @@ def _plugin_tags_to_html(text, output_func):

def _render_tag(m):
try:
plugin_id = int(m.groupdict()['pk'])
plugin_id = int(m.groupdict()["pk"])
obj = plugins_by_id[plugin_id]
except KeyError:
# Object must have been deleted. It cannot be rendered to
# end user so just remove it from the HTML altogether
return ''
return ""
else:
obj._render_meta.text_enabled = True
return output_func(obj, m)

return OBJ_ADMIN_RE.sub(_render_tag, text)


def plugin_tags_to_user_html(text, context):
def _render_plugin(obj, match):
return _render_cms_plugin(obj, context)

return _plugin_tags_to_html(text, output_func=_render_plugin)


def plugin_tags_to_admin_html(text, context):
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)


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

return _plugin_tags_to_html(text, output_func=_strip_plugin_content)


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

plugins_by_id = CMSPlugin.objects.in_bulk(id_dict.values())

def _replace_tag(m):
try:
plugin_id = int(m.groupdict()['pk'])
plugin_id = int(m.groupdict()["pk"])
new_id = id_dict[plugin_id]
plugin = plugins_by_id[new_id]
except KeyError:
# Object must have been deleted. It cannot be rendered to
# end user, or edited, so just remove it from the HTML
# altogether
return ''
return ""
return plugin_to_tag(plugin)

return regex.sub(_replace_tag, text)


def get_plugins_from_text(text, regex=OBJ_ADMIN_RE):
from cms.utils.plugins import downcast_plugins
from cms.models import CMSPlugin

plugin_ids = plugin_tags_to_id_list(text, regex)
plugins = CMSPlugin.objects.filter(pk__in=plugin_ids).select_related('placeholder')
plugins = CMSPlugin.objects.filter(pk__in=plugin_ids).select_related("placeholder")
plugin_list = downcast_plugins(plugins, select_placeholder=True)
return {plugin.pk: plugin for plugin in plugin_list}

Expand All @@ -157,14 +167,16 @@ def get_plugins_from_text(text, regex=OBJ_ADMIN_RE):
The following class is taken from https://github.com/jezdez/django/compare/feature/staticfiles-templatetag
and should be removed and replaced by the django-core version in 1.4
"""
default_storage = 'django.contrib.staticfiles.storage.StaticFilesStorage'
default_storage = "django.contrib.staticfiles.storage.StaticFilesStorage"


class ConfiguredStorage(LazyObject):

def _setup(self):
from django.conf import settings
self._wrapped = get_storage_class(getattr(settings, 'STATICFILES_STORAGE', default_storage))()

self._wrapped = get_storage_class(
getattr(settings, "STATICFILES_STORAGE", default_storage)
)()


configured_storage = ConfiguredStorage()
Expand All @@ -175,5 +187,5 @@ def static_url(path):
Helper that prefixes a URL with STATIC_URL and cms
"""
if not path:
return ''
return configured_storage.url(os.path.join('', path))
return ""
return configured_storage.url(os.path.join("", path))