Skip to content

Commit

Permalink
Add django 3.1 support (django-cms#543)
Browse files Browse the repository at this point in the history
* add django 3.1 compatibility

* fix tests

* enable frontend tests

* run as seperate job

* use rst

* add fe to envlist

* nope

* further cleanup

* remove # -*- coding: utf-8 -*-

* get linting to work

* use same setup as on filer

* add additional adaptions

* needs to be py2 compatible for now

* renamce license file
  • Loading branch information
FinalAngel authored Sep 9, 2020
1 parent f8dc530 commit 11e7797
Show file tree
Hide file tree
Showing 49 changed files with 1,028 additions and 1,168 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ htmlcov/
.cache
nosetests.xml
*.cover
.coverage
.hypothesis/

# Translations
Expand Down
59 changes: 22 additions & 37 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,65 +1,50 @@
language: python

sudo: false
dist: xenial

matrix:
include:
- python: 3.5
env: TOX_ENV='flake8'
- python: 3.5
env: TOX_ENV='isort'
# Django 1.11
- python: 3.4
env: DJANGO='dj111' CMS='cms35' FE=1
- python: 3.5
env: DJANGO='dj111' CMS='cms36' FE=1
- python: 3.6
env: DJANGO='dj111' CMS='cms37' FE=1
# Django 2.1
- python: 3.6
env: DJANGO='dj21' CMS='cms36' FE=1
- python: 3.6
env: DJANGO='dj21' CMS='cms37' FE=1
env: TOX_ENV='frontend'
# Django 2.2
- python: 3.5
env: DJANGO='dj22' CMS='cms37'
- python: 3.6
env: DJANGO='dj22' CMS='cms37' FE=1
env: DJANGO='dj22' CMS='cms37'
- python: 3.7
env: DJANGO='dj22' CMS='cms37' FE=1
dist: xenial
sudo: true
env: DJANGO='dj22' CMS='cms37'
- python: 3.8
env: DJANGO='dj22' CMS='cms37'
# Django 3.0
# Django 3.0, always run the lowest supported version
- python: 3.6
env: DJANGO='dj30' CMS='cms37'
- python: 3.7
env: DJANGO='dj30' CMS='cms37'
- python: 3.8
env: DJANGO='dj30' CMS='cms37'
# Django 3.1, always run the lowest supported version
- python: 3.6
env: DJANGO='dj31' CMS='cms38'
allow_failures:
- python: 3.6
env: DJANGO='dj31' CMS='cms38'

before_script:
install:
- pip install coverage isort tox
- "if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then export PY_VER=py34; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export PY_VER=py35; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then export PY_VER=py36; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then export PY_VER=py37; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.8' ]]; then export PY_VER=py38; fi"
- "if [[ ${DJANGO}z != 'z' ]]; then export TOX_ENV=$PY_VER-$DJANGO-$CMS; fi"
- if [ "$FE" == 1 ]; then nvm install 8 && nvm use 8; fi
- if [ "$FE" == 1 ]; then npm config set spin false; fi
- if [ "$FE" == 1 ]; then npm install -g npm; fi
- if [ "$FE" == 1 ]; then npm install -g [email protected]; fi
- if [ "$FE" == 1 ]; then npm install -g codeclimate-test-reporter; fi
- if [ "$FE" == 1 ]; then npm ci; fi

install:
- pip install -U tox>=1.8 coveralls
- "if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then export PY_VER=py34; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export PY_VER=py35; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then export PY_VER=py36; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then export PY_VER=py37; fi"
- "if [[ ${DJANGO}z != 'z' ]]; then export TOX_ENV=$PY_VER-$DJANGO-$CMS; fi"
- "if [[ $FE == 1 ]]; then export FE_STR='-fe'; fi"
before_script:
- if [ $TOX_ENV == 'frontend' ]; then
nvm install 8.10.0 && nvm use 8.10.0;
npm config set spin false;
npm install -g npm;
npm install -g [email protected];
npm install;
fi

script:
- tox -e $TOX_ENV
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@
Changelog
=========

4.0.0 (unreleased)
==================

* Added support for Django 3.1
* Dropped support for Python 2.7 and Python 3.4
* Dropped support for Django < 2.2
* Ensure that correct urls are generated when static files are hosted on a CDN
* Allow to style WYSIWYG content based on parent plugins, by adding
``CMSPluginBase.child_ckeditor_body_css_class`` to a parent (#520)


3.10.0 (2020-08-04)
===================

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include LICENSE.txt
include LICENSE
include README.rst
recursive-include djangocms_text_ckeditor/locale *
recursive-include djangocms_text_ckeditor/static *
Expand Down
17 changes: 6 additions & 11 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,8 @@ for easy installation.

.. WARNING::

- For django CMS 3.4.x use ``djangocms-text-ckeditor`` >= 3.2.x (e.g.: version 3.2.1).
- For django CMS 3.3.x use ``djangocms-text-ckeditor`` >= 3.1.x,<3.5.1 (e.g.: version 3.1.0).
- For django CMS 3.2.x use ``djangocms-text-ckeditor`` <= 2.9.x (e.g.: version 2.9.3).
- For django CMS 3.0 and 3.1 use ``djangocms-text-ckeditor`` <= 2.7 (e.g.: version 2.7.0).
- For django CMS 2.3 and 2.4 use the ``djangocms-text-ckeditor`` 1.x releases (e.g.: version 1.0.10).
- For Django 1.4 and 1.5 use ``djangocms-text-ckeditor`` < 2.7.
- ``cms.plugins.text`` and ``djangocms-text-ckeditor`` can't be used at the same time.
- For django CMS 3.8.x+ use ``djangocms-text-ckeditor`` >= 4.x.x (e.g.: version 4.0.0).
- For django CMS 3.4.x+ use ``djangocms-text-ckeditor`` >= 3.2.x (e.g.: version 3.2.1).

.. image:: preview.gif

Expand Down Expand Up @@ -347,7 +342,7 @@ to render out all child plugins located in the ``body`` field. For example::
'name': instance.name,
})
# Other custom render code you may have
return super(MyTextPlugin, self).render(context, instance, placeholder)
return super().render(context, instance, placeholder)

