From 71f0d7fed35217ca374346a926c57494e01ae63d Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 14 May 2024 08:07:59 +0200 Subject: [PATCH 1/4] Revert "Removed default plugin creation for placeholders (#6468)" This reverts commit eef5cbbfe3b4875b9b00db60611171d700c29ced. --- cms/tests/test_permmod.py | 22 +++++++++ cms/tests/test_placeholder.py | 86 +++++++++++++++++++++++++++++++++-- cms/tests/test_rendering.py | 1 + cms/utils/plugins.py | 47 +++++++++++++++++++ 4 files changed, 153 insertions(+), 3 deletions(-) diff --git a/cms/tests/test_permmod.py b/cms/tests/test_permmod.py index a0337a18812..06b9ae2c933 100644 --- a/cms/tests/test_permmod.py +++ b/cms/tests/test_permmod.py @@ -139,6 +139,28 @@ def test_slave_cannot_add_page_to_root(self): response = self.client.get(self.get_page_add_uri('en')) self.assertEqual(response.status_code, 403) + @override_settings( + CMS_PLACEHOLDER_CONF={ + 'col_left': { + 'default_plugins': [ + { + 'plugin_type': 'TextPlugin', + 'values': { + 'body': 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Culpa, repellendus, delectus, quo quasi ullam inventore quod quam aut voluptatum aliquam voluptatibus harum officiis officia nihil minus unde accusamus dolorem repudiandae.' + }, + }, + ] + }, + }, + ) + def test_default_plugins(self): + with self.login_user_context(self.user_slave): + self.assertEqual(CMSPlugin.objects.count(), 0) + response = self.client.get(self.slave_page.get_absolute_url(), {'edit': 1}) + self.assertEqual(response.status_code, 200) + self.assertEqual(CMSPlugin.objects.count(), 1) + + def test_super_can_add_plugin(self): self._add_plugin(self.user_super, page=self.slave_page) diff --git a/cms/tests/test_placeholder.py b/cms/tests/test_placeholder.py index dbcb68887e0..412b3f0b83c 100644 --- a/cms/tests/test_placeholder.py +++ b/cms/tests/test_placeholder.py @@ -607,7 +607,14 @@ def test_get_placeholder_conf(self): 'main': { 'name': 'main content', 'plugins': ['TextPlugin', 'LinkPlugin'], - 'require_parent': False, + 'default_plugins': [ + { + 'plugin_type': 'TextPlugin', + 'values': { + 'body': '

Some default text

' + }, + }, + ], }, 'layout/home.html main': { 'name': 'main content with FilerImagePlugin and limit', @@ -642,8 +649,8 @@ def test_get_placeholder_conf(self): returned = get_placeholder_conf('excluded_plugins', 'main', 'layout/other.html') self.assertEqual(returned, TEST_CONF['layout/other.html main']['excluded_plugins']) # test grandparent inherited value - returned = get_placeholder_conf('require_parent', 'main', 'layout/other.html') - self.assertEqual(returned, TEST_CONF['main']['require_parent']) + returned = get_placeholder_conf('default_plugins', 'main', 'layout/other.html') + self.assertEqual(returned, TEST_CONF['main']['default_plugins']) # test generic configuration returned = get_placeholder_conf('plugins', 'something') self.assertEqual(returned, TEST_CONF[None]['plugins']) @@ -738,6 +745,79 @@ def test_placeholder_field_dynamic_slot_update(self): self.assertEqual(old_placeholder_1_plugin_count, current_placeholder_1_plugin_count) self.assertEqual(old_placeholder_2_plugin_count, current_placeholder_2_plugin_count) + def test_plugins_prepopulate(self): + """ Tests prepopulate placeholder configuration """ + + conf = { + 'col_left': { + 'default_plugins' : [ + { + 'plugin_type':'TextPlugin', + 'values':{'body':'

en default body 1

'}, + }, + { + 'plugin_type':'TextPlugin', + 'values':{'body':'

en default body 2

'}, + }, + ] + }, + } + with self.settings(CMS_PLACEHOLDER_CONF=conf): + page = create_page('page_en', 'col_two.html', 'en') + placeholder = page.get_placeholders("en").get(slot='col_left') + context = SekizaiContext() + context['request'] = self.get_request(language="en", page=page) + # Our page should have "en default body 1" AND "en default body 2" + content = _render_placeholder(placeholder, context) + self.assertRegexpMatches(content, "^

en default body 1

\s*

en default body 2

$") + + def test_plugins_children_prepopulate(self): + """ + Validate a default textplugin with a nested default link plugin + """ + + conf = { + 'col_left': { + 'default_plugins': [ + { + 'plugin_type': 'TextPlugin', + 'values': { + 'body': '

body %(_tag_child_1)s and %(_tag_child_2)s

' + }, + 'children': [ + { + 'plugin_type': 'LinkPlugin', + 'values': { + 'name': 'django', + 'external_link': 'https://www.djangoproject.com/' + }, + }, + { + 'plugin_type': 'LinkPlugin', + 'values': { + 'name': 'django-cms', + 'external_link': 'https://www.django-cms.org' + }, + }, + ] + }, + ] + }, + } + + with self.settings(CMS_PLACEHOLDER_CONF=conf): + page = create_page('page_en', 'col_two.html', 'en') + placeholder = page.get_placeholders("en").get(slot='col_left') + context = SekizaiContext() + context['request'] = self.get_request(language="en", page=page) + _render_placeholder(placeholder, context) + plugins = placeholder.get_plugins_list() + self.assertEqual(len(plugins), 3) + self.assertEqual(plugins[0].plugin_type, 'TextPlugin') + self.assertEqual(plugins[1].plugin_type, 'LinkPlugin') + self.assertEqual(plugins[2].plugin_type, 'LinkPlugin') + self.assertTrue(plugins[1].parent == plugins[2].parent and plugins[1].parent == plugins[0]) + def test_placeholder_pk_thousands_format(self): page = create_page("page", "nav_playground.html", "en") title = page.get_content_obj("en") diff --git a/cms/tests/test_rendering.py b/cms/tests/test_rendering.py index b542db75bbe..62ac442ca31 100644 --- a/cms/tests/test_rendering.py +++ b/cms/tests/test_rendering.py @@ -723,6 +723,7 @@ def test_inherit_placeholder_override(self): r = self.render(self.test_page5) self.assertEqual(r, '|' + self.test_data5['text_main'] + '|' + self.test_data5['text_sub']) + @override_settings(CMS_PLACEHOLDER_CONF={None: {'language_fallback': False}}) def test_inherit_placeholder_queries(self): with self.assertNumQueries(FuzzyInt(6, 10)): r = self.render(self.test_page2) diff --git a/cms/utils/plugins.py b/cms/utils/plugins.py index df3bbe36e46..473f98b55f0 100644 --- a/cms/utils/plugins.py +++ b/cms/utils/plugins.py @@ -3,6 +3,8 @@ from collections import OrderedDict, defaultdict, deque from copy import deepcopy from functools import lru_cache +from itertools import starmap +from operator import itemgetter from django.utils.encoding import force_str from django.utils.translation import gettext as _ @@ -12,6 +14,7 @@ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool from cms.utils import get_language_from_request +from cms.utils.permissions import has_plugin_permission from cms.utils.placeholder import get_placeholder_conf logger = logging.getLogger(__name__) @@ -91,6 +94,9 @@ def assign_plugins(request, placeholders, template=None, lang=None): .objects .filter(placeholder__in=placeholders, language=lang) ) + if not plugins: + # Create default plugins if enabled + plugins = create_default_plugins(request, placeholders, template, lang) plugins = downcast_plugins(plugins, placeholders, request=request) # split the plugins up by placeholder @@ -112,6 +118,47 @@ def assign_plugins(request, placeholders, template=None, lang=None): placeholder._plugins_cache = layered_plugins +def create_default_plugins(request, placeholders, template, lang): + """ + Create all default plugins for the given ``placeholders`` if they have + a "default_plugins" configuration value in settings. + return all plugins, children, grandchildren (etc.) created + """ + from cms.api import add_plugin + + def _create_default_plugins(placeholder, confs, parent=None): + """ + Auxillary function that builds all of a placeholder's default plugins + at the current level and drives the recursion down the tree. + Returns the plugins at the current level along with all descendants. + """ + plugins, descendants = [], [] + addable_confs = (conf for conf in confs + if has_plugin_permission(request.user, + conf['plugin_type'], 'add')) + for conf in addable_confs: + plugin = add_plugin(placeholder, conf['plugin_type'], lang, + target=parent, **conf['values']) + if 'children' in conf: + args = placeholder, conf['children'], plugin + descendants += _create_default_plugins(*args) + plugin.notify_on_autoadd(request, conf) + plugins.append(plugin) + if parent: + parent.notify_on_autoadd_children(request, conf, plugins) + return plugins + descendants + + unfiltered_confs = ((ph, get_placeholder_conf('default_plugins', + ph.slot, template)) + for ph in placeholders) + # Empty confs must be filtered before filtering on add permission + mutable_confs = ((ph, default_plugin_confs) + for ph, default_plugin_confs + in filter(itemgetter(1), unfiltered_confs) + if ph.has_change_permission(request.user)) + return sum(starmap(_create_default_plugins, mutable_confs), []) + + def get_plugins_as_layered_tree(plugins): """ Given an iterable of plugins ordered by position, From ebfdcfcb740c9147c225bead32b2d6f9b8feff98 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 14 May 2024 10:26:13 +0200 Subject: [PATCH 2/4] Fix: Only change structure board endpoint in edit mode --- cms/static/cms/js/modules/cms.structureboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cms/static/cms/js/modules/cms.structureboard.js b/cms/static/cms/js/modules/cms.structureboard.js index d8012e08efd..24ef10589c5 100644 --- a/cms/static/cms/js/modules/cms.structureboard.js +++ b/cms/static/cms/js/modules/cms.structureboard.js @@ -181,7 +181,7 @@ class StructureBoard { const width = this.ui.window[0].innerWidth; const BREAKPOINT = 1024; - if (width > BREAKPOINT && !this.condensed) { + if (width > BREAKPOINT && !this.condensed || CMS.settings.mode === 'edit') { this._makeCondensed(); } @@ -683,7 +683,7 @@ class StructureBoard { if (CMS.settings.mode === 'structure') { history.replaceState({}, '', CMS.config.settings.structure); - this.ui.html.addClass('cms-overflow'); + $('html.cms-structure-mode-structure').addClass('cms-overflow'); } this.ui.container.css('right', 0); From 465a4eb800ecc8085b4ed54652a800d7084c075d Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 14 May 2024 10:35:50 +0200 Subject: [PATCH 3/4] Fix typo --- cms/static/cms/js/modules/cms.structureboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/static/cms/js/modules/cms.structureboard.js b/cms/static/cms/js/modules/cms.structureboard.js index 24ef10589c5..98600c97e32 100644 --- a/cms/static/cms/js/modules/cms.structureboard.js +++ b/cms/static/cms/js/modules/cms.structureboard.js @@ -181,7 +181,7 @@ class StructureBoard { const width = this.ui.window[0].innerWidth; const BREAKPOINT = 1024; - if (width > BREAKPOINT && !this.condensed || CMS.settings.mode === 'edit') { + if (width > BREAKPOINT && !this.condensed && CMS.settings.mode === 'edit') { this._makeCondensed(); } From 606eb3bace6461291ee28d3782207f88ab260a06 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 14 May 2024 10:41:15 +0200 Subject: [PATCH 4/4] Revert "Revert "Removed default plugin creation for placeholders (#6468)"" This reverts commit 71f0d7fed35217ca374346a926c57494e01ae63d. --- cms/tests/test_permmod.py | 22 --------- cms/tests/test_placeholder.py | 86 ++--------------------------------- cms/tests/test_rendering.py | 1 - cms/utils/plugins.py | 47 ------------------- 4 files changed, 3 insertions(+), 153 deletions(-) diff --git a/cms/tests/test_permmod.py b/cms/tests/test_permmod.py index 06b9ae2c933..a0337a18812 100644 --- a/cms/tests/test_permmod.py +++ b/cms/tests/test_permmod.py @@ -139,28 +139,6 @@ def test_slave_cannot_add_page_to_root(self): response = self.client.get(self.get_page_add_uri('en')) self.assertEqual(response.status_code, 403) - @override_settings( - CMS_PLACEHOLDER_CONF={ - 'col_left': { - 'default_plugins': [ - { - 'plugin_type': 'TextPlugin', - 'values': { - 'body': 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Culpa, repellendus, delectus, quo quasi ullam inventore quod quam aut voluptatum aliquam voluptatibus harum officiis officia nihil minus unde accusamus dolorem repudiandae.' - }, - }, - ] - }, - }, - ) - def test_default_plugins(self): - with self.login_user_context(self.user_slave): - self.assertEqual(CMSPlugin.objects.count(), 0) - response = self.client.get(self.slave_page.get_absolute_url(), {'edit': 1}) - self.assertEqual(response.status_code, 200) - self.assertEqual(CMSPlugin.objects.count(), 1) - - def test_super_can_add_plugin(self): self._add_plugin(self.user_super, page=self.slave_page) diff --git a/cms/tests/test_placeholder.py b/cms/tests/test_placeholder.py index 412b3f0b83c..dbcb68887e0 100644 --- a/cms/tests/test_placeholder.py +++ b/cms/tests/test_placeholder.py @@ -607,14 +607,7 @@ def test_get_placeholder_conf(self): 'main': { 'name': 'main content', 'plugins': ['TextPlugin', 'LinkPlugin'], - 'default_plugins': [ - { - 'plugin_type': 'TextPlugin', - 'values': { - 'body': '

Some default text

' - }, - }, - ], + 'require_parent': False, }, 'layout/home.html main': { 'name': 'main content with FilerImagePlugin and limit', @@ -649,8 +642,8 @@ def test_get_placeholder_conf(self): returned = get_placeholder_conf('excluded_plugins', 'main', 'layout/other.html') self.assertEqual(returned, TEST_CONF['layout/other.html main']['excluded_plugins']) # test grandparent inherited value - returned = get_placeholder_conf('default_plugins', 'main', 'layout/other.html') - self.assertEqual(returned, TEST_CONF['main']['default_plugins']) + returned = get_placeholder_conf('require_parent', 'main', 'layout/other.html') + self.assertEqual(returned, TEST_CONF['main']['require_parent']) # test generic configuration returned = get_placeholder_conf('plugins', 'something') self.assertEqual(returned, TEST_CONF[None]['plugins']) @@ -745,79 +738,6 @@ def test_placeholder_field_dynamic_slot_update(self): self.assertEqual(old_placeholder_1_plugin_count, current_placeholder_1_plugin_count) self.assertEqual(old_placeholder_2_plugin_count, current_placeholder_2_plugin_count) - def test_plugins_prepopulate(self): - """ Tests prepopulate placeholder configuration """ - - conf = { - 'col_left': { - 'default_plugins' : [ - { - 'plugin_type':'TextPlugin', - 'values':{'body':'

en default body 1

'}, - }, - { - 'plugin_type':'TextPlugin', - 'values':{'body':'

en default body 2

'}, - }, - ] - }, - } - with self.settings(CMS_PLACEHOLDER_CONF=conf): - page = create_page('page_en', 'col_two.html', 'en') - placeholder = page.get_placeholders("en").get(slot='col_left') - context = SekizaiContext() - context['request'] = self.get_request(language="en", page=page) - # Our page should have "en default body 1" AND "en default body 2" - content = _render_placeholder(placeholder, context) - self.assertRegexpMatches(content, "^

en default body 1

\s*

en default body 2

$") - - def test_plugins_children_prepopulate(self): - """ - Validate a default textplugin with a nested default link plugin - """ - - conf = { - 'col_left': { - 'default_plugins': [ - { - 'plugin_type': 'TextPlugin', - 'values': { - 'body': '

body %(_tag_child_1)s and %(_tag_child_2)s

' - }, - 'children': [ - { - 'plugin_type': 'LinkPlugin', - 'values': { - 'name': 'django', - 'external_link': 'https://www.djangoproject.com/' - }, - }, - { - 'plugin_type': 'LinkPlugin', - 'values': { - 'name': 'django-cms', - 'external_link': 'https://www.django-cms.org' - }, - }, - ] - }, - ] - }, - } - - with self.settings(CMS_PLACEHOLDER_CONF=conf): - page = create_page('page_en', 'col_two.html', 'en') - placeholder = page.get_placeholders("en").get(slot='col_left') - context = SekizaiContext() - context['request'] = self.get_request(language="en", page=page) - _render_placeholder(placeholder, context) - plugins = placeholder.get_plugins_list() - self.assertEqual(len(plugins), 3) - self.assertEqual(plugins[0].plugin_type, 'TextPlugin') - self.assertEqual(plugins[1].plugin_type, 'LinkPlugin') - self.assertEqual(plugins[2].plugin_type, 'LinkPlugin') - self.assertTrue(plugins[1].parent == plugins[2].parent and plugins[1].parent == plugins[0]) - def test_placeholder_pk_thousands_format(self): page = create_page("page", "nav_playground.html", "en") title = page.get_content_obj("en") diff --git a/cms/tests/test_rendering.py b/cms/tests/test_rendering.py index 62ac442ca31..b542db75bbe 100644 --- a/cms/tests/test_rendering.py +++ b/cms/tests/test_rendering.py @@ -723,7 +723,6 @@ def test_inherit_placeholder_override(self): r = self.render(self.test_page5) self.assertEqual(r, '|' + self.test_data5['text_main'] + '|' + self.test_data5['text_sub']) - @override_settings(CMS_PLACEHOLDER_CONF={None: {'language_fallback': False}}) def test_inherit_placeholder_queries(self): with self.assertNumQueries(FuzzyInt(6, 10)): r = self.render(self.test_page2) diff --git a/cms/utils/plugins.py b/cms/utils/plugins.py index 473f98b55f0..df3bbe36e46 100644 --- a/cms/utils/plugins.py +++ b/cms/utils/plugins.py @@ -3,8 +3,6 @@ from collections import OrderedDict, defaultdict, deque from copy import deepcopy from functools import lru_cache -from itertools import starmap -from operator import itemgetter from django.utils.encoding import force_str from django.utils.translation import gettext as _ @@ -14,7 +12,6 @@ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool from cms.utils import get_language_from_request -from cms.utils.permissions import has_plugin_permission from cms.utils.placeholder import get_placeholder_conf logger = logging.getLogger(__name__) @@ -94,9 +91,6 @@ def assign_plugins(request, placeholders, template=None, lang=None): .objects .filter(placeholder__in=placeholders, language=lang) ) - if not plugins: - # Create default plugins if enabled - plugins = create_default_plugins(request, placeholders, template, lang) plugins = downcast_plugins(plugins, placeholders, request=request) # split the plugins up by placeholder @@ -118,47 +112,6 @@ def assign_plugins(request, placeholders, template=None, lang=None): placeholder._plugins_cache = layered_plugins -def create_default_plugins(request, placeholders, template, lang): - """ - Create all default plugins for the given ``placeholders`` if they have - a "default_plugins" configuration value in settings. - return all plugins, children, grandchildren (etc.) created - """ - from cms.api import add_plugin - - def _create_default_plugins(placeholder, confs, parent=None): - """ - Auxillary function that builds all of a placeholder's default plugins - at the current level and drives the recursion down the tree. - Returns the plugins at the current level along with all descendants. - """ - plugins, descendants = [], [] - addable_confs = (conf for conf in confs - if has_plugin_permission(request.user, - conf['plugin_type'], 'add')) - for conf in addable_confs: - plugin = add_plugin(placeholder, conf['plugin_type'], lang, - target=parent, **conf['values']) - if 'children' in conf: - args = placeholder, conf['children'], plugin - descendants += _create_default_plugins(*args) - plugin.notify_on_autoadd(request, conf) - plugins.append(plugin) - if parent: - parent.notify_on_autoadd_children(request, conf, plugins) - return plugins + descendants - - unfiltered_confs = ((ph, get_placeholder_conf('default_plugins', - ph.slot, template)) - for ph in placeholders) - # Empty confs must be filtered before filtering on add permission - mutable_confs = ((ph, default_plugin_confs) - for ph, default_plugin_confs - in filter(itemgetter(1), unfiltered_confs) - if ph.has_change_permission(request.user)) - return sum(starmap(_create_default_plugins, mutable_confs), []) - - def get_plugins_as_layered_tree(plugins): """ Given an iterable of plugins ordered by position,