From fa09b8fc94045d7b2c66394989ef1bbad0c9780d Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Wed, 3 Jan 2018 16:49:44 -0200 Subject: [PATCH 01/10] WIP --- djangocms_text_ckeditor/cms_plugins.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index 0f28ccc9a..af95fb640 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -35,6 +35,8 @@ plugin_tags_to_user_html, random_comment_exempt, replace_plugin_tags, + plugin_to_tag, + _plugin_tags_to_html, ) from .widgets import TextEditorWidget @@ -197,6 +199,16 @@ def do_post_copy(self, instance, source_map): new_text = replace_plugin_tags(instance.body, ids_map) self.model.objects.filter(pk=instance.pk).update(body=new_text) + @staticmethod + def get_djangocms_translation_content(instance): + def _render_plugin_with_content(obj, match): + # FIXME: use text_field_child_label_field = TRANSLATIONS_CONF[obj.plugin_type]['text_field_child_label'] + text_field_child_label_field = 'label' # FIXME: + content = getattr(obj, text_field_child_label_field) + return plugin_to_tag(obj, content) + + return _plugin_tags_to_html(instance.body, output_func=_render_plugin_with_content) + def get_editor_widget(self, request, plugins, plugin): """ Returns the Django form Widget to be used for From 04755f433ab572fe6c7cfd2219020fbe40c5d434 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Wed, 3 Jan 2018 18:08:50 -0200 Subject: [PATCH 02/10] WIP --- djangocms_text_ckeditor/cms_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index af95fb640..0d940637b 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -200,14 +200,14 @@ def do_post_copy(self, instance, source_map): self.model.objects.filter(pk=instance.pk).update(body=new_text) @staticmethod - def get_djangocms_translation_content(instance): + def get_translation_content(field, plugin_data): def _render_plugin_with_content(obj, match): # FIXME: use text_field_child_label_field = TRANSLATIONS_CONF[obj.plugin_type]['text_field_child_label'] text_field_child_label_field = 'label' # FIXME: content = getattr(obj, text_field_child_label_field) return plugin_to_tag(obj, content) - return _plugin_tags_to_html(instance.body, output_func=_render_plugin_with_content) + return _plugin_tags_to_html(plugin_data[field], output_func=_render_plugin_with_content) def get_editor_widget(self, request, plugins, plugin): """ From 8e26b0de4f077334a0667282932ffef8ba3ee2a2 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Thu, 4 Jan 2018 19:13:42 -0200 Subject: [PATCH 03/10] WIP --- djangocms_text_ckeditor/cms_plugins.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index 0d940637b..54568dd99 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -202,12 +202,23 @@ def do_post_copy(self, instance, source_map): @staticmethod def get_translation_content(field, plugin_data): def _render_plugin_with_content(obj, match): - # FIXME: use text_field_child_label_field = TRANSLATIONS_CONF[obj.plugin_type]['text_field_child_label'] - text_field_child_label_field = 'label' # FIXME: - content = getattr(obj, text_field_child_label_field) + field = 'label' # FIXME: Get field via DJANGOCMS_TRANSLATIONS_CONF + content = getattr(obj, field) return plugin_to_tag(obj, content) - return _plugin_tags_to_html(plugin_data[field], output_func=_render_plugin_with_content) + content = _plugin_tags_to_html(plugin_data[field], output_func=_render_plugin_with_content) + subplugins_within_this_content = plugin_tags_to_id_list(content) + return content, subplugins_within_this_content + + @staticmethod + def get_translation_children_content(content, plugin): + import bs4 # FIXME: Try to stick to ckeditor utils. + soup = bs4.BeautifulSoup(content, 'html.parser') + + return { + subplugin_id: soup.find('cms-plugin', id=subplugin_id).text + for subplugin_id in plugin_tags_to_id_list(content) + } def get_editor_widget(self, request, plugins, plugin): """ From ce1ef7c610a473407b56f1a71d5342a6b17feade Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Fri, 5 Jan 2018 17:14:53 -0200 Subject: [PATCH 04/10] Add tests --- .gitignore | 122 +++++++++++++++--- .../test_app/cms_plugins.py | 8 ++ .../test_app/migrations/0001_initial.py | 36 ++++++ .../test_app/migrations/__init__.py | 0 djangocms_text_ckeditor/test_app/models.py | 19 +++ .../test_app/templates/test_app/base.html | 24 ++++ .../test_app/templates/test_app/page.html | 8 ++ djangocms_text_ckeditor/tests/test_plugin.py | 110 +++++++++++++++- test_requirements.txt | 13 ++ test_settings.py | 4 + tox.ini | 7 +- 11 files changed, 329 insertions(+), 22 deletions(-) create mode 100644 djangocms_text_ckeditor/test_app/migrations/0001_initial.py create mode 100644 djangocms_text_ckeditor/test_app/migrations/__init__.py create mode 100644 djangocms_text_ckeditor/test_app/templates/test_app/base.html create mode 100644 djangocms_text_ckeditor/test_app/templates/test_app/page.html create mode 100644 test_requirements.txt diff --git a/.gitignore b/.gitignore index 02289bd8f..542cffaa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,108 @@ -*.log -*.pot -*.pyc -.project -.pydevproject -.settings -build -env* -dist -!djangocms_text_ckeditor/static/djangocms_text_ckeditor/js/dist -djangocms_text_ckeditor.egg-info -*.DS_Store -.idea -.tox +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ .coverage -/node_modules/ +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Custom +*.sqlite3 +*.sqlite diff --git a/djangocms_text_ckeditor/test_app/cms_plugins.py b/djangocms_text_ckeditor/test_app/cms_plugins.py index 3ce840be1..6383f6246 100644 --- a/djangocms_text_ckeditor/test_app/cms_plugins.py +++ b/djangocms_text_ckeditor/test_app/cms_plugins.py @@ -3,6 +3,8 @@ from django.template import engines from djangocms_text_ckeditor.cms_plugins import TextPlugin +from djangocms_text_ckeditor.test_app.models import DummyLink + @plugin_pool.register_plugin class PreviewDisabledPlugin(CMSPluginBase): @@ -22,3 +24,9 @@ class SekizaiPlugin(CMSPluginBase): @plugin_pool.register_plugin class ExtendedTextPlugin(TextPlugin): name = 'Extended' + + +@plugin_pool.register_plugin +class DummyLinkPlugin(CMSPluginBase): + render_plugin = False + model = DummyLink diff --git a/djangocms_text_ckeditor/test_app/migrations/0001_initial.py b/djangocms_text_ckeditor/test_app/migrations/0001_initial.py new file mode 100644 index 000000000..d04db2a8a --- /dev/null +++ b/djangocms_text_ckeditor/test_app/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-01-03 18:23 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +from djangocms_text_ckeditor.fields import HTMLField + + +class Migration(migrations.Migration): + initial = True + + operations = [ + migrations.CreateModel( + name='DummyLink', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='test_app_dummylink', serialize=False, to='cms.CMSPlugin')), + ('label', models.TextField()), + ], + options={ + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + + migrations.CreateModel( + name='SimpleText', + fields=[ + ('text', HTMLField(blank=True)), + ], + options={ + 'abstract': False, + }, + bases=(models.Model,), + ), + ] diff --git a/djangocms_text_ckeditor/test_app/migrations/__init__.py b/djangocms_text_ckeditor/test_app/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/djangocms_text_ckeditor/test_app/models.py b/djangocms_text_ckeditor/test_app/models.py index 92396f2c9..ba31618e7 100644 --- a/djangocms_text_ckeditor/test_app/models.py +++ b/djangocms_text_ckeditor/test_app/models.py @@ -1,8 +1,27 @@ # -*- coding: utf-8 -*- +from cms.models import CMSPlugin from django.db import models +from django.utils.encoding import python_2_unicode_compatible from djangocms_text_ckeditor.fields import HTMLField class SimpleText(models.Model): text = HTMLField(blank=True) + + +@python_2_unicode_compatible +class DummyLink(CMSPlugin): + cmsplugin_ptr = models.OneToOneField( + CMSPlugin, + on_delete=models.CASCADE, + related_name='%(app_label)s_%(class)s', + parent_link=True, + ) + label = models.TextField() + + class Meta: + abstract = False + + def __str__(self): + return 'dummy link object' diff --git a/djangocms_text_ckeditor/test_app/templates/test_app/base.html b/djangocms_text_ckeditor/test_app/templates/test_app/base.html new file mode 100644 index 000000000..db9d806d0 --- /dev/null +++ b/djangocms_text_ckeditor/test_app/templates/test_app/base.html @@ -0,0 +1,24 @@ +{% load cms_tags static menu_tags sekizai_tags %} + + + + {% block title %}This is my new project home page{% endblock title %} + {% render_block "css" %} + + + +{% cms_toolbar %} +
+ + {% block content %} diff --git a/djangocms_text_ckeditor/test_app/templates/test_app/page.html b/djangocms_text_ckeditor/test_app/templates/test_app/page.html new file mode 100644 index 000000000..e96a8f2f0 --- /dev/null +++ b/djangocms_text_ckeditor/test_app/templates/test_app/page.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% load cms_tags %} + +{% block title %}{% page_attribute 'title' %}{% endblock title %} + +{% block content %} + {% placeholder "content" %} +{% endblock content %} diff --git a/djangocms_text_ckeditor/tests/test_plugin.py b/djangocms_text_ckeditor/tests/test_plugin.py index 995746e0f..f7d7d4d92 100644 --- a/djangocms_text_ckeditor/tests/test_plugin.py +++ b/djangocms_text_ckeditor/tests/test_plugin.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import copy +import json import re from cms.api import add_plugin, create_page, create_title @@ -13,6 +14,8 @@ from django.utils.html import escape from django.utils.http import urlencode, urlunquote +from djangocms_transfer.exporter import export_page + from djangocms_text_ckeditor.cms_plugins import TextPlugin from djangocms_text_ckeditor.models import Text from djangocms_text_ckeditor.utils import ( @@ -782,8 +785,113 @@ def test_text_plugin_xss(self): with self.login_user_context(self.user): data = { - "body": "
divcontent
acontent" + "body": ( + "
divcontent
acontent" + ) } response = self.client.post(endpoint, data) self.assertEqual(response.status_code, 200) self.assertEqual(self.reload(plugin).body, '
divcontent
acontent') + + +class DjangoCMSTranslationsIntegrationTestCase(BaseTestCase): + def setUp(self): + super(DjangoCMSTranslationsIntegrationTestCase, self).setUp() + self.page = create_page('test page', 'page.html', 'en', published=True) + self.placeholder = self.page.placeholders.get(slot='content') + + def _export_page(self): + return json.loads(export_page(self.page, 'en')) + + def test_textfield_without_children(self): + raw_content = '

Please CLICK ON LINK1 to go to link1.

' + add_plugin(self.placeholder, 'TextPlugin', 'en', body=raw_content) + + plugin = self._export_page()[0]['plugins'][0] + result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + + self.assertEquals(result, raw_content) + self.assertEquals(children_included_in_this_content, []) + + result = TextPlugin.get_translation_children_content(result, plugin) + self.assertDictEqual(result, {}) + + def test_textfield_with_children(self): + parent = add_plugin(self.placeholder, 'TextPlugin', 'en', body='') + child1 = add_plugin(self.placeholder, 'DummyLinkPlugin', 'en', target=parent, label='CLICK ON LINK1') + parent_body = ( + '

Please to go to link1.

' + ).format(child1.pk) + parent.body = parent_body + parent.save() + + plugin = self._export_page()[0]['plugins'][0] + result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + + expected = ( + parent_body + .replace('>', '>CLICK ON LINK1', 1) + ) + self.assertEquals(result, expected) + self.assertEquals(children_included_in_this_content, [child1.pk]) + + result = TextPlugin.get_translation_children_content(result, plugin) + self.assertDictEqual(result, {child1.pk: 'CLICK ON LINK1'}) + + def test_textfield_with_multiple_children(self): + parent = add_plugin(self.placeholder, 'TextPlugin', 'en', body='') + child1 = add_plugin(self.placeholder, 'DummyLinkPlugin', 'en', target=parent, label='CLICK ON LINK1') + child2 = add_plugin(self.placeholder, 'DummyLinkPlugin', 'en', target=parent, label='CLICK ON LINK2') + parent_body = ( + '

Please to go to link1 ' + 'or to go to link2.

' + ).format(child1.pk, child2.pk) + parent.body = parent_body + parent.save() + + plugin = self._export_page()[0]['plugins'][0] + result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + + expected = ( + parent_body + .replace('>', '>CLICK ON LINK1', 1) + .replace('>', '>CLICK ON LINK2', 1) + ) + self.assertEquals(result, expected) + self.assertEquals(children_included_in_this_content, [child1.pk, child2.pk]) + + result = TextPlugin.get_translation_children_content(result, plugin) + self.assertDictEqual(result, {child1.pk: 'CLICK ON LINK1', child2.pk: 'CLICK ON LINK2'}) + + def test_textfield_with_multiple_children_one_deleted(self): + parent = add_plugin(self.placeholder, 'TextPlugin', 'en', body='') + child1 = add_plugin(self.placeholder, 'DummyLinkPlugin', 'en', target=parent, label='CLICK ON LINK1') + child2 = add_plugin(self.placeholder, 'DummyLinkPlugin', 'en', target=parent, label='CLICK ON LINK2') + parent_body = ( + '

Please to go to link1 ' + 'or to go to link2.

' + ).format(child1.pk, child2.pk) + parent.body = parent_body + parent.save() + + plugin = self._export_page()[0]['plugins'][0] + + child1.delete() + + result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + + expected = ( + '

Please to go to link1 ' + 'or CLICK ON LINK2 to go to link2.

' + ).format(child2.pk) + self.assertEquals(result, expected) + self.assertEquals(children_included_in_this_content, [child2.pk]) + + result = TextPlugin.get_translation_children_content(result, plugin) + self.assertDictEqual(result, {child2.pk: 'CLICK ON LINK2'}) diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 000000000..81ec46b63 --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1,13 @@ +djangocms-helper>=0.9.2,<0.10 +django-filer>=1.3.0 +djangocms-picture>=2.0.6 +djangocms-link>=2.1.2 + +Pillow +html5lib>=0.999999 + +tox>=2.9.1 +coverage>=4.4.2 +flake8>=3.0.4 + +-e git+ssh://git@github.com/divio/djangocms-transfer.git@master#egg=djangocms-transfer diff --git a/test_settings.py b/test_settings.py index 5bee09399..6071c8b97 100644 --- a/test_settings.py +++ b/test_settings.py @@ -80,6 +80,10 @@ def gettext(s): 'filer.thumbnail_processors.scale_and_crop_with_subject_location', 'easy_thumbnails.processors.filters', ), + 'CMS_TEMPLATES': ( + ('page.html', 'Normal page'), + ('plugin_with_sekizai.html', 'Plugin with sekizai'), + ), } diff --git a/tox.ini b/tox.ini index 948fefe4b..869192e29 100644 --- a/tox.ini +++ b/tox.ini @@ -9,19 +9,14 @@ skip_missing_interpreters=True commands = {env:COMMAND:python} setup.py test deps = + -r{toxinidir}/test_requirements.txt cms33: https://github.com/divio/django-cms/archive/release/3.3.x.zip cms34: https://github.com/divio/django-cms/archive/release/3.4.x.zip cms35: https://github.com/divio/django-cms/archive/develop.zip - https://github.com/nephila/djangocms-helper/archive/develop.zip django18: django<1.9 django19: django<1.10 django110: django<1.11 django111: django<2.0 - Pillow - html5lib>=0.999999 - coverage - djangocms-picture>=2.0.2 - djangocms-link>=2.0.1 [testenv:pep8] deps = flake8 From ae7abddd8f6db2bb4d18dee375e49de56de4042a Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Fri, 5 Jan 2018 17:28:58 -0200 Subject: [PATCH 05/10] Fix FIXMEs --- djangocms_text_ckeditor/cms_plugins.py | 3 ++- test_settings.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index 54568dd99..583ef3443 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -8,6 +8,7 @@ from cms.plugin_pool import plugin_pool from cms.utils.placeholder import get_toolbar_plugin_struct from cms.utils.urlutils import admin_reverse +from django.conf import settings as django_settings from django.conf.urls import url from django.contrib.admin.utils import unquote from django.core import signing @@ -202,7 +203,7 @@ def do_post_copy(self, instance, source_map): @staticmethod def get_translation_content(field, plugin_data): def _render_plugin_with_content(obj, match): - field = 'label' # FIXME: Get field via DJANGOCMS_TRANSLATIONS_CONF + field = django_settings.DJANGOCMS_TRANSLATIONS_CONF[obj.plugin_type]['text_field_child_label'] content = getattr(obj, field) return plugin_to_tag(obj, content) diff --git a/test_settings.py b/test_settings.py index 6071c8b97..d297b11c2 100644 --- a/test_settings.py +++ b/test_settings.py @@ -84,6 +84,10 @@ def gettext(s): ('page.html', 'Normal page'), ('plugin_with_sekizai.html', 'Plugin with sekizai'), ), + 'DJANGOCMS_TRANSLATIONS_CONF': { + 'Bootstrap3ButtonCMSPlugin': {'text_field_child_label': 'label'}, + 'DummyLinkPlugin': {'text_field_child_label': 'label'}, + }, } From d44cb66a73f43d533311f85f115938f2a5c19e0e Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Fri, 5 Jan 2018 17:51:48 -0200 Subject: [PATCH 06/10] Fix FIXMEs --- djangocms_text_ckeditor/cms_plugins.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index 583ef3443..6202a5301 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -import json from distutils.version import LooseVersion +import json +import re import cms from cms.models import CMSPlugin @@ -31,6 +32,7 @@ from .forms import ActionTokenValidationForm, DeleteOnCancelForm, RenderPluginForm, TextForm from .models import Text from .utils import ( + OBJ_ADMIN_RE_PATTERN, plugin_tags_to_admin_html, plugin_tags_to_id_list, plugin_tags_to_user_html, @@ -213,11 +215,15 @@ def _render_plugin_with_content(obj, match): @staticmethod def get_translation_children_content(content, plugin): - import bs4 # FIXME: Try to stick to ckeditor utils. - soup = bs4.BeautifulSoup(content, 'html.parser') + def _rreplace(text, old, new, count): + return new.join(text.rsplit(old, count)) + + OBJ_ADMIN_RE_PATTERN_WITH_CONTENT = _rreplace(OBJ_ADMIN_RE_PATTERN, '.*?', '(?P.*?)', 1) + data = [x.groups() for x in re.finditer(OBJ_ADMIN_RE_PATTERN_WITH_CONTENT, content)] + data = {int(k): v for k, v in data} return { - subplugin_id: soup.find('cms-plugin', id=subplugin_id).text + subplugin_id: data[subplugin_id] for subplugin_id in plugin_tags_to_id_list(content) } From 0a189f3628685da1775e6cf6cb46341f061b7cbe Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Thu, 11 Jan 2018 12:22:38 -0200 Subject: [PATCH 07/10] Update according to @czpython suggestions --- djangocms_text_ckeditor/cms_plugins.py | 18 ++++------ .../test_app/migrations/0001_initial.py | 2 -- djangocms_text_ckeditor/test_app/models.py | 6 ---- djangocms_text_ckeditor/tests/test_plugin.py | 34 +++++++++++++------ djangocms_text_ckeditor/utils.py | 1 + test_requirements.txt | 4 ++- 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/djangocms_text_ckeditor/cms_plugins.py b/djangocms_text_ckeditor/cms_plugins.py index 6202a5301..85b3f9a6d 100644 --- a/djangocms_text_ckeditor/cms_plugins.py +++ b/djangocms_text_ckeditor/cms_plugins.py @@ -9,7 +9,6 @@ from cms.plugin_pool import plugin_pool from cms.utils.placeholder import get_toolbar_plugin_struct from cms.utils.urlutils import admin_reverse -from django.conf import settings as django_settings from django.conf.urls import url from django.contrib.admin.utils import unquote from django.core import signing @@ -32,7 +31,7 @@ from .forms import ActionTokenValidationForm, DeleteOnCancelForm, RenderPluginForm, TextForm from .models import Text from .utils import ( - OBJ_ADMIN_RE_PATTERN, + OBJ_ADMIN_WITH_CONTENT_RE_PATTERN, plugin_tags_to_admin_html, plugin_tags_to_id_list, plugin_tags_to_user_html, @@ -203,9 +202,10 @@ def do_post_copy(self, instance, source_map): self.model.objects.filter(pk=instance.pk).update(body=new_text) @staticmethod - def get_translation_content(field, plugin_data): + def get_translation_export_content(field, plugin_data): def _render_plugin_with_content(obj, match): - field = django_settings.DJANGOCMS_TRANSLATIONS_CONF[obj.plugin_type]['text_field_child_label'] + from djangocms_translations.utils import get_text_field_child_label + field = get_text_field_child_label(obj.plugin_type) content = getattr(obj, field) return plugin_to_tag(obj, content) @@ -214,13 +214,9 @@ def _render_plugin_with_content(obj, match): return content, subplugins_within_this_content @staticmethod - def get_translation_children_content(content, plugin): - def _rreplace(text, old, new, count): - return new.join(text.rsplit(old, count)) - - OBJ_ADMIN_RE_PATTERN_WITH_CONTENT = _rreplace(OBJ_ADMIN_RE_PATTERN, '.*?', '(?P.*?)', 1) - data = [x.groups() for x in re.finditer(OBJ_ADMIN_RE_PATTERN_WITH_CONTENT, content)] - data = {int(k): v for k, v in data} + def set_translation_import_content(content, plugin): + data = [x.groups() for x in re.finditer(OBJ_ADMIN_WITH_CONTENT_RE_PATTERN, content)] + data = {int(pk): value for pk, value in data} return { subplugin_id: data[subplugin_id] diff --git a/djangocms_text_ckeditor/test_app/migrations/0001_initial.py b/djangocms_text_ckeditor/test_app/migrations/0001_initial.py index d04db2a8a..dbe30c9e7 100644 --- a/djangocms_text_ckeditor/test_app/migrations/0001_initial.py +++ b/djangocms_text_ckeditor/test_app/migrations/0001_initial.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals from django.db import migrations, models -import django.db.models.deletion from djangocms_text_ckeditor.fields import HTMLField @@ -14,7 +13,6 @@ class Migration(migrations.Migration): migrations.CreateModel( name='DummyLink', fields=[ - ('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='test_app_dummylink', serialize=False, to='cms.CMSPlugin')), ('label', models.TextField()), ], options={ diff --git a/djangocms_text_ckeditor/test_app/models.py b/djangocms_text_ckeditor/test_app/models.py index ba31618e7..08378e724 100644 --- a/djangocms_text_ckeditor/test_app/models.py +++ b/djangocms_text_ckeditor/test_app/models.py @@ -12,12 +12,6 @@ class SimpleText(models.Model): @python_2_unicode_compatible class DummyLink(CMSPlugin): - cmsplugin_ptr = models.OneToOneField( - CMSPlugin, - on_delete=models.CASCADE, - related_name='%(app_label)s_%(class)s', - parent_link=True, - ) label = models.TextField() class Meta: diff --git a/djangocms_text_ckeditor/tests/test_plugin.py b/djangocms_text_ckeditor/tests/test_plugin.py index f7d7d4d92..2a5601f1c 100644 --- a/djangocms_text_ckeditor/tests/test_plugin.py +++ b/djangocms_text_ckeditor/tests/test_plugin.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- import copy +import importlib import json import re +import unittest from cms.api import add_plugin, create_page, create_title from cms.models import CMSPlugin, Page, Title @@ -14,8 +16,6 @@ from django.utils.html import escape from django.utils.http import urlencode, urlunquote -from djangocms_transfer.exporter import export_page - from djangocms_text_ckeditor.cms_plugins import TextPlugin from djangocms_text_ckeditor.models import Text from djangocms_text_ckeditor.utils import ( @@ -26,6 +26,15 @@ from .base import BaseTestCase +def _dependencies_are_installed(*modules): + for module in modules: + try: + importlib.import_module(module) + except ImportError: + return False + return True + + class PluginActionsTestCase(BaseTestCase): def get_custom_admin_url(self, plugin_class, name): @@ -794,6 +803,10 @@ def test_text_plugin_xss(self): self.assertEqual(self.reload(plugin).body, '
divcontent
acontent') +@unittest.skipIf( + not(_dependencies_are_installed('djangocms_transfer', 'djangocms_translations')), + 'Optional dependencies for tests are not installed.' +) class DjangoCMSTranslationsIntegrationTestCase(BaseTestCase): def setUp(self): super(DjangoCMSTranslationsIntegrationTestCase, self).setUp() @@ -801,6 +814,7 @@ def setUp(self): self.placeholder = self.page.placeholders.get(slot='content') def _export_page(self): + from djangocms_transfer.exporter import export_page return json.loads(export_page(self.page, 'en')) def test_textfield_without_children(self): @@ -808,12 +822,12 @@ def test_textfield_without_children(self): add_plugin(self.placeholder, 'TextPlugin', 'en', body=raw_content) plugin = self._export_page()[0]['plugins'][0] - result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + result, children_included_in_this_content = TextPlugin.get_translation_export_content('body', plugin['data']) self.assertEquals(result, raw_content) self.assertEquals(children_included_in_this_content, []) - result = TextPlugin.get_translation_children_content(result, plugin) + result = TextPlugin.set_translation_import_content(result, plugin) self.assertDictEqual(result, {}) def test_textfield_with_children(self): @@ -827,7 +841,7 @@ def test_textfield_with_children(self): parent.save() plugin = self._export_page()[0]['plugins'][0] - result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + result, children_included_in_this_content = TextPlugin.get_translation_export_content('body', plugin['data']) expected = ( parent_body @@ -836,7 +850,7 @@ def test_textfield_with_children(self): self.assertEquals(result, expected) self.assertEquals(children_included_in_this_content, [child1.pk]) - result = TextPlugin.get_translation_children_content(result, plugin) + result = TextPlugin.set_translation_import_content(result, plugin) self.assertDictEqual(result, {child1.pk: 'CLICK ON LINK1'}) def test_textfield_with_multiple_children(self): @@ -853,7 +867,7 @@ def test_textfield_with_multiple_children(self): parent.save() plugin = self._export_page()[0]['plugins'][0] - result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + result, children_included_in_this_content = TextPlugin.get_translation_export_content('body', plugin['data']) expected = ( parent_body @@ -863,7 +877,7 @@ def test_textfield_with_multiple_children(self): self.assertEquals(result, expected) self.assertEquals(children_included_in_this_content, [child1.pk, child2.pk]) - result = TextPlugin.get_translation_children_content(result, plugin) + result = TextPlugin.set_translation_import_content(result, plugin) self.assertDictEqual(result, {child1.pk: 'CLICK ON LINK1', child2.pk: 'CLICK ON LINK2'}) def test_textfield_with_multiple_children_one_deleted(self): @@ -883,7 +897,7 @@ def test_textfield_with_multiple_children_one_deleted(self): child1.delete() - result, children_included_in_this_content = TextPlugin.get_translation_content('body', plugin['data']) + result, children_included_in_this_content = TextPlugin.get_translation_export_content('body', plugin['data']) expected = ( '

Please to go to link1 ' @@ -893,5 +907,5 @@ def test_textfield_with_multiple_children_one_deleted(self): self.assertEquals(result, expected) self.assertEquals(children_included_in_this_content, [child2.pk]) - result = TextPlugin.get_translation_children_content(result, plugin) + result = TextPlugin.set_translation_import_content(result, plugin) self.assertDictEqual(result, {child2.pk: 'CLICK ON LINK2'}) diff --git a/djangocms_text_ckeditor/utils.py b/djangocms_text_ckeditor/utils.py index ac562e04b..f17143080 100644 --- a/djangocms_text_ckeditor/utils.py +++ b/djangocms_text_ckeditor/utils.py @@ -14,6 +14,7 @@ OBJ_ADMIN_RE_PATTERN = r'.*?' +OBJ_ADMIN_WITH_CONTENT_RE_PATTERN = r'(?P.*?)' OBJ_ADMIN_RE = re.compile(OBJ_ADMIN_RE_PATTERN, flags=re.DOTALL) diff --git a/test_requirements.txt b/test_requirements.txt index 81ec46b63..65078dcbb 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -10,4 +10,6 @@ tox>=2.9.1 coverage>=4.4.2 flake8>=3.0.4 --e git+ssh://git@github.com/divio/djangocms-transfer.git@master#egg=djangocms-transfer +# In order to run skipped tests uncomment the next lines: +# -e git+ssh://git@github.com/divio/djangocms-transfer.git@master#egg=djangocms-transfer +# -e git+ssh://git@github.com/divio/djangocms-translations.git@master#egg=djangocms-translations From e513215fdeab0875d2997227845bb521047dab72 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Thu, 11 Jan 2018 16:10:38 -0200 Subject: [PATCH 08/10] Avoid creating migrations for test app --- Makefile | 5 +++ djangocms_text_ckeditor/html.py | 7 ---- djangocms_text_ckeditor/settings.py | 4 ++- .../test_app/migrations/0001_initial.py | 34 ------------------- .../test_app/migrations/__init__.py | 0 test_settings.py | 21 +++++++++++- 6 files changed, 28 insertions(+), 43 deletions(-) create mode 100644 Makefile delete mode 100644 djangocms_text_ckeditor/test_app/migrations/0001_initial.py delete mode 100644 djangocms_text_ckeditor/test_app/migrations/__init__.py diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..6ea9769b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + flake8 djangocms_text_ckeditor --max-line-length=120 --ignore=E731 --exclude=.*,*/migrations/*,*/static/*,*__init__* + coverage erase + coverage run setup.py test + coverage report diff --git a/djangocms_text_ckeditor/html.py b/djangocms_text_ckeditor/html.py index ca176b2d0..023d5be53 100644 --- a/djangocms_text_ckeditor/html.py +++ b/djangocms_text_ckeditor/html.py @@ -140,10 +140,3 @@ def img_data_to_plugin(filename, image, parent_plugin, width=None, height=None): ) func = getattr(module, func_name) return func(filename, image, parent_plugin, width=width, height=height) - - -if __name__ == "__main__": - extract_images("""

- sada dadad asdas dsasd

""", object()) - extract_images("""

- sada dadad asdas dsasd

""", object()) diff --git a/djangocms_text_ckeditor/settings.py b/djangocms_text_ckeditor/settings.py index 74505f636..962414cf4 100644 --- a/djangocms_text_ckeditor/settings.py +++ b/djangocms_text_ckeditor/settings.py @@ -30,7 +30,9 @@ TEXT_ADDITIONAL_PROTOCOLS = getattr(settings, 'TEXT_ADDITIONAL_PROTOCOLS', ()) TEXT_CKEDITOR_CONFIGURATION = getattr(settings, 'TEXT_CKEDITOR_CONFIGURATION', None) TEXT_HTML_SANITIZE = getattr(settings, 'TEXT_HTML_SANITIZE', True) -TEXT_CKEDITOR_BASE_PATH = getattr(settings, 'TEXT_CKEDITOR_BASE_PATH', urljoin(settings.STATIC_URL, 'djangocms_text_ckeditor/ckeditor/')) +TEXT_CKEDITOR_BASE_PATH = getattr( + settings, 'TEXT_CKEDITOR_BASE_PATH', urljoin(settings.STATIC_URL, 'djangocms_text_ckeditor/ckeditor/') +) TEXT_AUTO_HYPHENATE = getattr(settings, 'TEXT_AUTO_HYPHENATE', True) TEXT_PLUGIN_NAME = getattr(settings, 'TEXT_PLUGIN_NAME', _("Text")) TEXT_PLUGIN_MODULE_NAME = getattr(settings, 'TEXT_PLUGIN_MODULE_NAME', _("Generic")) diff --git a/djangocms_text_ckeditor/test_app/migrations/0001_initial.py b/djangocms_text_ckeditor/test_app/migrations/0001_initial.py deleted file mode 100644 index dbe30c9e7..000000000 --- a/djangocms_text_ckeditor/test_app/migrations/0001_initial.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.8 on 2018-01-03 18:23 -from __future__ import unicode_literals - -from django.db import migrations, models -from djangocms_text_ckeditor.fields import HTMLField - - -class Migration(migrations.Migration): - initial = True - - operations = [ - migrations.CreateModel( - name='DummyLink', - fields=[ - ('label', models.TextField()), - ], - options={ - 'abstract': False, - }, - bases=('cms.cmsplugin',), - ), - - migrations.CreateModel( - name='SimpleText', - fields=[ - ('text', HTMLField(blank=True)), - ], - options={ - 'abstract': False, - }, - bases=(models.Model,), - ), - ] diff --git a/djangocms_text_ckeditor/test_app/migrations/__init__.py b/djangocms_text_ckeditor/test_app/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/test_settings.py b/test_settings.py index d297b11c2..8adeb8179 100644 --- a/test_settings.py +++ b/test_settings.py @@ -1,10 +1,26 @@ # -*- coding: utf-8 -*- from tempfile import mkdtemp +import sys def gettext(s): return s + +class DisableMigrations(dict): + def __contains__(self, item): + return True + + def __getitem__(self, item): + from distutils.version import LooseVersion + import django + DJANGO_1_9 = LooseVersion(django.get_version()) < LooseVersion('1.10') + if DJANGO_1_9: + return 'notmigrations' + else: + return None + + HELPER_SETTINGS = { 'INSTALLED_APPS': [ 'easy_thumbnails', @@ -12,7 +28,6 @@ def gettext(s): 'mptt', 'djangocms_picture', 'djangocms_link', - 'djangocms_text_ckeditor.test_app', ], 'LANGUAGE_CODE': 'en', 'LANGUAGES': ( @@ -89,11 +104,15 @@ def gettext(s): 'DummyLinkPlugin': {'text_field_child_label': 'label'}, }, } +if 'test' in sys.argv: + HELPER_SETTINGS['MIGRATION_MODULES'] = DisableMigrations() + HELPER_SETTINGS['INSTALLED_APPS'].append('djangocms_text_ckeditor.test_app') def run(): from djangocms_helper import runner runner.cms('djangocms_text_ckeditor') + if __name__ == '__main__': run() From 01cd9034ac55906b36d5a692f818bdc37c52746f Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Thu, 11 Jan 2018 16:53:44 -0200 Subject: [PATCH 09/10] Change the way we skip tests based on missing modules --- djangocms_text_ckeditor/tests/test_plugin.py | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/djangocms_text_ckeditor/tests/test_plugin.py b/djangocms_text_ckeditor/tests/test_plugin.py index 2a5601f1c..d82af2a72 100644 --- a/djangocms_text_ckeditor/tests/test_plugin.py +++ b/djangocms_text_ckeditor/tests/test_plugin.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import copy -import importlib import json import re import unittest @@ -16,6 +15,18 @@ from django.utils.html import escape from django.utils.http import urlencode, urlunquote +try: + from djangocms_transfer.exporter import export_page + HAS_DJANGOCMS_TRANSFER = True +except ImportError: + HAS_DJANGOCMS_TRANSFER = False + +try: + import djangocms_translations # noqa + HAS_DJANGOCMS_TRANSLATIONS = True +except ImportError: + HAS_DJANGOCMS_TRANSLATIONS = False + from djangocms_text_ckeditor.cms_plugins import TextPlugin from djangocms_text_ckeditor.models import Text from djangocms_text_ckeditor.utils import ( @@ -26,15 +37,6 @@ from .base import BaseTestCase -def _dependencies_are_installed(*modules): - for module in modules: - try: - importlib.import_module(module) - except ImportError: - return False - return True - - class PluginActionsTestCase(BaseTestCase): def get_custom_admin_url(self, plugin_class, name): @@ -803,8 +805,8 @@ def test_text_plugin_xss(self): self.assertEqual(self.reload(plugin).body, '
divcontent
acontent') -@unittest.skipIf( - not(_dependencies_are_installed('djangocms_transfer', 'djangocms_translations')), +@unittest.skipUnless( + HAS_DJANGOCMS_TRANSLATIONS and HAS_DJANGOCMS_TRANSFER and X, 'Optional dependencies for tests are not installed.' ) class DjangoCMSTranslationsIntegrationTestCase(BaseTestCase): @@ -814,7 +816,6 @@ def setUp(self): self.placeholder = self.page.placeholders.get(slot='content') def _export_page(self): - from djangocms_transfer.exporter import export_page return json.loads(export_page(self.page, 'en')) def test_textfield_without_children(self): From b5cda862abc0748d2ff1db96f4a22cfda0dfdeb3 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Mon, 15 Jan 2018 11:30:25 -0200 Subject: [PATCH 10/10] Remove the X of question --- djangocms_text_ckeditor/tests/test_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_text_ckeditor/tests/test_plugin.py b/djangocms_text_ckeditor/tests/test_plugin.py index d82af2a72..5a88b3af3 100644 --- a/djangocms_text_ckeditor/tests/test_plugin.py +++ b/djangocms_text_ckeditor/tests/test_plugin.py @@ -806,7 +806,7 @@ def test_text_plugin_xss(self): @unittest.skipUnless( - HAS_DJANGOCMS_TRANSLATIONS and HAS_DJANGOCMS_TRANSFER and X, + HAS_DJANGOCMS_TRANSLATIONS and HAS_DJANGOCMS_TRANSFER, 'Optional dependencies for tests are not installed.' ) class DjangoCMSTranslationsIntegrationTestCase(BaseTestCase):