Skip to content

Commit

Permalink
feat: Universal support for django CMS v3 and v4 (#631)
Browse files Browse the repository at this point in the history
* add django-cms 4 support to main branch
* Fix: render plugin test to use LinkPlugin in stead of PicturePlugin (since newer versions of easythumbnailer return float instead of int)
Add: Tests for django CMS 4
  • Loading branch information
fsbraun authored Dec 21, 2022
1 parent c8d5fce commit 3d09c69
Show file tree
Hide file tree
Showing 31 changed files with 365 additions and 616 deletions.
23 changes: 20 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,35 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ 3.7, 3.8, 3.9, '3.10']
python-version: [ 3.7, 3.8, 3.9, '3.10', "3.11"]
requirements-file: [
dj22_cms37.txt,
dj22_cms38.txt,
dj22_cms40.txt,
dj31_cms38.txt,
dj32_cms39.txt,
dj32_cms310.txt
dj32_cms310.txt,
dj32_cms311.txt,
dj32_cms41.txt,
dj40_cms311.txt,
dj40_cms41.txt
]
os: [
ubuntu-20.04,
]

exclude:
- python-version: 3.7
requirements-file: dj40_cms311.txt
- python-version: 3.7
requirements-file: dj40_cms41.txt
- python-version: 3.7
requirements-file: dj41_cms311.txt
- python-version: 3.7
requirements-file: dj41_cms41.txt
- python-version: "3.10"
requirements-file: dj22_cms40.txt
- python-version: "3.11"
requirements-file: dj22_cms40.txt
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog
Unreleased
==========

* Add suport for django CMS 4
* 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.


Expand Down
7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
django CMS Text CKEditor
========================

|pypi| |coverage| |python| |django| |djangocms|
|pypi| |coverage| |python| |django| |djangocms| |djangocms4|


.. note::
Expand Down Expand Up @@ -512,10 +512,11 @@ You can run tests by executing::
:target: https://travis-ci.org/divio/djangocms-text-ckeditor
.. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-text-ckeditor/branch/master/graph/badge.svg
:target: https://codecov.io/gh/django-cms/djangocms-text-ckeditor

.. |python| image:: https://img.shields.io/badge/python-3.7+-blue.svg
:target: https://pypi.org/project/djangocms-text-ckeditor/
.. |django| image:: https://img.shields.io/badge/django-2.2,%203.1,%203.2-blue.svg
.. |django| image:: https://img.shields.io/badge/django-2.2--4.0-blue.svg
:target: https://www.djangoproject.com/
.. |djangocms| image:: https://img.shields.io/badge/django%20CMS-3.7%2B-blue.svg
:target: https://www.django-cms.org/
.. |djangocms4| image:: https://img.shields.io/badge/django%20CMS-4-blue.svg
:target: https://www.django-cms.org/
3 changes: 1 addition & 2 deletions aldryn_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ def to_settings(self, data, settings):
else:
ckeditor_settings['contentsCss'] = ['/static/css/base.css']

style_set = ''
if data.get('style_set'):
style_set = data['style_set']
else:
style_set = ''

ckeditor_settings['stylesSet'] = f'default:{style_set}'

Expand Down
1 change: 1 addition & 0 deletions djangocms_text_ckeditor/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
class TextCkeditorConfig(AppConfig):
name = 'djangocms_text_ckeditor'
verbose_name = 'django CMS Text CKEditor'
default_auto_field = 'django.db.models.AutoField'
42 changes: 20 additions & 22 deletions djangocms_text_ckeditor/cms_plugins.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json
import operator
import re
from distutils.version import LooseVersion

from django.contrib.admin.utils import unquote
from django.core import signing
Expand All @@ -19,7 +18,6 @@
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.decorators.http import require_POST

import cms
from cms.models import CMSPlugin
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
Expand All @@ -30,21 +28,12 @@
from .forms import ActionTokenValidationForm, DeleteOnCancelForm, RenderPluginForm, TextForm
from .models import Text
from .utils import (
OBJ_ADMIN_WITH_CONTENT_RE_PATTERN, _plugin_tags_to_html, plugin_tags_to_admin_html, plugin_tags_to_id_list,
plugin_tags_to_user_html, plugin_to_tag, random_comment_exempt, replace_plugin_tags,
OBJ_ADMIN_WITH_CONTENT_RE_PATTERN, _plugin_tags_to_html, cms_placeholder_add_plugin, plugin_tags_to_admin_html,
plugin_tags_to_id_list, plugin_tags_to_user_html, plugin_to_tag, random_comment_exempt, replace_plugin_tags,
)
from .widgets import TextEditorWidget


