forked from ungleich-public/dynamicweb
597 lines
26 KiB
Python
597 lines
26 KiB
Python
from collections import deque
|
|
|
|
from django.template import Context
|
|
from django.test.utils import override_settings
|
|
|
|
from cms.api import add_plugin, create_page
|
|
from cms.models import CMSPlugin, Placeholder
|
|
from cms.plugin_rendering import (
|
|
ContentRenderer,
|
|
LegacyRenderer,
|
|
StructureRenderer,
|
|
)
|
|
from cms.test_utils.testcases import CMSTestCase
|
|
from cms.test_utils.util.context_managers import override_placeholder_conf
|
|
from cms.toolbar.toolbar import CMSToolbar
|
|
from cms.toolbar.utils import get_object_edit_url
|
|
|
|
|
|
class TestStructureRenderer(CMSTestCase):
|
|
renderer_class = StructureRenderer
|
|
|
|
def get_renderer(self, path=None, language='en', page=None):
|
|
if page and path is None:
|
|
path = page.get_absolute_url(language)
|
|
request = self.get_request(path, language, page=page)
|
|
return self.renderer_class(request)
|
|
|
|
def test_get_plugin_class_cache(self):
|
|
plugin = CMSPlugin(plugin_type='MultiColumnPlugin')
|
|
renderer = self.get_renderer()
|
|
plugin_class = renderer.get_plugin_class(plugin)
|
|
self.assertIn('MultiColumnPlugin', renderer._cached_plugin_classes)
|
|
self.assertEqual(plugin_class.__name__, 'MultiColumnPlugin')
|
|
self.assertEqual(renderer._cached_plugin_classes['MultiColumnPlugin'], plugin_class)
|
|
|
|
def test_get_placeholder_plugin_menu(self):
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
superuser = self.get_superuser()
|
|
placeholder_1 = cms_page.get_placeholders("en").get(slot='body')
|
|
|
|
with self.login_user_context(superuser):
|
|
renderer = self.get_renderer()
|
|
plugin_menu = renderer.get_placeholder_plugin_menu(placeholder_1)
|
|
expected = '<div class="cms-submenu-item cms-submenu-item-title"><span>Multi Columns</span></div>'
|
|
self.assertTrue(expected in plugin_menu)
|
|
|
|
def test_render_placeholder_toolbar_js(self):
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
renderer = self.get_renderer()
|
|
placeholder = cms_page.get_placeholders("en").get(slot='body')
|
|
content = renderer.get_placeholder_toolbar_js(placeholder)
|
|
|
|
expected_bits = [
|
|
'"MultiColumnPlugin"',
|
|
'"addPluginHelpTitle": "Add plugin to placeholder \\"Body\\""',
|
|
'"name": "Body"',
|
|
f'"placeholder_id": "{placeholder.pk}"',
|
|
]
|
|
|
|
for bit in expected_bits:
|
|
self.assertIn(bit, content)
|
|
|
|
def test_render_placeholder_toolbar_js_escaping(self):
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
renderer = self.get_renderer()
|
|
placeholder = cms_page.get_placeholders("en").get(slot='body')
|
|
|
|
conf = {placeholder.slot: {'name': 'Content-with-dash'}}
|
|
|
|
with override_placeholder_conf(CMS_PLACEHOLDER_CONF=conf):
|
|
content = renderer.get_placeholder_toolbar_js(placeholder)
|
|
|
|
expected_bits = [
|
|
'"MultiColumnPlugin"',
|
|
'"addPluginHelpTitle": "Add plugin to placeholder \\"Content-with-dash\\""',
|
|
'"name": "Content-with-dash"',
|
|
f'"placeholder_id": "{placeholder.pk}"',
|
|
]
|
|
|
|
for bit in expected_bits:
|
|
self.assertIn(bit, content)
|
|
|
|
|
|
class TestContentRenderer(TestStructureRenderer):
|
|
renderer_class = ContentRenderer
|
|
|
|
@override_settings(CMS_PLACEHOLDER_CACHE=True)
|
|
def test_placeholder_cache_enabled(self):
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
request_path = cms_page.get_absolute_url('en')
|
|
renderer = self.get_renderer(request_path, page=cms_page)
|
|
self.assertTrue(renderer.placeholder_cache_is_enabled())
|
|
|
|
@override_settings(CMS_PLACEHOLDER_CACHE=True)
|
|
def test_placeholder_cache_disabled(self):
|
|
with override_settings(CMS_PLACEHOLDER_CACHE=False):
|
|
# Placeholder cache has been explicitly disabled
|
|
renderer = self.get_renderer()
|
|
self.assertFalse(renderer.placeholder_cache_is_enabled())
|
|
|
|
with self.login_user_context(self.get_staff_user_with_no_permissions()):
|
|
# Placeholder cache is disabled for all staff users
|
|
renderer = self.get_renderer()
|
|
self.assertFalse(renderer.placeholder_cache_is_enabled())
|
|
|
|
def test_preload_placeholders_for_page_with_inherit_off(self):
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
placeholder_1 = cms_page.get_placeholders("en").get(slot='body')
|
|
placeholder_1_plugin_1 = add_plugin(
|
|
placeholder_1,
|
|
plugin_type='LinkPlugin',
|
|
language='en',
|
|
name='Link #1',
|
|
external_link='https://www.django-cms.org',
|
|
)
|
|
placeholder_1_plugin_2 = add_plugin(
|
|
placeholder_1,
|
|
plugin_type='LinkPlugin',
|
|
language='en',
|
|
name='Link #2',
|
|
external_link='https://www.django-cms.org',
|
|
)
|
|
placeholder_2 = cms_page.get_placeholders("en").get(slot='right-column')
|
|
placeholder_2_plugin_1 = add_plugin(
|
|
placeholder_2,
|
|
plugin_type='LinkPlugin',
|
|
language='en',
|
|
name='Link #3',
|
|
external_link='https://www.django-cms.org',
|
|
)
|
|
renderer = self.get_renderer(page=cms_page)
|
|
renderer._preload_placeholders_for_page(cms_page)
|
|
|
|
self.assertIn(cms_page.pk, renderer._placeholders_by_page_cache)
|
|
self.assertIn(placeholder_1.slot, renderer._placeholders_by_page_cache[cms_page.pk])
|
|
self.assertIn(placeholder_2.slot, renderer._placeholders_by_page_cache[cms_page.pk])
|
|
|
|
cache = renderer._placeholders_by_page_cache[cms_page.pk]
|
|
|
|
self.assertEqual(cache[placeholder_1.slot], placeholder_1)
|
|
self.assertEqual(
|
|
cache[placeholder_1.slot]._plugins_cache, deque([placeholder_1_plugin_1, placeholder_1_plugin_2])
|
|
)
|
|
self.assertEqual(cache[placeholder_2.slot], placeholder_2)
|
|
self.assertEqual(cache[placeholder_2.slot]._plugins_cache, deque([placeholder_2_plugin_1]))
|
|
|
|
|
|
class TestExceptionCatchers(CMSTestCase):
|
|
renderer_class = ContentRenderer
|
|
|
|
def setUp(self):
|
|
self.user = self.get_superuser()
|
|
|
|
self.cms_page = create_page("page", 'nav_playground.html', "en")
|
|
self.placeholder_1 = self.cms_page.get_placeholders("en").get(slot='body')
|
|
add_plugin(
|
|
self.placeholder_1,
|
|
plugin_type='LinkPlugin',
|
|
language='en',
|
|
name='Link #1',
|
|
external_link='https://www.django-cms.org',
|
|
)
|
|
add_plugin(
|
|
self.placeholder_1,
|
|
plugin_type='BuggyPlugin',
|
|
language='en',
|
|
)
|
|
self.placeholder_2 = self.cms_page.get_placeholders("en").get(slot='right-column')
|
|
self.placeholder_2_plugin_1 = add_plugin(
|
|
self.placeholder_2,
|
|
plugin_type='LinkPlugin',
|
|
language='en',
|
|
name='Link #3',
|
|
external_link='https://www.django-cms.org',
|
|
)
|
|
|
|
self.request = self.get_request(
|
|
get_object_edit_url(self.cms_page.get_admin_content('en')),
|
|
'en',
|
|
page=self.cms_page,
|
|
)
|
|
self.request.toolbar = CMSToolbar(self.request)
|
|
self.renderer = self.renderer_class(self.request)
|
|
|
|
def test_non_existent_plugins_creates_error_log_and_does_not_fail(self):
|
|
self.placeholder_2_plugin_1.plugin_type = 'NonExistingPlugin'
|
|
self.placeholder_2_plugin_1.save()
|
|
with self.assertLogs("cms.utils.plugins", level="ERROR") as logs:
|
|
plugin_context = Context()
|
|
self.renderer.render_placeholder(self.placeholder_2, plugin_context, "en")
|
|
self.assertEqual(len(logs.output), 1)
|
|
self.assertIn("Plugin not installed: NonExistingPlugin", logs.output[0])
|
|
self.placeholder_2_plugin_1.plugin_type = 'LinkPlugin'
|
|
self.placeholder_2_plugin_1.save()
|
|
|
|
def test_exception_in_plugin_render_creates_error_log_and_does_not_fail(self):
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR") as logs:
|
|
plugin_context = Context()
|
|
self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en")
|
|
self.assertEqual(len(logs.output), 1)
|
|
self.assertIn("ZeroDivisionError: division by zero", logs.output[0])
|
|
|
|
def test_exception_in_template_rendering_creates_error_log_and_does_not_fail(self):
|
|
from cms.test_utils.project.pluginapp.plugins.link.cms_plugins import (
|
|
LinkPlugin,
|
|
)
|
|
link_template = LinkPlugin.render_template
|
|
LinkPlugin.render_template = "pluginapp/link/bugs.html"
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR") as logs:
|
|
plugin_context = Context()
|
|
self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en")
|
|
self.assertEqual(len(logs.output), 1)
|
|
self.assertIn("TemplateSyntaxError", logs.output[0])
|
|
self.assertIn("Invalid block tag on line 1", logs.output[0])
|
|
LinkPlugin.render_template = link_template
|
|
|
|
def test_exception_in_plugin_render_shows_error_in_edit_mode(self):
|
|
plugin_context = Context()
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR"):
|
|
markup = self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en", editable=True)
|
|
self.assertTrue(self.renderer.toolbar.edit_mode_active)
|
|
self.assertIn('<div class="cms-rendering-exception">', markup)
|
|
self.assertIn("ZeroDivisionError", markup)
|
|
self.assertIn("division by zero", markup)
|
|
|
|
def test_exception_in_plugin_render_escapes_user_content_in_edit_mode(self):
|
|
# The exception heading interpolates user-controlled data such as the
|
|
# object title (``str(placeholder.source)``). It must be HTML-escaped
|
|
# before being returned as safe markup, otherwise a stored payload
|
|
# would execute in a staff user's browser in edit mode.
|
|
payload = '<img src=x onerror=alert(1)>'
|
|
page_content = self.cms_page.get_admin_content('en')
|
|
page_content.title = payload
|
|
page_content.save()
|
|
# Re-fetch the placeholder so its (generic) ``source`` is reloaded with
|
|
# the updated title rather than the value cached during ``setUp``.
|
|
placeholder = Placeholder.objects.get(pk=self.placeholder_1.pk)
|
|
|
|
plugin_context = Context()
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR"):
|
|
markup = self.renderer.render_placeholder(placeholder, plugin_context, "en", editable=True)
|
|
|
|
self.assertIn('<div class="cms-rendering-exception">', markup)
|
|
self.assertNotIn(payload, markup)
|
|
self.assertIn('<img src=x onerror=alert(1)>', markup)
|
|
|
|
def test_exception_in_plugin_render_is_silent_in_preview_mode(self):
|
|
plugin_context = Context()
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR"):
|
|
markup = self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en", editable=False)
|
|
self.assertEqual('', markup)
|
|
|
|
def test_exception_in_plugin_render_is_silent_in_live_mode(self):
|
|
plugin_context = Context()
|
|
self.request = self.get_request(
|
|
self.cms_page.get_absolute_url('en'),
|
|
'en',
|
|
page=self.cms_page,
|
|
)
|
|
self.request.toolbar = CMSToolbar(self.request)
|
|
self.renderer = self.renderer_class(self.request)
|
|
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR"):
|
|
markup = self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en", editable=False)
|
|
self.assertFalse(self.renderer.toolbar.edit_mode_active)
|
|
self.assertEqual('', markup)
|
|
|
|
@override_settings(CMS_CATCH_PLUGIN_500_EXCEPTION=False)
|
|
def test_exception_in_plugin_render_is_raised__in_live_mode(self):
|
|
plugin_context = Context()
|
|
self.request = self.get_request(
|
|
self.cms_page.get_absolute_url('en'),
|
|
'en',
|
|
page=self.cms_page,
|
|
)
|
|
self.request.toolbar = CMSToolbar(self.request)
|
|
self.renderer = self.renderer_class(self.request)
|
|
|
|
with self.assertRaises(ZeroDivisionError):
|
|
with self.assertLogs("cms.plugin_rendering", level="ERROR"):
|
|
self.renderer.render_placeholder(self.placeholder_1, plugin_context, "en", editable=False)
|
|
|
|
|
|
class TestLegacyRenderer(TestContentRenderer):
|
|
renderer_class = LegacyRenderer
|
|
|
|
|
|
class TestLegacyRendererExceptionCatcher(TestExceptionCatchers):
|
|
renderer_class = LegacyRenderer
|
|
|
|
|
|
class TestValidModelsFiltering(CMSTestCase):
|
|
"""Tests that get_placeholder_toolbar_js only lists plugins valid for the placeholder's source model.
|
|
|
|
We create a placeholder on a non-page model (Example1) and register two temporary plugins:
|
|
- AllowedExamplePlugin: allowed_models explicitly lists the Example1 model.
|
|
- DisallowedExamplePlugin: allowed_models lists a different model path.
|
|
|
|
The toolbar js should include AllowedExamplePlugin but exclude DisallowedExamplePlugin.
|
|
"""
|
|
|
|
def setUp(self):
|
|
from cms.plugin_base import CMSPluginBase
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
self.example = Example1.objects.create(
|
|
char_1="ex1", char_2="ex2", char_3="ex3", char_4="ex4", publish=True
|
|
)
|
|
self.placeholder = self.example.placeholder
|
|
|
|
# dynamic registration of two plugins with differing allowed_models
|
|
class AllowedExamplePlugin(CMSPluginBase):
|
|
name = "AllowedExamplePlugin"
|
|
render_template = "cms/content.html"
|
|
allowed_models = ["placeholderapp.example1"]
|
|
|
|
class DisallowedExamplePlugin(CMSPluginBase):
|
|
name = "DisallowedExamplePlugin"
|
|
render_template = "cms/content.html"
|
|
allowed_models = ["placeholderapp.someothermodel"]
|
|
|
|
self.AllowedExamplePlugin = AllowedExamplePlugin
|
|
self.DisallowedExamplePlugin = DisallowedExamplePlugin
|
|
plugin_pool.register_plugin(AllowedExamplePlugin)
|
|
plugin_pool.register_plugin(DisallowedExamplePlugin)
|
|
|
|
def tearDown(self):
|
|
# Unregister temporary plugins to avoid leaking into other tests
|
|
from cms.plugin_pool import plugin_pool
|
|
plugin_pool.unregister_plugin(self.AllowedExamplePlugin)
|
|
plugin_pool.unregister_plugin(self.DisallowedExamplePlugin)
|
|
|
|
def test_get_placeholder_toolbar_js_allowed_models_filtering(self):
|
|
from cms.plugin_rendering import ContentRenderer
|
|
|
|
request = self.get_request("/", "en")
|
|
renderer = ContentRenderer(request)
|
|
js = renderer.get_placeholder_toolbar_js(self.placeholder)
|
|
# Allowed plugin should appear
|
|
self.assertIn('"AllowedExamplePlugin"', js)
|
|
# Disallowed plugin must not appear
|
|
self.assertNotIn('"DisallowedExamplePlugin"', js)
|
|
# Sanity check: placeholder id present
|
|
self.assertIn(f'"placeholder_id": "{self.placeholder.pk}"', js)
|
|
|
|
def test_get_placeholder_toolbar_js_pagecontent_allowed_models(self):
|
|
from cms.api import create_page
|
|
from cms.plugin_base import CMSPluginBase
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.plugin_rendering import ContentRenderer
|
|
|
|
# Create a CMS page placeholder as source model cms.pagecontent
|
|
cms_page = create_page("page", 'nav_playground.html', "en")
|
|
page_placeholder = cms_page.get_placeholders("en").get(slot='body')
|
|
|
|
class PageOnlyTmpPlugin(CMSPluginBase):
|
|
name = "PageOnlyTmpPlugin"
|
|
render_template = "cms/content.html"
|
|
allowed_models = ["cms.pagecontent"]
|
|
|
|
try:
|
|
plugin_pool.register_plugin(PageOnlyTmpPlugin)
|
|
|
|
# For a page placeholder, the page-only plugin should be present
|
|
request = self.get_request(cms_page.get_absolute_url('en'), 'en', page=cms_page)
|
|
renderer = ContentRenderer(request)
|
|
js_page = renderer.get_placeholder_toolbar_js(page_placeholder)
|
|
self.assertIn('"PageOnlyTmpPlugin"', js_page)
|
|
|
|
# For a non-page model placeholder, it must be excluded
|
|
request2 = self.get_request('/', 'en')
|
|
renderer2 = ContentRenderer(request2)
|
|
js_obj = renderer2.get_placeholder_toolbar_js(self.placeholder)
|
|
self.assertNotIn('"PageOnlyTmpPlugin"', js_obj)
|
|
finally:
|
|
plugin_pool.unregister_plugin(PageOnlyTmpPlugin)
|
|
|
|
|
|
class TestAllowedPluginsFiltering(CMSTestCase):
|
|
"""Tests for model-level plugin filtering via the allowed_plugins attribute.
|
|
|
|
Tests that get_placeholder_toolbar_js and get_all_plugins_for_model respect the
|
|
allowed_plugins property on models. This is a model-level restriction that works
|
|
in combination with the plugin-level allowed_models attribute.
|
|
|
|
Model-level filtering (allowed_plugins on model):
|
|
- None (default): All plugins are allowed (subject to plugin's allowed_models)
|
|
- List of plugin names: Only those plugins are allowed
|
|
- Empty list []: No plugins are allowed
|
|
|
|
We create a placeholder on a model with an allowed_plugins property and verify that only
|
|
those plugins appear in the toolbar and are returned by get_all_plugins_for_model.
|
|
"""
|
|
|
|
def setUp(self):
|
|
from cms.plugin_base import CMSPluginBase
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
# Create an Example1 instance
|
|
self.example = Example1.objects.create(
|
|
char_1="ex1", char_2="ex2", char_3="ex3", char_4="ex4", publish=True
|
|
)
|
|
self.placeholder = self.example.placeholder
|
|
|
|
# Register temporary test plugins
|
|
class AllowedPluginA(CMSPluginBase):
|
|
name = "AllowedPluginA"
|
|
render_template = "cms/content.html"
|
|
|
|
class AllowedPluginB(CMSPluginBase):
|
|
name = "AllowedPluginB"
|
|
render_template = "cms/content.html"
|
|
|
|
class DisallowedPlugin(CMSPluginBase):
|
|
name = "DisallowedPlugin"
|
|
render_template = "cms/content.html"
|
|
|
|
self.AllowedPluginA = AllowedPluginA
|
|
self.AllowedPluginB = AllowedPluginB
|
|
self.DisallowedPlugin = DisallowedPlugin
|
|
|
|
plugin_pool.register_plugin(AllowedPluginA)
|
|
plugin_pool.register_plugin(AllowedPluginB)
|
|
plugin_pool.register_plugin(DisallowedPlugin)
|
|
|
|
def tearDown(self):
|
|
from cms.plugin_pool import plugin_pool
|
|
plugin_pool.unregister_plugin(self.AllowedPluginA)
|
|
plugin_pool.unregister_plugin(self.AllowedPluginB)
|
|
plugin_pool.unregister_plugin(self.DisallowedPlugin)
|
|
|
|
def test_get_all_plugins_for_model_without_allowed_plugins(self):
|
|
"""Test that models without allowed_plugins (None) return all plugins.
|
|
|
|
When allowed_plugins is None (not defined), no model-level filtering occurs.
|
|
Only plugin-level filtering (allowed_models) is applied.
|
|
"""
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
all_plugins = plugin_pool.get_all_plugins_for_model(Example1)
|
|
plugin_names = [p.__name__ for p in all_plugins]
|
|
|
|
# All three test plugins should be included
|
|
self.assertIn('AllowedPluginA', plugin_names)
|
|
self.assertIn('AllowedPluginB', plugin_names)
|
|
self.assertIn('DisallowedPlugin', plugin_names)
|
|
|
|
def test_get_all_plugins_for_model_with_allowed_plugins(self):
|
|
"""Test that allowed_plugins property applies model-level filtering.
|
|
|
|
When allowed_plugins is a list of plugin names, only those plugins
|
|
(and those passing the allowed_models filter) are returned.
|
|
"""
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
# Temporarily add allowed_plugins to Example1
|
|
Example1.allowed_plugins = ['AllowedPluginA', 'AllowedPluginB']
|
|
# Clear the cache since get_all_plugins_for_model uses @lru_cache
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
try:
|
|
filtered_plugins = plugin_pool.get_all_plugins_for_model(Example1)
|
|
plugin_names = [p.__name__ for p in filtered_plugins]
|
|
|
|
# Only allowed plugins should be included
|
|
self.assertIn('AllowedPluginA', plugin_names)
|
|
self.assertIn('AllowedPluginB', plugin_names)
|
|
self.assertNotIn('DisallowedPlugin', plugin_names)
|
|
finally:
|
|
# Clean up
|
|
delattr(Example1, 'allowed_plugins')
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
def test_get_all_plugins_for_model_with_empty_allowed_plugins(self):
|
|
"""Test that empty allowed_plugins list returns no plugins.
|
|
|
|
Semantic difference:
|
|
- None: No model-level restriction (all plugins allowed subject to allowed_models)
|
|
- []: Explicit restriction to allow no plugins
|
|
- ['PluginA']: Explicit restriction to specific plugins
|
|
"""
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
Example1.allowed_plugins = []
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
try:
|
|
filtered_plugins = plugin_pool.get_all_plugins_for_model(Example1)
|
|
# Empty list should result in no plugins
|
|
self.assertEqual(len(filtered_plugins), 0)
|
|
finally:
|
|
delattr(Example1, 'allowed_plugins')
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
def test_get_placeholder_toolbar_js_with_allowed_plugins(self):
|
|
"""Test that toolbar JS respects model-level allowed_plugins filtering.
|
|
|
|
The toolbar should only show plugins that pass both the plugin's allowed_models
|
|
filter and the model's allowed_plugins filter.
|
|
"""
|
|
from cms.plugin_rendering import ContentRenderer
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
Example1.allowed_plugins = ['AllowedPluginA', 'AllowedPluginB']
|
|
|
|
try:
|
|
request = self.get_request("/", "en")
|
|
renderer = ContentRenderer(request)
|
|
js = renderer.get_placeholder_toolbar_js(self.placeholder)
|
|
|
|
# Allowed plugins should appear
|
|
self.assertIn('"AllowedPluginA"', js)
|
|
self.assertIn('"AllowedPluginB"', js)
|
|
# Disallowed plugin must not appear
|
|
self.assertNotIn('"DisallowedPlugin"', js)
|
|
finally:
|
|
delattr(Example1, 'allowed_plugins')
|
|
from cms.plugin_pool import plugin_pool
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
def test_allowed_plugins_with_nonexistent_plugin(self):
|
|
"""Test that nonexistent plugins in allowed_plugins are safely ignored.
|
|
|
|
If allowed_plugins contains plugin names that don't exist or aren't registered,
|
|
they are silently ignored without raising errors.
|
|
"""
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
Example1.allowed_plugins = ['AllowedPluginA', 'NonExistentPlugin']
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
try:
|
|
filtered_plugins = plugin_pool.get_all_plugins_for_model(Example1)
|
|
plugin_names = [p.__name__ for p in filtered_plugins]
|
|
|
|
# Only the existing allowed plugin should be included
|
|
self.assertIn('AllowedPluginA', plugin_names)
|
|
self.assertNotIn('AllowedPluginB', plugin_names)
|
|
self.assertNotIn('DisallowedPlugin', plugin_names)
|
|
# No error should be raised for nonexistent plugin
|
|
finally:
|
|
delattr(Example1, 'allowed_plugins')
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
def test_allowed_plugins_combined_with_allowed_models(self):
|
|
"""Test that both allowed_plugins and allowed_models filters work together.
|
|
|
|
For a plugin to be available on a model, it must pass both filters:
|
|
1. Plugin-level: If plugin.allowed_models is set, model must be in the list
|
|
2. Model-level: If model.allowed_plugins is set, plugin must be in the list
|
|
|
|
If either filter excludes the plugin, it won't be available.
|
|
"""
|
|
from cms.plugin_base import CMSPluginBase
|
|
from cms.plugin_pool import plugin_pool
|
|
from cms.test_utils.project.placeholderapp.models import Example1
|
|
|
|
# Register a plugin with allowed_models restriction
|
|
class RestrictedModelPlugin(CMSPluginBase):
|
|
name = "RestrictedModelPlugin"
|
|
render_template = "cms/content.html"
|
|
allowed_models = ["placeholderapp.example1"]
|
|
|
|
class WrongModelPlugin(CMSPluginBase):
|
|
name = "WrongModelPlugin"
|
|
render_template = "cms/content.html"
|
|
allowed_models = ["placeholderapp.someothermodel"]
|
|
|
|
plugin_pool.register_plugin(RestrictedModelPlugin)
|
|
plugin_pool.register_plugin(WrongModelPlugin)
|
|
|
|
Example1.allowed_plugins = ['AllowedPluginA', 'RestrictedModelPlugin', 'WrongModelPlugin']
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|
|
|
|
try:
|
|
filtered_plugins = plugin_pool.get_all_plugins_for_model(Example1)
|
|
plugin_names = [p.__name__ for p in filtered_plugins]
|
|
|
|
# AllowedPluginA should be included (in allowed_plugins, no model restriction)
|
|
self.assertIn('AllowedPluginA', plugin_names)
|
|
# RestrictedModelPlugin should be included (in allowed_plugins AND correct allowed_models)
|
|
self.assertIn('RestrictedModelPlugin', plugin_names)
|
|
# WrongModelPlugin should be excluded (in allowed_plugins but wrong allowed_models)
|
|
self.assertNotIn('WrongModelPlugin', plugin_names)
|
|
# AllowedPluginB should be excluded (not in allowed_plugins)
|
|
self.assertNotIn('AllowedPluginB', plugin_names)
|
|
finally:
|
|
delattr(Example1, 'allowed_plugins')
|
|
plugin_pool.unregister_plugin(RestrictedModelPlugin)
|
|
plugin_pool.unregister_plugin(WrongModelPlugin)
|
|
plugin_pool.get_all_plugins_for_model.cache_clear()
|