Convert demo StreamField pages to RecipePage data model

pull/385/head
Thibaud Colas 2022-12-14 19:34:21 +00:00
rodzic 8bbf239cf6
commit f3754842d6
9 zmienionych plików z 493 dodań i 379 usunięć

Wyświetl plik

@ -1,54 +1,74 @@
from django import forms
from wagtail.contrib.table_block.blocks import TableBlock
from wagtail.contrib.typed_table_block.blocks import TypedTableBlock
from wagtail.core.blocks import (
CharBlock,
ChoiceBlock,
FloatBlock,
ListBlock,
PageChooserBlock,
RichTextBlock,
StreamBlock,
StructBlock,
TextBlock,
URLBlock,
)
from wagtail.documents.blocks import DocumentChooserBlock
from wagtail.embeds.blocks import EmbedBlock
from wagtail.images.blocks import ImageChooserBlock
from bakerydemo.base.blocks import BlockQuote, HeadingBlock, ImageBlock
class RecipeTableBlock(StructBlock):
title = CharBlock()
description = TextBlock()
table = TypedTableBlock(
class RecipeStepBlock(StructBlock):
text = RichTextBlock(features=["bold", "italic", "link"])
difficulty = ChoiceBlock(
widget=forms.RadioSelect,
choices=[("S", "Small"), ("M", "Medium"), ("L", "Large")],
default="S",
)
class Meta:
template = 'blocks/recipe_step_block.html'
icon = 'tick'
class RecipeStreamBlock(StreamBlock):
"""
Define the custom blocks that `StreamField` will utilize
"""
heading_block = HeadingBlock(group="Content")
paragraph_block = RichTextBlock(
icon="pilcrow", template="blocks/paragraph_block.html", group="Content"
)
block_quote = BlockQuote(group="Content")
table_block = TableBlock(group="Content")
typed_table_block = TypedTableBlock(
[
("text", CharBlock()),
("numeric", FloatBlock()),
("rich_text", RichTextBlock()),
("image", ImageChooserBlock()),
]
],
group="Content",
)
image_block = ImageBlock(group="Media")
embed_block = EmbedBlock(
help_text="Insert an embed URL e.g https://www.youtube.com/watch?v=SGJFWirQ3ks",
icon="media",
template="blocks/embed_block.html",
group="Media",
)
class RecipeStreamBlock(StreamBlock):
page = PageChooserBlock()
embed = EmbedBlock()
image = ImageChooserBlock()
BLOCKS = [
("char", CharBlock()),
(
"choice",
ChoiceBlock(choices=[("M", "Medium"), ("L", "Large"), ("XL", "Extra large")]),
),
("list", ListBlock(child_block=CharBlock())),
("page", PageChooserBlock()),
("text", TextBlock()),
("rich_text", RichTextBlock()),
("url", URLBlock()),
("document", DocumentChooserBlock()),
("embed", EmbedBlock()),
("image", ImageChooserBlock()),
("table", RecipeTableBlock()),
("stream", RecipeStreamBlock()),
]
ingredients_list = ListBlock(
RichTextBlock(features=["bold", "italic", "link"]),
min_num=2,
max_num=10,
icon="list-ol",
group="Cooking",
)
steps_list = ListBlock(
RecipeStepBlock(),
min_num=2,
max_num=10,
icon="tasks",
group="Cooking",
)

Wyświetl plik

