from django.core.cache import cache from django.http.response import Http404 from django.test.utils import override_settings from django.urls import reverse from sekizai.context import SekizaiContext from cms import plugin_rendering from cms.api import add_plugin, create_page from cms.cache.placeholder import get_placeholder_cache from cms.models import EmptyPageContent, Page, Placeholder from cms.plugin_rendering import PluginContext from cms.test_utils.project.placeholderapp.models import Example1 from cms.test_utils.testcases import CMSTestCase from cms.test_utils.util.context_managers import override_placeholder_conf from cms.test_utils.util.fuzzy_int import FuzzyInt from cms.toolbar.toolbar import CMSToolbar from cms.toolbar.utils import get_object_edit_url from cms.views import details TEMPLATE_NAME = 'tests/rendering/base.html' INHERIT_TEMPLATE_NAME = 'tests/rendering/inherit.html' INHERIT_WITH_OR_TEMPLATE_NAME = 'tests/rendering/inherit_with_or.html' def sample_plugin_processor(instance, placeholder, rendered_content, original_context): original_context_var = original_context['original_context_var'] return f'{rendered_content}|test_plugin_processor_ok|{instance.body}|{placeholder.slot}|{original_context_var}' def sample_plugin_context_processor(instance, placeholder, original_context): content = 'test_plugin_context_processor_ok|' + instance.body + '|' + \ placeholder.slot + '|' + original_context['original_context_var'] return { 'test_plugin_context_processor': content, } @override_settings( CMS_TEMPLATES=[ (TEMPLATE_NAME, TEMPLATE_NAME), ('extra_context.html', 'extra_context.html') ], ) class RenderingEmptyTestCase(CMSTestCase): def setUp(self): super().setUp() self.test_data = { 'title': 'RenderingTestCase-title', 'slug': 'renderingtestcase-slug', 'reverse_id': 'renderingtestcase-reverse-id', } self.user = self.get_superuser() self.client.force_login(self.user) self.page = create_page( self.test_data['title'], TEMPLATE_NAME, 'en', slug=self.test_data['slug'], created_by=self.user, reverse_id=self.test_data['reverse_id'] ) # we need an empty pagecontent for this. self.page.pagecontent_set.all().delete() def test_page_with_empty_pagecontent(self): """ A page with EmptyPageContent except root returns 404 when you try to view it. """ page_content = self.page.get_content_obj('en', force_reload=True) self.assertEqual(isinstance(page_content, EmptyPageContent), True) with self.assertRaises(Http404): path = self.page.get_path('en') request = self.get_request(path=path, page=self.page) details(request, slug=path) def test_page_with_empty_pagecontent_for_root_url(self): """ The root page with EmptyPageContent redirects to PageContent's changelist """ page_content = self.page.get_content_obj('en', force_reload=True) self.assertEqual(isinstance(page_content, EmptyPageContent), True) request = self.get_request(path='/en/', page=self.page) response = details(request, slug=self.page.get_path('en')) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('admin:cms_pagecontent_changelist')) @override_settings( CMS_TEMPLATES=[ (TEMPLATE_NAME, TEMPLATE_NAME), (INHERIT_TEMPLATE_NAME, INHERIT_TEMPLATE_NAME), (INHERIT_WITH_OR_TEMPLATE_NAME, INHERIT_WITH_OR_TEMPLATE_NAME), ('extra_context.html', 'extra_context.html') ], ) class RenderingTestCase(CMSTestCase): def setUp(self): super().setUp() self.test_user = self.get_superuser() with self.login_user_context(self.test_user): self.test_data = { 'title': 'RenderingTestCase-title', 'slug': 'renderingtestcase-slug', 'reverse_id': 'renderingtestcase-reverse-id', 'text_main': 'RenderingTestCase-main', 'text_sub': 'RenderingTestCase-sub', } self.test_data2 = { 'title': 'RenderingTestCase-title2', 'slug': 'RenderingTestCase-slug2', 'reverse_id': 'renderingtestcase-reverse-id2', } self.test_data3 = { 'title': 'RenderingTestCase-title3', 'slug': 'RenderingTestCase-slug3', 'reverse_id': 'renderingtestcase-reverse-id3', 'text_sub': 'RenderingTestCase-sub3', } self.test_data4 = { 'title': 'RenderingTestCase-title3', 'no_extra': 'no extra var!', 'placeholderconf': {'extra_context': {'extra_context': {'extra_var': 'found extra var'}}}, 'extra': 'found extra var', } self.test_data5 = { 'title': 'RenderingTestCase-title5', 'slug': 'RenderingTestCase-slug5', 'reverse_id': 'renderingtestcase-reverse-id5', 'text_main': 'RenderingTestCase-main-page5', 'text_sub': 'RenderingTestCase-sub5', } self.test_data6 = { 'title': 'RenderingTestCase-title6', 'slug': 'RenderingTestCase-slug6', 'reverse_id': 'renderingtestcase-reverse-id6', 'text_sub': 'RenderingTestCase-sub6', } self.test_data7 = { 'title': 'RenderingTestCase-title7', 'slug': 'RenderingTestCase-slug7', 'reverse_id': 'renderingtestcase-reverse-id7', 'text_sub': 'RenderingTestCase-sub7', } self.test_data8 = { 'title': 'RenderingTestCase-title8', 'slug': 'RenderingTestCase-slug8', 'reverse_id': 'renderingtestcase-reverse-id8', 'text_sub': 'RenderingTestCase-sub8', } self.test_data9 = { 'title': 'RenderingTestCase-title9', 'slug': 'RenderingTestCase-slug9', 'reverse_id': 'renderingtestcase-reverse-id9', 'text_sub': 'RenderingTestCase-sub9', } self.test_data10 = { 'title': 'RenderingTestCase-title10', 'slug': 'RenderingTestCase-slug10', 'reverse_id': 'renderingtestcase-reverse-id10', 'text_sub': 'RenderingTestCase-sub10', } self.insert_test_content() def insert_test_content(self): # Insert a page p = create_page(self.test_data['title'], TEMPLATE_NAME, 'en', slug=self.test_data['slug'], created_by=self.test_user, reverse_id=self.test_data['reverse_id']) # Placeholders have been inserted on post_save signal: self.test_placeholders = {} for placeholder in p.get_placeholders('en'): self.test_placeholders[placeholder.slot] = placeholder # Insert some test Text plugins add_plugin(self.test_placeholders['main'], 'TextPlugin', 'en', body=self.test_data['text_main']) add_plugin(self.test_placeholders['sub'], 'TextPlugin', 'en', body=self.test_data['text_sub']) # Insert another page that is not the home page p2 = create_page(self.test_data2['title'], INHERIT_TEMPLATE_NAME, 'en', parent=p, slug=self.test_data2['slug'], reverse_id=self.test_data2['reverse_id']) # Insert another page that is not the home page p3 = create_page(self.test_data3['title'], INHERIT_TEMPLATE_NAME, 'en', slug=self.test_data3['slug'], parent=p2, reverse_id=self.test_data3['reverse_id']) # Placeholders have been inserted on post_save signal: self.test_placeholders3 = {} for placeholder in p3.get_placeholders('en'): self.test_placeholders3[placeholder.slot] = placeholder # # Insert some test Text plugins add_plugin(self.test_placeholders3['sub'], 'TextPlugin', 'en', body=self.test_data3['text_sub']) # Insert another page that is not the home p4 = create_page(self.test_data4['title'], 'extra_context.html', 'en', parent=p) # Placeholders have been inserted on post_save signal: self.test_placeholders4 = {} for placeholder in p4.get_placeholders('en'): self.test_placeholders4[placeholder.slot] = placeholder # Insert some test plugins add_plugin(self.test_placeholders4['extra_context'], 'ExtraContextPlugin', 'en') # Insert another page that is not the home page p5 = create_page( self.test_data5['title'], INHERIT_TEMPLATE_NAME, 'en', parent=p, slug=self.test_data5['slug'], reverse_id=self.test_data5['reverse_id'] ) # Placeholders have been inserted on post_save signal: self.test_placeholders5 = {} for placeholder in p5.get_placeholders('en'): self.test_placeholders5[placeholder.slot] = placeholder # # Insert some test Text plugins add_plugin(self.test_placeholders5['sub'], 'TextPlugin', 'en', body=self.test_data5['text_sub']) add_plugin(self.test_placeholders5['main'], 'TextPlugin', 'en', body=self.test_data5['text_main']) # Insert another page that is not the home page p6 = create_page( self.test_data6['title'], INHERIT_TEMPLATE_NAME, 'en', slug=self.test_data6['slug'], parent=p5, reverse_id=self.test_data6['reverse_id'] ) # Placeholders have been inserted on post_save signal: self.test_placeholders6 = {} for placeholder in p6.get_placeholders('en'): self.test_placeholders6[placeholder.slot] = placeholder # # Insert some test Text plugins add_plugin(self.test_placeholders6['sub'], 'TextPlugin', 'en', body=self.test_data6['text_sub']) p7 = create_page( self.test_data7['title'], INHERIT_TEMPLATE_NAME, 'en', slug=self.test_data7['slug'], parent=p6, reverse_id=self.test_data7['reverse_id'] ) p8 = create_page( self.test_data8['title'], INHERIT_WITH_OR_TEMPLATE_NAME, 'en', slug=self.test_data8['slug'], parent=p7, reverse_id=self.test_data8['reverse_id'] ) p9 = create_page( self.test_data9['title'], INHERIT_WITH_OR_TEMPLATE_NAME, 'en', slug=self.test_data9['slug'], reverse_id=self.test_data9['reverse_id'] ) p10 = create_page( self.test_data10['title'], INHERIT_WITH_OR_TEMPLATE_NAME, 'en', slug=self.test_data10['slug'], parent=p9, reverse_id=self.test_data10['reverse_id']) # Reload test pages self.test_page = self.reload(p) self.test_page2 = self.reload(p2) self.test_page3 = self.reload(p3) self.test_page4 = self.reload(p4) self.test_page5 = self.reload(p5) self.test_page6 = self.reload(p6) self.test_page7 = self.reload(p7) self.test_page8 = self.reload(p8) self.test_page9 = self.reload(p9) self.test_page10 = self.reload(p10) def strip_rendered(self, content): return content.strip().replace("\n", "") @override_settings(CMS_TEMPLATES=[(TEMPLATE_NAME, '')]) def render(self, page, template=None, context_vars=None, request=None): if request is None: request = self.get_request(page=page) request.toolbar = CMSToolbar(request) if context_vars is None: context_vars = {} if template is None: template = page.get_template() template_obj = self.get_template(template) output = template_obj.render(context_vars, request) else: output = self.render_template_obj(template, context_vars, request) return self.strip_rendered(output) @override_settings(CMS_TEMPLATES=[(TEMPLATE_NAME, '')]) def test_details_view(self): """ Tests that the `detail` view is working. """ response = details(self.get_request(page=self.test_page), self.test_page.get_path('en')) response.render() r = self.strip_rendered(response.content.decode('utf8')) self.assertEqual(r, '|' + self.test_data['text_main'] + '|' + self.test_data['text_sub'] + '|') def test_getting_placeholders(self): """ContentRenderer._get_content_object uses toolbar to return placeholders of a page""" request = self.get_request(page=self.test_page) request.toolbar = CMSToolbar(request) wrong_content_obj = self.test_page2.get_content_obj("en") wrong_content_obj.page = self.test_page request.toolbar.set_object(wrong_content_obj) content_renderer = self.get_content_renderer(request) placeholders = content_renderer._get_content_object(self.test_page) wrong_content_obj.page = self.test_page2 self.assertEqual(len(placeholders), 2) @override_settings( CMS_PLUGIN_PROCESSORS=('cms.tests.test_rendering.sample_plugin_processor',), CMS_PLUGIN_CONTEXT_PROCESSORS=('cms.tests.test_rendering.sample_plugin_context_processor',), ) def test_processors(self): """ Tests that plugin processors and plugin context processors can be defined in settings and are working and that extra plugin context processors can be passed to PluginContext. """ from djangocms_text.cms_plugins import TextPlugin from cms.plugin_pool import plugin_pool instance = self.test_placeholders['main'].get_plugins('en').first().get_bound_plugin() load_from_string = self.load_template_from_string @plugin_pool.register_plugin class ProcessorTestPlugin(TextPlugin): name = "Test Plugin" def get_render_template(self, context, instance, placeholder): t = '{% load cms_tags %}' + \ '{{ body }}|test_passed_plugin_context_processor_ok|' \ '{{ test_plugin_context_processor }}' return load_from_string(t) def test_passed_plugin_context_processor(instance, placeholder, context): return {'test_passed_plugin_context_processor': 'test_passed_plugin_context_processor_ok'} instance.plugin_type = 'ProcessorTestPlugin' instance._inst = instance context = PluginContext( {'original_context_var': 'original_context_var_ok', "request": None}, instance, self.test_placeholders['main'], processors=(test_passed_plugin_context_processor,) ) plugin_rendering._standard_processors = {} content_renderer = self.get_content_renderer() r = content_renderer.render_plugin(instance, context, self.test_placeholders['main']) expected = ( f"{self.test_data['text_main']}|test_passed_plugin_context_processor_ok|test_plugin_context_processor_ok|" f"{self.test_data['text_main']}|main|original_context_var_ok|test_plugin_processor_ok|" f"{self.test_data['text_main']}|main|original_context_var_ok" ) self.assertEqual(r, expected) plugin_rendering._standard_processors = {} def test_placeholder(self): """ Tests the {% placeholder %} templatetag. """ r = self.render(self.test_page) self.assertEqual(r, '|' + self.test_data['text_main'] + '|' + self.test_data['text_sub'] + '|') def test_placeholder_extra_context(self): t = '{% load cms_tags %}{% placeholder "extra_context" %}' r = self.render(self.test_page4, template=t) self.assertEqual(r, self.test_data4['no_extra']) cache.clear() with override_placeholder_conf(CMS_PLACEHOLDER_CONF=self.test_data4['placeholderconf']): r = self.render(self.test_page4, template=t) self.assertEqual(r, self.test_data4['extra']) def test_placeholder_extra_context_template_specific(self): """Template-specific CMS_PLACEHOLDER_CONF keys must be respected (regression #8649).""" t = '{% load cms_tags %}{% placeholder "extra_context" %}' template_specific_conf = { 'extra_context.html extra_context': self.test_data4['placeholderconf']['extra_context'], } r = self.render(self.test_page4, template=t) self.assertEqual(r, self.test_data4['no_extra']) cache.clear() with override_placeholder_conf(CMS_PLACEHOLDER_CONF=template_specific_conf): r = self.render(self.test_page4, template=t) self.assertEqual(r, self.test_data4['extra']) def test_placeholder_extra_context_template_specific_takes_precedence(self): """Template-specific CMS_PLACEHOLDER_CONF key takes precedence over generic slot key.""" t = '{% load cms_tags %}{% placeholder "extra_context" %}' combined_conf = { 'extra_context': {'extra_context': {'extra_var': 'generic var'}}, 'extra_context.html extra_context': self.test_data4['placeholderconf']['extra_context'], } cache.clear() with override_placeholder_conf(CMS_PLACEHOLDER_CONF=combined_conf): r = self.render(self.test_page4, template=t) self.assertEqual(r, self.test_data4['extra']) # template-specific wins, not 'generic var' def test_placeholder_or(self): """ Tests the {% placeholder %} templatetag. """ t = '{% load cms_tags %}' + \ '|{% placeholder "empty" or %}No content{% endplaceholder %}' r = self.render(self.test_page, template=t) self.assertEqual(r, '|No content') def test_placeholder_or_in_edit_mode(self): """ Tests the {% placeholder or %} templatetag in edit mode. """ t = '{% load cms_tags %}' + \ '|{% placeholder "empty" or %}No content{% endplaceholder %}' superuser = self.get_superuser() with self.login_user_context(superuser): endpoint = self.test_page.get_absolute_url() + '?edit' request = self.get_request(endpoint, page=self.test_page) request.session['cms_edit'] = True request.toolbar = CMSToolbar(request) renderer = self.get_content_renderer(request) context = SekizaiContext() context['cms_content_renderer'] = renderer placeholder = self.test_page.get_placeholders('en').get(slot='empty') expected = renderer.render_placeholder( placeholder, context=context, language='en', page=self.test_page, editable=True, ) expected = f'|{expected}No content' rendered = self.render(self.test_page, template=t, request=request) self.assertEqual(rendered, self.strip_rendered(expected)) def test_render_placeholder_tag(self): """ Tests the {% render_placeholder %} templatetag. """ render_placeholder_body = "I'm the render placeholder body" ex1 = Example1(char_1="char_1", char_2="char_2", char_3="char_3", char_4="char_4") ex1.save() add_plugin(ex1.placeholder, "TextPlugin", "en", body=render_placeholder_body) t = '''{% extends "base.html" %} {% load cms_tags %} {% block content %}
Ultimate fallback
|') def test_inherit_placeholder_with_or(self): # Tests that the "or" statement used in a {% placeholder %} # declaration is used as the last fallback when inheritance # fails to find content. expected_8 = '|' + self.test_data5['text_main'] + '|' self.assertEqual(self.render(self.test_page8), expected_8) expected_10 = '|Ultimate fallback
|' self.assertEqual(self.render(self.test_page10), expected_10) def test_inherit_placeholder_override(self): # Tests that the user can override the inherited content # in a placeholder by adding plugins to the inherited placeholder. # a page whose parent has 'main' placeholder inherits from the parent, not ancestors 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) self.assertEqual(r, '|' + self.test_data['text_main'] + '|') def test_render_placeholder_toolbar(self): placeholder = Placeholder() placeholder.slot = 'test' placeholder.pk = placeholder.id = 99 with self.login_user_context(self.get_superuser()): page_content = self.get_pagecontent_obj(self.test_page) request = self.get_request(get_object_edit_url(page_content)) request.session = {} request.toolbar = CMSToolbar(request) renderer = self.get_content_renderer(request) context = SekizaiContext() context['request'] = request classes = [ "cms-placeholder-%s" % placeholder.pk, 'cms-placeholder', ] output = renderer.render_placeholder(placeholder, context, 'en', editable=True) for cls in classes: self.assertTrue(cls in output, f'{cls!r} is not in {output!r}') def test_render_plugin_toolbar_markup(self): """ Ensures that the edit-mode markup is correct """ page = self.test_page placeholder = page.get_placeholders('en').get(slot='main') parent_plugin = add_plugin(placeholder, 'SolarSystemPlugin', 'en') child_plugin_1 = add_plugin(placeholder, 'PlanetPlugin', 'en', target=parent_plugin) child_plugin_2 = add_plugin(placeholder, 'PlanetPlugin', 'en', target=parent_plugin) parent_plugin.child_plugin_instances = [ child_plugin_1, child_plugin_2, ] plugins = [ parent_plugin, child_plugin_1, child_plugin_2, ] with self.login_user_context(self.get_superuser()): page_content = self.get_pagecontent_obj(page) request = self.get_request(get_object_edit_url(page_content)) request.session = {} request.toolbar = CMSToolbar(request) context = SekizaiContext() context['request'] = request content_renderer = request.toolbar.get_content_renderer() output = content_renderer.render_plugin( instance=parent_plugin, context=context, placeholder=placeholder, editable=True, ) tag_format = ('') for plugin in plugins: start_tag = tag_format.format(plugin.pk, plugin.placeholder_id, plugin.position) self.assertIn(start_tag, output) @override_settings( CMS_TEMPLATES=[ (TEMPLATE_NAME, TEMPLATE_NAME), ], ) class RenderedPlaceholderTestCase(CMSTestCase): """Test cases for the RenderedPlaceholder class""" def test_rendered_placeholder_init(self): """Test RenderedPlaceholder initialization""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholder = page.get_placeholders("en").first() rendered_placeholder = RenderedPlaceholder( placeholder=placeholder, language="en", site_id=1, cached=True, editable=False, has_content=True, ) self.assertEqual(rendered_placeholder.placeholder, placeholder) self.assertEqual(rendered_placeholder.language, "en") self.assertEqual(rendered_placeholder.site_id, 1) self.assertTrue(rendered_placeholder.cached) self.assertFalse(rendered_placeholder.editable) self.assertTrue(rendered_placeholder.has_content) def test_rendered_placeholder_equality(self): """Test RenderedPlaceholder equality comparison""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholders = page.get_placeholders("en") placeholder1 = placeholders[0] # Create two RenderedPlaceholder instances with same placeholder # but different parameters rendered1 = RenderedPlaceholder( placeholder=placeholder1, language="en", site_id=1, cached=True, editable=True, ) rendered2 = RenderedPlaceholder( placeholder=placeholder1, language="de", site_id=2, cached=False, editable=False, ) # They should be equal because they represent the same placeholder self.assertEqual(rendered1, rendered2) def test_rendered_placeholder_inequality(self): """Test RenderedPlaceholder inequality comparison""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholders = list(page.get_placeholders("en")) # Need at least 2 placeholders if len(placeholders) < 2: self.skipTest("Template needs at least 2 placeholders") placeholder1 = placeholders[0] placeholder2 = placeholders[1] rendered1 = RenderedPlaceholder( placeholder=placeholder1, language="en", site_id=1, ) rendered2 = RenderedPlaceholder( placeholder=placeholder2, language="en", site_id=1, ) # They should not be equal because they represent different placeholders self.assertNotEqual(rendered1, rendered2) def test_rendered_placeholder_hash(self): """Test RenderedPlaceholder hash functionality""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholder = page.get_placeholders("en").first() rendered1 = RenderedPlaceholder( placeholder=placeholder, language="en", site_id=1, ) rendered2 = RenderedPlaceholder( placeholder=placeholder, language="de", site_id=2, ) # Hash should be the same because it's based on the placeholder self.assertEqual(hash(rendered1), hash(rendered2)) self.assertEqual(hash(rendered1), hash(placeholder)) def test_rendered_placeholder_in_set(self): """Test RenderedPlaceholder can be used in a set""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholder = page.get_placeholders("en").first() rendered1 = RenderedPlaceholder( placeholder=placeholder, language="en", site_id=1, ) rendered2 = RenderedPlaceholder( placeholder=placeholder, language="de", site_id=2, ) # Adding both to a set should result in only one item # because they represent the same placeholder placeholder_set = {rendered1, rendered2} self.assertEqual(len(placeholder_set), 1) def test_rendered_placeholder_default_values(self): """Test RenderedPlaceholder default parameter values""" from cms.plugin_rendering import RenderedPlaceholder page = create_page("Test", TEMPLATE_NAME, "en") placeholder = page.get_placeholders("en").first() # Create with minimal required parameters rendered = RenderedPlaceholder( placeholder=placeholder, language="en", site_id=1, ) # Check default values self.assertFalse(rendered.cached) self.assertFalse(rendered.editable) self.assertFalse(rendered.has_content)