diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 99eab1c7c..50d020086 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -17,6 +17,7 @@ Changelog * Fix: Prevent explorer view from crashing when page model definitions are missing, allowing the offending pages to be deleted (Matt Westcott) * Fix: Hide the userbar from printed page representation (Eugene Morozov) * Fix: Prevent the page editor footer content from collapsing into two lines unnecessarily (Jack Paine) + * Fix: StructBlock values no longer render HTML templates as their `str` representation, to prevent infinite loops in debugging / logging tools (Matt Westcott) 1.11.1 (07.07.2017) diff --git a/docs/releases/1.12.rst b/docs/releases/1.12.rst index 5fccd8fad..ac2c76821 100644 --- a/docs/releases/1.12.rst +++ b/docs/releases/1.12.rst @@ -37,6 +37,7 @@ Bug fixes * Prevent explorer view from crashing when page model definitions are missing, allowing the offending pages to be deleted (Matt Westcott) * Hide the userbar from printed page representation (Eugene Morozov) * Prevent the page editor footer content from collapsing into two lines unnecessarily (Jack Paine) + * StructBlock values no longer render HTML templates as their ``str`` representation, to prevent infinite loops in debugging / logging tools (Matt Westcott) Upgrade considerations ====================== diff --git a/wagtail/wagtailcore/blocks/struct_block.py b/wagtail/wagtailcore/blocks/struct_block.py index e0263b26b..d87c1d958 100644 --- a/wagtail/wagtailcore/blocks/struct_block.py +++ b/wagtail/wagtailcore/blocks/struct_block.py @@ -9,7 +9,6 @@ from django.forms.utils import ErrorList from django.template.loader import render_to_string # Must be imported from Django so we get the new implementation of with_metaclass from django.utils import six -from django.utils.encoding import python_2_unicode_compatible from django.utils.functional import cached_property from django.utils.html import format_html, format_html_join @@ -194,13 +193,12 @@ class StructBlock(six.with_metaclass(DeclarativeSubBlocksMetaclass, BaseStructBl pass -@python_2_unicode_compatible # provide equivalent __unicode__ and __str__ methods on Py2 class StructValue(collections.OrderedDict): def __init__(self, block, *args): super(StructValue, self).__init__(*args) self.block = block - def __str__(self): + def __html__(self): return self.block.render(self) def render_as_block(self, context=None): diff --git a/wagtail/wagtailcore/tests/test_blocks.py b/wagtail/wagtailcore/tests/test_blocks.py index af654909e..e99493be8 100644 --- a/wagtail/wagtailcore/tests/test_blocks.py +++ b/wagtail/wagtailcore/tests/test_blocks.py @@ -1316,16 +1316,35 @@ class TestStructBlock(SimpleTestCase): def test_render_structvalue(self): """ - The string representation of a StructValue should use the block's template + The HTML representation of a StructValue should use the block's template + """ + block = SectionBlock() + value = block.to_python({'title': 'Hello', 'body': 'italic world'}) + result = value.__html__() + self.assertEqual(result, """

Hello

italic world
""") + + # value.render_as_block() should be equivalent to value.__html__() + result = value.render_as_block() + self.assertEqual(result, """

Hello

italic world
""") + + def test_str_structvalue(self): + """ + The str() representation of a StructValue should NOT render the template, as that's liable + to cause an infinite loop if any debugging / logging code attempts to log the fact that + it rendered a template with this object in the context: + https://github.com/wagtail/wagtail/issues/2874 + https://github.com/jazzband/django-debug-toolbar/issues/950 """ block = SectionBlock() value = block.to_python({'title': 'Hello', 'body': 'italic world'}) result = str(value) - self.assertEqual(result, """

Hello

italic world
""") - - # value.render_as_block() should be equivalent to str(value) - result = value.render_as_block() - self.assertEqual(result, """

Hello

italic world
""") + self.assertNotIn('

', result) + # The expected rendering should correspond to the native representation of an OrderedDict: + # "StructValue([('title', u'Hello'), ('body', )])" + # - give or take some quoting differences between Python versions + self.assertIn('StructValue', result) + self.assertIn('title', result) + self.assertIn('Hello', result) def test_render_structvalue_with_extra_context(self): block = SectionBlock()