@ -1,11 +1,11 @@
# Generated by Django 4.0.8 on 2022-11-17 15:14
# Generated by Django 4.1.4 on 2022-12-20 14:17
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
import wagtail.blocks
import wagtail.contrib.table_block.blocks
import wagtail.contrib.typed_table_block.blocks
import wagtail.documents.blocks
import wagtail.embeds.blocks
import wagtail.fields
import wagtail.images.blocks
@ -16,10 +16,35 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
("base", "0012_person_expire_at_person_expired_and_more"),
("wagtailcore", "0078_referenceindex"),
]
operations = [
migrations.CreateModel(
name="RecipeIndexPage",
fields=[
(
"page_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="wagtailcore.page",
),
),
(
"introduction",
models.TextField(blank=True, help_text="Text to describe the page"),
),
],
options={
"abstract": False,
},
bases=("wagtailcore.page",),
),
migrations.CreateModel(
name="RecipePage",
fields=[
@ -34,169 +59,260 @@ class Migration(migrations.Migration):
to="wagtailcore.page",
),
),
("title_1", models.CharField(default="Title 1", max_length=255)),
(
"section_1",
"date_published",
models.DateField(
blank=True, null=True, verbose_name="Date article published"
),
),
("subtitle", models.CharField(blank=True, max_length=255)),
("introduction", models.TextField(blank=True, max_length=500)),
(
"backstory",
wagtail.fields.StreamField(
[
("char", wagtail.blocks.CharBlock()),
(
"choice",
wagtail.blocks.ChoiceBlock(
choices=[
("M", "Medium"),
("L", "Large"),
("XL", "Extra large"),
]
),
),
(
"list",
wagtail.blocks.ListBlock(
child_block=wagtail.blocks.CharBlock()
),
),
("page", wagtail.blocks.PageChooserBlock()),
("text", wagtail.blocks.TextBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
("url", wagtail.blocks.URLBlock()),
(
"document",
wagtail.documents.blocks.DocumentChooserBlock(),
),
("embed", wagtail.embeds.blocks.EmbedBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"table",
"heading_block",
wagtail.blocks.StructBlock(
[
("title", wagtail.blocks.CharBlock()),
("description", wagtail.blocks.TextBlock()),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"text",
wagtail.blocks.CharBlock(),
),
(
"numeric",
wagtail.blocks.FloatBlock(),
),
(
"rich_text",
wagtail.blocks.RichTextBlock(),
),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
"heading_text",
wagtail.blocks.CharBlock(
form_classname="title", required=True
),
),
(
"size",
wagtail.blocks.ChoiceBlock(
blank=True,
choices=[
("", "Select a header size"),
("h2", "H2"),
("h3", "H3"),
("h4", "H4"),
],
required=False,
),
),
]
),
),
(
"stream",
wagtail.blocks.StreamBlock(
"paragraph_block",
wagtail.blocks.RichTextBlock(
icon="pilcrow",
template="blocks/paragraph_block.html",
),
),
(
"image_block",
wagtail.blocks.StructBlock(
[
("page", wagtail.blocks.PageChooserBlock()),
("embed", wagtail.embeds.blocks.EmbedBlock()),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
wagtail.images.blocks.ImageChooserBlock(
required=True
),
),
(
"caption",
wagtail.blocks.CharBlock(required=False),
),
(
"attribution",
wagtail.blocks.CharBlock(required=False),
),
]
),
),
(
"block_quote",
wagtail.blocks.StructBlock(
[
("text", wagtail.blocks.TextBlock()),
(
"attribute_name",
wagtail.blocks.CharBlock(
blank=True,
label="e.g. Mary Berry",
required=False,
),
),
]
),
),
(
"embed_block",
wagtail.embeds.blocks.EmbedBlock(
help_text="Insert an embed URL e.g https://www.youtube.com/watch?v=SGJFWirQ3ks",
icon="media",
template="blocks/embed_block.html",
),
),
],
blank=True,
help_text="Section 1 is a StreamField in a regular FieldPanel",
help_text="Use only a minimum number of headings and large blocks.",
use_json_field=True,
),
),
("title_2", models.CharField(default="Title 2", max_length=255)),
(
"section_2",
"recipe_headline",
wagtail.fields.RichTextField(
blank=True, help_text="Keep to a single line", max_length=120
),
),
(
"body",
wagtail.fields.StreamField(
[
("char", wagtail.blocks.CharBlock()),
(
"choice",
wagtail.blocks.ChoiceBlock(
choices=[
("M", "Medium"),
("L", "Large"),
("XL", "Extra large"),
]
),
),
(
"list",
wagtail.blocks.ListBlock(
child_block=wagtail.blocks.CharBlock()
),
),
("page", wagtail.blocks.PageChooserBlock()),
("text", wagtail.blocks.TextBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
("url", wagtail.blocks.URLBlock()),
(
"document",
wagtail.documents.blocks.DocumentChooserBlock(),
),
("embed", wagtail.embeds.blocks.EmbedBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"table",
"heading_block",
wagtail.blocks.StructBlock(
[
("title", wagtail.blocks.CharBlock()),
("description", wagtail.blocks.TextBlock()),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"text",
wagtail.blocks.CharBlock(),
),
(
"numeric",
wagtail.blocks.FloatBlock(),
),
(
"rich_text",
wagtail.blocks.RichTextBlock(),
),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
"heading_text",
wagtail.blocks.CharBlock(
form_classname="title", required=True
),
),
]
(
"size",
wagtail.blocks.ChoiceBlock(
blank=True,
choices=[
("", "Select a header size"),
("h2", "H2"),
("h3", "H3"),
("h4", "H4"),
],
required=False,
),
),
],
group="Content",
),
),
(
"stream",
wagtail.blocks.StreamBlock(
"paragraph_block",
wagtail.blocks.RichTextBlock(
group="Content",
icon="pilcrow",
template="blocks/paragraph_block.html",
),
),
(
"block_quote",
wagtail.blocks.StructBlock(
[
("page", wagtail.blocks.PageChooserBlock()),
("embed", wagtail.embeds.blocks.EmbedBlock()),
("text", wagtail.blocks.TextBlock()),
(
"attribute_name",
wagtail.blocks.CharBlock(
blank=True,
label="e.g. Mary Berry",
required=False,
),
),
],
group="Content",
),
),
(
"table_block",
wagtail.contrib.table_block.blocks.TableBlock(
group="Content"
),
),
(
"typed_table_block",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
("text", wagtail.blocks.CharBlock()),
("numeric", wagtail.blocks.FloatBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
],
group="Content",
),
),
(
"image_block",
wagtail.blocks.StructBlock(
[
(
"image",
wagtail.images.blocks.ImageChooserBlock(
required=True
),
),
(
"caption",
wagtail.blocks.CharBlock(required=False),
),
(
"attribution",
wagtail.blocks.CharBlock(required=False),
),
],
group="Media",
),
),
(
"embed_block",
wagtail.embeds.blocks.EmbedBlock(
group="Media",
help_text="Insert an embed URL e.g https://www.youtube.com/watch?v=SGJFWirQ3ks",
icon="media",
template="blocks/embed_block.html",
),
),
(
"ingredients_list",
wagtail.blocks.ListBlock(
wagtail.blocks.RichTextBlock(
features=["bold", "italic", "link"]
),
group="Cooking",
icon="list-ol",
max_num=10,
min_num=2,
),
),
(
"steps_list",
wagtail.blocks.ListBlock(
wagtail.blocks.StructBlock(
[
(
"text",
wagtail.blocks.RichTextBlock(
features=["bold", "italic", "link"]
),
),
(
"difficulty",
wagtail.blocks.ChoiceBlock(
choices=[
("S", "Small"),
("M", "Medium"),
("L", "Large"),
]
),
),
]
),
group="Cooking",
icon="tasks",
max_num=10,
min_num=2,
),
),
],
blank=True,
help_text="Section 2 is a StreamField in a MultiFieldPanel",
help_text="The recipes step-by-step instructions and any other relevant information.",
use_json_field=True,
),
),
@ -207,7 +323,7 @@ class Migration(migrations.Migration):
bases=("wagtailcore.page",),
),
migrations.CreateModel(
name="Item",
name="RecipePersonRelationship",
fields=[
(
"id",
@ -222,180 +338,22 @@ class Migration(migrations.Migration):
"sort_order",
models.IntegerField(blank=True, editable=False, null=True),
),
("title_1", models.CharField(default="Title 1", max_length=255)),
(
"section_1",
wagtail.fields.StreamField(
[
("char", wagtail.blocks.CharBlock()),
(
"choice",
wagtail.blocks.ChoiceBlock(
choices=[
("M", "Medium"),
("L", "Large"),
("XL", "Extra large"),
]
),
),
(
"list",
wagtail.blocks.ListBlock(
child_block=wagtail.blocks.CharBlock()
),
),
("page", wagtail.blocks.PageChooserBlock()),
("text", wagtail.blocks.TextBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
("url", wagtail.blocks.URLBlock()),
(
"document",
wagtail.documents.blocks.DocumentChooserBlock(),
),
("embed", wagtail.embeds.blocks.EmbedBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"table",
wagtail.blocks.StructBlock(
[
("title", wagtail.blocks.CharBlock()),
("description", wagtail.blocks.TextBlock()),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"text",
wagtail.blocks.CharBlock(),
),
(
"numeric",
wagtail.blocks.FloatBlock(),
),
(
"rich_text",
wagtail.blocks.RichTextBlock(),
),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
),
),
]
),
),
(
"stream",
wagtail.blocks.StreamBlock(
[
("page", wagtail.blocks.PageChooserBlock()),
("embed", wagtail.embeds.blocks.EmbedBlock()),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
),
),
],
blank=True,
help_text="Section 1 is a StreamField in a regular FieldPanel",
use_json_field=True,
),
),
("title_2", models.CharField(default="Title 2", max_length=255)),
(
"section_2",
wagtail.fields.StreamField(
[
("char", wagtail.blocks.CharBlock()),
(
"choice",
wagtail.blocks.ChoiceBlock(
choices=[
("M", "Medium"),
("L", "Large"),
("XL", "Extra large"),
]
),
),
(
"list",
wagtail.blocks.ListBlock(
child_block=wagtail.blocks.CharBlock()
),
),
("page", wagtail.blocks.PageChooserBlock()),
("text", wagtail.blocks.TextBlock()),
("rich_text", wagtail.blocks.RichTextBlock()),
("url", wagtail.blocks.URLBlock()),
(
"document",
wagtail.documents.blocks.DocumentChooserBlock(),
),
("embed", wagtail.embeds.blocks.EmbedBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"table",
wagtail.blocks.StructBlock(
[
("title", wagtail.blocks.CharBlock()),
("description", wagtail.blocks.TextBlock()),
(
"table",
wagtail.contrib.typed_table_block.blocks.TypedTableBlock(
[
(
"text",
wagtail.blocks.CharBlock(),
),
(
"numeric",
wagtail.blocks.FloatBlock(),
),
(
"rich_text",
wagtail.blocks.RichTextBlock(),
),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
),
),
]
),
),
(
"stream",
wagtail.blocks.StreamBlock(
[
("page", wagtail.blocks.PageChooserBlock()),
("embed", wagtail.embeds.blocks.EmbedBlock()),
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
]
),
),
],
blank=True,
help_text="Section 2 is a StreamField in a MultiFieldPanel",
use_json_field=True,
),
),
(
"page",
modelcluster.fields.ParentalKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="items",
related_name="recipe_person_relationship",
to="recipes.recipepage",
),
),
(
"person",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="person_recipe_relationship",
to="base.person",
),
),
],
options={
"ordering": ["sort_order"],

Wyświetl plik

@ -1,73 +1,152 @@
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.fields import StreamField
from wagtail.admin.panels import FieldPanel, HelpPanel, InlinePanel, MultiFieldPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.models import Orderable, Page
from wagtail.search import index
from .blocks import BLOCKS
from bakerydemo.base.blocks import BaseStreamBlock
from bakerydemo.base.models import Person
from .blocks import RecipeStreamBlock
class RecipePersonRelationship(Orderable, models.Model):
"""
This defines the relationship between the `Person` within the `base`
app and the RecipePage below. This allows people to be added to a RecipePage.
We have created a two way relationship between RecipePage and Person using
the ParentalKey and ForeignKey
"""
page = ParentalKey(
"RecipePage",
related_name="recipe_person_relationship",
on_delete=models.CASCADE,
)
person = models.ForeignKey(
"base.Person",
related_name="person_recipe_relationship",
on_delete=models.CASCADE,
)
panels = [FieldPanel("person")]
class RecipePage(Page):
"""
RecipePage, to illustrate some Recipe Wagtail scenario's
Recipe pages are more complex than blog pages, demonstrating more advanced StreamField patterns.
"""
title_1 = models.CharField(max_length=255, default="Title 1")
section_1 = StreamField(
BLOCKS,
date_published = models.DateField("Date article published", blank=True, null=True)
subtitle = models.CharField(blank=True, max_length=255)
introduction = models.TextField(blank=True, max_length=500)
backstory = StreamField(
BaseStreamBlock(),
# Demonstrate block_counts to keep the backstory concise.
block_counts={
"heading_block": {"max_num": 1},
"image_block": {"max_num": 1},
"embed_block": {"max_num": 1},
},
blank=True,
use_json_field=True,
help_text="Section 1 is a StreamField in a regular FieldPanel",
help_text="Use only a minimum number of headings and large blocks.",
)
title_2 = models.CharField(max_length=255, default="Title 2")
section_2 = StreamField(
BLOCKS,
# An example of using rich text for single-line content.
recipe_headline = RichTextField(
blank=True,
max_length=120,
features=["bold", "italic", "link"],
help_text="Keep to a single line",
)
body = StreamField(
RecipeStreamBlock(),
blank=True,
use_json_field=True,
help_text="Section 2 is a StreamField in a MultiFieldPanel",
help_text="The recipes step-by-step instructions and any other relevant information.",
)
content_panels = Page.content_panels + [
FieldPanel("title_1"),
FieldPanel("section_1"),
FieldPanel("date_published"),
# Using `title` to make a field larger.
FieldPanel("subtitle", classname="title"),
MultiFieldPanel(
[
FieldPanel("title_2"),
FieldPanel("section_2"),
# Example use case for HelpPanel.
HelpPanel(
"Refer to keywords analysis and correct international ingredients names to craft the best introduction backstory, and headline."
),
FieldPanel("introduction"),
# StreamField inside a MultiFieldPanel.
FieldPanel("backstory"),
FieldPanel("recipe_headline"),
],
heading="MultiFieldPanel for section 2",
heading="Preface",
),
FieldPanel("body"),
InlinePanel(
"items", label="Items", help_text="Related items via an inline panel"
"recipe_person_relationship",
heading="Authors",
label="Author",
help_text="Select between one and three authors",
panels=None,
min_num=1,
max_num=3,
),
]
class Item(Orderable):
page = ParentalKey(RecipePage, on_delete=models.CASCADE, related_name="items")
title_1 = models.CharField(max_length=255, default="Title 1")
section_1 = StreamField(
BLOCKS,
blank=True,
use_json_field=True,
help_text="Section 1 is a StreamField in a regular FieldPanel",
)
title_2 = models.CharField(max_length=255, default="Title 2")
section_2 = StreamField(
BLOCKS,
blank=True,
use_json_field=True,
help_text="Section 2 is a StreamField in a MultiFieldPanel",
)
panels = [
FieldPanel("title_1"),
FieldPanel("section_1"),
MultiFieldPanel(
[
FieldPanel("title_2"),
FieldPanel("section_2"),
],
heading="MultiFieldPanel for section 2",
),
search_fields = Page.search_fields + [
index.SearchField("backstory"),
index.SearchField("body"),
]
def authors(self):
"""
Returns the BlogPage's related people. Again note that we are using
the ParentalKey's related_name from the BlogPersonRelationship model
to access these objects. This allows us to access the Person objects
with a loop on the template. If we tried to access the blog_person_
relationship directly we'd print `blog.BlogPersonRelationship.None`
"""
return Person.objects.filter(live=True, person_recipe_relationship__page=self)
# Specifies parent to Recipe as being RecipeIndexPages
parent_page_types = ["RecipeIndexPage"]
# Specifies what content types can exist as children of RecipePage.
# Empty list means that no child content types are allowed.
subpage_types = []
class RecipeIndexPage(Page):
"""
Index page for recipe.
We need to alter the page model's context to return the child page objects,
the RecipePage objects, so that it works as an index page
"""
introduction = models.TextField(help_text="Text to describe the page", blank=True)
content_panels = Page.content_panels + [
FieldPanel("introduction"),
]
# Specifies that only RecipePage objects can live under this index page
subpage_types = ["RecipePage"]
# Defines a method to access the children of the page (e.g. RecipePage
# objects).
def children(self):
return self.get_children().specific().live()
# Overrides the context to list all child items, that are live, by the
# date that they were published
# https://docs.wagtail.org/en/stable/getting_started/tutorial.html#overriding-context
def get_context(self, request):
context = super(RecipeIndexPage, self).get_context(request)
context["recipes"] = (
RecipePage.objects.descendant_of(self).live().order_by("-date_published")
)
return context

Wyświetl plik

@ -54,6 +54,7 @@ INSTALLED_APPS = [
"wagtail.admin",
"wagtail.api.v2",
"wagtail.locales",
"wagtail.contrib.table_block",
"wagtail.contrib.typed_table_block",
"wagtail.contrib.modeladmin",
"wagtail.contrib.routable_page",

Wyświetl plik

@ -404,6 +404,17 @@ blockquote .attribute-name {
height: 100%;
}
/* stylelint-disable-next-line selector-class-pattern */
:is(.block-table_block, .block-typed_table_block) :is(table, tr, td, th) {
border: 1px solid currentColor;
padding: 0.25em;
}
/* stylelint-disable-next-line selector-class-pattern */
:is(.block-table_block, .block-typed_table_block) table {
margin-bottom: 30px;
}
@media screen and (min-width: 768px) {
.header,
.footer {

Wyświetl plik

@ -0,0 +1 @@
{{ self.text }}

Wyświetl plik

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load wagtailcore_tags navigation_tags wagtailimages_tags %}
{% block content %}
<div class="container">
<div class="blog-list">
{% if recipes %}
{% for recipe in recipes %}
{% include "includes/card/blog-listing-card.html" with blog=recipe %}
{% endfor %}
{% else %}
<div class="col-md-12">
<p>Oh, snap. Looks like we were too busy baking to write any recipes. Sorry.</p>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

Wyświetl plik

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% load navigation_tags wagtailimages_tags wagtailcore_tags %}
{% block content %}
{% image self.image fill-1920x600 as hero_img %}
{% include "base/include/header-blog.html" %}
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="blog__meta">
{% if page.authors %}
<div class="blog__avatars">
{% for author in page.authors %}
<div class="blog__author">{% image author.image fill-50x50-c100 class="blog__avatar" %}
{{ author.first_name }} {{ author.last_name }}</div>
{% endfor %}
</div>
{% endif %}
</div>
{% if page.backstory %}
{{ page.backstory }}
<hr>
{% endif %}
{# Give a label to the recipe section for screen reader users. #}
<div id="recipe-headline" class="sr-only">
Recipe{% if page.recipe_headline %}: {{ page.recipe_headline|richtext }}{% endif %}
</div>
<section aria-labelledby="recipe-headline">
{{ page.body }}
</section>
</div>
</div>
</div>
{% endblock content %}

Wyświetl plik

@ -1,14 +0,0 @@
{% extends "base.html" %}
{% block content %}
{% include "base/include/header-hero.html" %}
<div class="container bread-detail">
<div class="row">
<div class="col-md-12">
<p>Recipe page is about admin capabilities and does not render content.</p>
</div>
</div>
</div>
{% endblock content %}