CMS_34 = LooseVersion(cms.__version__) >= LooseVersion("3.4")


def _user_can_change_placeholder(request, placeholder):
if CMS_34:
return placeholder.has_change_permission(request.user)
return placeholder.has_change_permission(request)


def post_add_plugin(operation, **kwargs):
from djangocms_history.actions import ADD_PLUGIN
from djangocms_history.helpers import get_bound_plugins, get_plugin_data
Expand Down Expand Up @@ -187,10 +176,9 @@ class TextPlugin(CMSPluginBase):
"pre_change_plugin": pre_change_plugin,
}

if CMS_34:
# On django CMS 3.5 this attribute is set automatically
# when do_post_copy is defined in the plugin class.
_has_do_post_copy = True
# On django CMS 3.5 this attribute is set automatically
# when do_post_copy is defined in the plugin class.
_has_do_post_copy = True

@classmethod
def do_post_copy(cls, instance, source_map):
Expand Down Expand Up @@ -251,6 +239,7 @@ def get_editor_widget(self, request, plugins, plugin):
pk=plugin.pk,
placeholder=plugin.placeholder,
plugin_language=plugin.language,
plugin_position=plugin.position,
configuration=self.ckeditor_configuration,
render_plugin_url=render_plugin_url,
cancel_url=cancel_url,
Expand Down Expand Up @@ -331,6 +320,14 @@ def __init__(self, *args, **kwargs):

return TextPluginForm

@staticmethod
def _create_ghost_plugin(placeholder, plugin):
"""CMS version-save function to add a plugin to a placeholder"""
if hasattr(placeholder, "add_plugin"): # available as of CMS v4
placeholder.add_plugin(plugin)
else: # CMS < v4
plugin.save()