plugin_pool.register_plugin(MyTextPlugin)

Expand Down Expand Up @@ -472,9 +467,9 @@ You can run tests by executing::
.. |coverage| image:: https://codecov.io/gh/divio/djangocms-text-ckeditor/branch/master/graph/badge.svg
:target: https://codecov.io/gh/divio/djangocms-text-ckeditor

.. |python| image:: https://img.shields.io/badge/python-2.7%20%7C%203.4+-blue.svg
.. |python| image:: https://img.shields.io/badge/python-3.5+-blue.svg
:target: https://pypi.org/project/djangocms-text-ckeditor/
.. |django| image:: https://img.shields.io/badge/django-1.11%20%7C%202.2%20%7C%203.0-blue.svg
.. |django| image:: https://img.shields.io/badge/django-2.2,%203.0,%203.1-blue.svg
:target: https://www.djangoproject.com/
.. |djangocms| image:: https://img.shields.io/badge/django%20CMS-3.4%2B-blue.svg
.. |djangocms| image:: https://img.shields.io/badge/django%20CMS-3.7%2B-blue.svg
:target: https://www.django-cms.org/
63 changes: 63 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
======================================
Upgrade to a newer version of CKEditor
======================================

Simple upgrade
--------------

Download the latest minified version of ckeditor, bundled with all
configured CKEditor-4 plugins. The correct URL can be found in
``djangocms_text_ckeditor/static/djangocms_text_ckeditor/ckeditor/build-config.js``
under (3).

Unzip that file and replace
``djangocms_text_ckeditor/static/djangocms_text_ckeditor/ckeditor`` with
it.

Rebundle everything running ``gulp build``.

Full upgrade
------------

This requires to access the sources of CKEditor. Clone the repository
and build CKEditor:

.. code:: bash

git clone https://github.com/ckeditor/ckeditor4.git
cd ckeditor4
./dev/builder/build.sh --leave-js-unminified

This creates an unminified release of CKEditor, which is useful for
debugging. In a production environment, remove the option
``--leave-js-unminified``.

**djangocms-text-ckeditor** reuses two plugins from CKEditor-4, which
are patched to work inside **django-CMS**. These plugins are found at
``djangocms_text_ckeditor/static/djangocms_text_ckeditor/ckeditor_plugins/cmsdialog/plugin.js``
and
``djangocms_text_ckeditor/static/djangocms_text_ckeditor/ckeditor_plugins/cmsresize/plugin.js``.
These plugins have been derived from
``ckeditor4/plugins/plugins/dialog/plugin.js`` and
``ckeditor4/plugins/plugins/resize/plugin.js`` respectively.

The current version of djangocms-text-ckeditor is based on the version
4.14.0 of CKEditor4. If these two plugins have to be ported to a later
version, first make a ``diff -u ...`` against version 4.14.0, then copy
these plugins into the current folders, ``cmsdialog`` and ``cmsresize``.
Then switch back to the latest version of CKEditor-4 and apply the
patches previously created.

Replace the folder
``djangocms_text_ckeditor/static/djangocms_text_ckeditor/ckeditor`` with
that from ``ckeditor4/dev/builder/release/ckeditor``.

Rebundle everything running ``gulp build``.

``.editorconfig``
~~~~~~~~~~~~~~~~~
Please don't convert tabs to spaces in the plugins patched from
CKEditor-4. They use tabs for indentation and if they are converted, it
makes file diffs much harder. That's the reason, why this folder
contains its own ``.editorconfig``.
1 change: 0 additions & 1 deletion aldryn_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from aldryn_client import forms


