-
Notifications
You must be signed in to change notification settings - Fork 20
Description
When you include a RichTextWithFootnotesBlock in a StreamBlock definition, things work great! This is because StreamBlock.to_python returns a StreamValue, which has a render_as_block() method that feeds back into StreamBlock.render_basic() to do the actual rendering... which calls the render() method of each sub-block, ultimately leading to RichTextWithFootnotesBlock.replace_footnote_tags() doing its thing.
When you include a RichTextWithFootnotesBlock as a sub-block of a StructBlock, things work out a little differently. The tendency is to do the following in StructBlock templates:
{% load wagtailcore_tags %}
{{ value.caption|richtext }}
This definitely won't trigger RichTextWithFootnotesBlock.replace_footnote_tags(), because we're basically just taking the value from the block and rendering it directly.
I thought that changing all instances of the above would resolve the problem:
{% load wagtailcore_tags %}
{% include_block value.caption %}
However, that didn't do the job because {% include_block %} only does anything special with the value if it has a render_as_block() method (like StreamValue does). If not, the value itself is used as the output value, which isn't really what we want.
What we want is for RichTextWithFootnotesBlock.render() to always be used in rendering, and in order to do that, RichTextWithFootnotesBlock needs to return something other than an instance of RichText... we need something with a render_as_block() method (like StreamValue does), so that we can always bring things back to RichTextWithFootnotesBlock.render(). Something like this worked in a project I am currently working on:
class RichTextBlockValue:
"""
Value type returned by CustomRichTextBlockWithFootnotes.to_python().
When rendered in a StructBlock, FieldBlock values are usually rendered directly, bypassing the
footnote replacement logic that is usually triggered by RichTextBlockWithFootnotes.render().
If the block instead returns a value with a 'render_as_block()' method, we can always reliably
hand things back to RichTextBlockWithFootnotes.render() to take care of rendering, where the
footnote replacement logic will kick-in.
"""
def __init__(self, block, value):
self.block = block
self.source = value
self.value = RichText(value)
def render_as_block(self, context=None):
return self.block.render(self, context=context)
def __html__(self):
return self.block.render(self)
def __str__(self):
return self.__html__()
class CustomRichTextWithFootnotesBlock(RichTextWithFootnotesBlock):
def to_python(self, value):
if isinstance(value, RichTextBlockValue):
return value
return RichTextBlockValue(self, value)
def render(self, value, context=None):
if isinstance(value, RichTextBlockValue):
value = value.value
return super().render(value, context=context)
With these changes, as long as I use {% include_block %} in all StructBlock templates, the footnotes are replaced successfully, wherever they are used:
{% load wagtailcore_tags %}
{% include_block value.caption %}