@xframe_options_sameorigin
def add_view(self, request, form_url="", extra_context=None):
if "plugin" in request.GET:
Expand Down Expand Up @@ -381,18 +378,19 @@ def add_view(self, request, form_url="", extra_context=None):
# Sadly we have to create the CMSPlugin record on add GET request
# because we need this record in order to allow the user to add
# child plugins to the text (image, link, etc..)
plugin = CMSPlugin.objects.create(
plugin = CMSPlugin(
language=data["plugin_language"],
plugin_type=data["plugin_type"],
position=data["position"],
placeholder=data["placeholder_id"],
position=data["position"],
parent=data.get("plugin_parent"),
)
self._create_ghost_plugin(data["placeholder_id"], plugin)

query = request.GET.copy()
query["plugin"] = str(plugin.pk)

success_url = admin_reverse("cms_page_add_plugin")
success_url = admin_reverse(cms_placeholder_add_plugin) # Version dependent
# Because we've created the cmsplugin record
# we need to delete the plugin when a user cancels.
success_url += "?delete-on-cancel&" + query.urlencode()
Expand Down Expand Up @@ -449,7 +447,7 @@ def render_plugin(self, request):

if not (
plugin_class.has_change_permission(request, obj=text_plugin)
and _user_can_change_placeholder(request, text_plugin.placeholder) # noqa
and text_plugin.placeholder.has_change_permission(request.user) # noqa
):
raise PermissionDenied
return HttpResponse(form.render_plugin(request))
Expand Down Expand Up @@ -486,7 +484,7 @@ def delete_on_cancel(self, request):
# and the ckeditor plugin itself.
if not (
plugin_class.has_add_permission(request)
and _user_can_change_placeholder(request, text_plugin.placeholder) # noqa
and text_plugin.placeholder.has_change_permission(request.user) # noqa
):
raise PermissionDenied
# Token is validated after checking permissions
Expand Down
6 changes: 0 additions & 6 deletions djangocms_text_ckeditor/compat.py

This file was deleted.

14 changes: 12 additions & 2 deletions djangocms_text_ckeditor/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,23 @@ def get_child_plugins(self):
queryset = queryset.exclude(pk__in=excluded_plugins)
return queryset

@staticmethod
def _delete_plugin(plugin):
"""Version-safe plugin delete method"""
placeholder = plugin.placeholder
if hasattr(placeholder, 'delete_plugin'): # since CMS v4
return placeholder.delete_plugin(plugin)
else:
return plugin.delete()

def delete(self):
child_plugins = self.cleaned_data.get('child_plugins')

if child_plugins:
child_plugins.delete()
for child in child_plugins:
self._delete_plugin(child)
else:
self.text_plugin.delete()
self._delete_plugin(self.text_plugin)


class TextForm(ModelForm):
Expand Down
27 changes: 18 additions & 9 deletions djangocms_text_ckeditor/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from copy import deepcopy

from django.db import models
from django.utils.encoding import force_str
from django.utils.html import strip_tags
from django.utils.text import Truncator
from django.utils.translation import gettext_lazy as _

from cms.models import CMSPlugin
from cms.utils.copy_plugins import copy_plugins_to

from . import settings
from .html import clean_html, extract_images
Expand Down Expand Up @@ -82,14 +83,22 @@ def clean_plugins(self):
def copy_referenced_plugins(self):
referenced_plugins = self.get_referenced_plugins()
if referenced_plugins:
plugins_pairs = list(copy_plugins_to(
referenced_plugins,
self.placeholder,
to_language=self.language,
parent_plugin_id=self.id,
))
self.add_existing_child_plugins_to_pairs(plugins_pairs)
self.post_copy(self, plugins_pairs)
plugin_pairs = []
for source_plugin in referenced_plugins:
new_plugin = deepcopy(source_plugin)
new_plugin.pk = None
new_plugin.id = None
new_plugin._state.adding = True
new_plugin.parent = self
if hasattr(self.placeholder, "add_plugin"): # CMS v4
new_plugin.position = self.position + 1
new_plugin = self.placeholder.add_plugin(new_plugin)
else:
new_plugin = self.add_child(instance=new_plugin)
new_plugin.copy_relations(source_plugin)
plugin_pairs.append((new_plugin, source_plugin))
self.add_existing_child_plugins_to_pairs(plugin_pairs)
self.post_copy(self, plugin_pairs)

def get_referenced_plugins(self):
ids_in_body = set(plugin_tags_to_id_list(self.body))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,23 @@
init: function (editor) {
var that = this;

CKEDITOR.on('instanceReady', function () {
var widgetInstances = [];

for (var key in editor.widgets.instances) {
if (editor.widgets.instances.hasOwnProperty(key)) {
widgetInstances.push(editor.widgets.instances[key]);
}
}

that.numberOfChildren = CKEDITOR.tools.array.filter(widgetInstances, function (i) {
return i.name === 'cms-widget';
}).length;
});
/**
* populated with _fresh_ child plugins
*/
this.child_plugins = [];
this.unsaved_child_plugins = [];
var settings = CMS.CKEditor.editors[editor.id].settings;
this.setupCancelCleanupCallback(settings);

Expand Down Expand Up @@ -210,9 +223,10 @@
// in case it's a fresh text plugin children don't have to be
// deleted separately
if (!editor.config.settings.delete_on_cancel && addedChildPlugin) {
that.child_plugins.push(data.plugin_id);
that.unsaved_child_plugins.push(data.plugin_id);
}
that.insertPlugin(data, dialog.sender._.editor);
that.numberOfChildren += 1

CMS.API.Helpers.onPluginSave = onSave;
return false;
Expand Down Expand Up @@ -315,6 +329,7 @@
plugin_type: item.attr('rel'),
plugin_parent: settings.plugin_id,
plugin_language: settings.plugin_language,
plugin_position: settings.plugin_position + 1 + this.numberOfChildren,
cms_path: window.parent.location.pathname,
cms_history: 0
};
Expand Down Expand Up @@ -391,18 +406,18 @@
var that = this;
var CMS = window.parent.CMS;
var cancelModalCallback = function cancelModalCallback(e, opts) {
if (!settings.delete_on_cancel && !that.child_plugins.length) {
if (!settings.delete_on_cancel && !that.unsaved_child_plugins.length) {
return;
}
if (that.child_plugins.length) {
if (that.unsaved_child_plugins.length) {
e.preventDefault();
CMS.API.Toolbar.showLoader();
var data = {
token: settings.action_token
};

if (!settings.delete_on_cancel) {
data.child_plugins = that.child_plugins;
data.child_plugins = that.unsaved_child_plugins;
}

$.ajax({
Expand Down
Loading

0 comments on commit 3d09c69

Please sign in to comment.