Expand Down
1 change: 0 additions & 1 deletion djangocms_text_ckeditor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
See PEP 440 (https://www.python.org/dev/peps/pep-0440/)
Expand Down
1 change: 0 additions & 1 deletion djangocms_text_ckeditor/apps.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig


Expand Down
1 change: 0 additions & 1 deletion djangocms_text_ckeditor/attribute_parsers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from .sanitizer import AllowTokenParser


Expand Down
30 changes: 13 additions & 17 deletions djangocms_text_ckeditor/cms_plugins.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
import json
import re
from distutils.version import LooseVersion

from django.conf.urls import url
from django.contrib.admin.utils import unquote
from django.core import signing
from django.core.exceptions import PermissionDenied, ValidationError
Expand All @@ -15,10 +13,10 @@
)
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.urls import reverse
from django.urls import re_path, reverse
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text
from django.utils.translation import ugettext
from django.utils.translation import gettext
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.decorators.http import require_POST

Expand All @@ -29,8 +27,6 @@
from cms.utils.placeholder import get_toolbar_plugin_struct
from cms.utils.urlutils import admin_reverse

from six import text_type

from . import settings
from .forms import (
ActionTokenValidationForm, DeleteOnCancelForm, RenderPluginForm, TextForm,
Expand Down Expand Up @@ -318,7 +314,7 @@ def __init__(self, *args, **kwargs):

if rendered_text:
initial['body'] = rendered_text
super(TextPluginForm, self).__init__(*args, initial=initial, **kwargs)
super().__init__(*args, initial=initial, **kwargs)
return TextPluginForm

@xframe_options_sameorigin
Expand All @@ -332,7 +328,7 @@ def add_view(self, request, form_url='', extra_context=None):
# and so a "ghost" plugin instance is left over.
# The instance is a record that points to the Text plugin
# but is not a real text plugin instance.
return super(TextPlugin, self).add_view(
return super().add_view(
request, form_url, extra_context
)

Expand All @@ -342,7 +338,7 @@ def add_view(self, request, form_url='', extra_context=None):
# This is NOT the normal workflow because we create a plugin
# on GET request to the /add/ endpoint and so we bypass
# django's add_view, thus bypassing permission check.
message = ugettext('You do not have permission to add a plugin.')
message = gettext('You do not have permission to add a plugin.')
return HttpResponseForbidden(force_text(message))

try:
Expand All @@ -361,7 +357,7 @@ def add_view(self, request, form_url='', extra_context=None):
}

except PermissionDenied:
message = ugettext('You do not have permission to add a plugin.')
message = gettext('You do not have permission to add a plugin.')
return HttpResponseForbidden(force_text(message))
except ValidationError as error:
return HttpResponseBadRequest(error.message)
Expand All @@ -378,7 +374,7 @@ def add_view(self, request, form_url='', extra_context=None):
)

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

success_url = admin_reverse('cms_page_add_plugin')
# Because we've created the cmsplugin record
Expand All @@ -389,7 +385,7 @@ def add_view(self, request, form_url='', extra_context=None):
def get_plugin_urls(self):
def pattern(regex, func):
name = self.get_admin_url_name(func.__name__)
return url(regex, func, name=name)
return re_path(regex, func, name=name)

url_patterns = [
pattern(r'^render-plugin/$', self.render_plugin),
Expand All @@ -414,7 +410,7 @@ def _get_text_plugin_from_request(self, request, data):

if text_plugin_id:
return self._get_plugin_or_404(text_plugin_id)
message = ugettext('Unable to process your request. Invalid token.')
message = gettext('Unable to process your request. Invalid token.')
raise ValidationError(message=force_text(message))

@random_comment_exempt
Expand All @@ -428,7 +424,7 @@ def render_plugin(self, request):
form = RenderPluginForm(request.GET, text_plugin=text_plugin)

if not form.is_valid():
message = ugettext('Unable to process your request.')
message = gettext('Unable to process your request.')
return HttpResponseBadRequest(message)

plugin_class = text_plugin.get_plugin_class_instance()
Expand Down Expand Up @@ -460,7 +456,7 @@ def delete_on_cancel(self, request):
form = DeleteOnCancelForm(request.POST, text_plugin=text_plugin)

if not form.is_valid():
message = ugettext('Unable to process your request.')
message = gettext('Unable to process your request.')
return HttpResponseBadRequest(message)

plugin_class = text_plugin.get_plugin_class_instance()
Expand Down Expand Up @@ -508,7 +504,7 @@ def get_form(self, request, obj=None, **kwargs):
plugin=plugin,
)
kwargs['form'] = form # override standard form
return super(TextPlugin, self).get_form(request, obj, **kwargs)
return super().get_form(request, obj, **kwargs)

def render(self, context, instance, placeholder):
context.update({
Expand All @@ -535,7 +531,7 @@ def save_model(self, request, obj, form, change):
value = getattr(self.cms_plugin_instance, field.name)
setattr(obj, field.name, value)

super(TextPlugin, self).save_model(request, obj, form, change)
super().save_model(request, obj, form, change)
# This must come after calling save
# If `clean_plugins()` deletes child plugins, django-treebeard will call
# save() again on the Text instance (aka obj in this context) to update mptt values (numchild, etc).
Expand Down
Loading

0 comments on commit 11e7797

Please sign in to comment.