diff --git a/network-api/networkapi/wagtailpages/migrations/0162_alter_articlepage_body.py b/network-api/networkapi/wagtailpages/migrations/0162_alter_articlepage_body.py
new file mode 100644
index 00000000000..d8a022be414
--- /dev/null
+++ b/network-api/networkapi/wagtailpages/migrations/0162_alter_articlepage_body.py
@@ -0,0 +1,1285 @@
+# Generated by Django 4.2.15 on 2024-09-12 12:35
+
+import django.core.validators
+import wagtail.blocks
+import wagtail.contrib.table_block.blocks
+import wagtail.documents.blocks
+import wagtail.embeds.blocks
+import wagtail.fields
+import wagtail.images.blocks
+from django.db import migrations
+from wagtail.blocks.migrations.migrate_operation import MigrateStreamData
+
+import networkapi.wagtailpages.pagemodels.customblocks.articles
+import networkapi.wagtailpages.validators
+from networkapi.utility.migration.operations import AlterStreamChildBlockDataOperation
+
+
+def migrate_tableblock(source_block):
+ """Copy old table data to new table in a StructBlock."""
+ return {
+ **source_block,
+ "value": {
+ "table": source_block["value"],
+ "wide": True,
+ },
+ }
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("wagtailpages", "0161_alter_homepage_ideas_headline_and_more"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="articlepage",
+ name="body",
+ field=wagtail.fields.StreamField(
+ [
+ (
+ "accordion",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "accordion_items",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "title",
+ wagtail.blocks.CharBlock(
+ help_text="Heading for the Accordion Item"
+ ),
+ ),
+ (
+ "content",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "rich_text",
+ wagtail.blocks.RichTextBlock(
+ blank=True,
+ features=[
+ "bold",
+ "italic",
+ "link",
+ "ul",
+ "ol",
+ "document-link",
+ ],
+ ),
+ ),
+ (
+ "datawrapper",
+ wagtail.embeds.blocks.EmbedBlock(
+ help_text='Enter the "visualization only" link of the Datawrapper chart. It looks something like this: https://datawrapper.dwcdn.net/KwSKp/1/',
+ icon="image",
+ template="wagtailpages/blocks/datawrapper_block.html",
+ ),
+ ),
+ (
+ "image",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(),
+ ),
+ (
+ "altText",
+ wagtail.blocks.CharBlock(
+ help_text="Image description (for screen readers).",
+ required=True,
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "video",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "url",
+ wagtail.blocks.URLBlock(
+ help_text="For YouTube: go to your YouTube video and click “Share,” then “Embed,” and then copy and paste the provided URL only. For example: https://www.youtube.com/embed/3FIVXBawyQw For Vimeo: follow similar steps to grab the embed URL. For example: https://player.vimeo.com/video/9004979",
+ label="Embed URL",
+ ),
+ ),
+ (
+ "caption",
+ wagtail.blocks.CharBlock(required=False),
+ ),
+ (
+ "caption_url",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ (
+ "external_url",
+ "External URL",
+ ),
+ (
+ "relative_url",
+ "Relative URL",
+ ),
+ ("email", "Email"),
+ (
+ "anchor",
+ "Anchor",
+ ),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ label="Page",
+ required=False,
+ ),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ (
+ "email",
+ wagtail.blocks.EmailBlock(
+ required=False
+ ),
+ ),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File",
+ required=False,
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone",
+ max_length=30,
+ required=False,
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window",
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional URL that this caption should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ (
+ "video_width",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("normal", "Normal"),
+ ("wide", "Wide"),
+ ("full_width", "Full Width"),
+ ],
+ help_text="Wide videos are col-12, Full-Width videos reach both ends of the screen.",
+ ),
+ ),
+ ]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ]
+ )
+ ),
+ )
+ ]
+ ),
+ ),
+ (
+ "airtable",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "url",
+ wagtail.blocks.URLBlock(
+ help_text="Copied from the Airtable embed code. The word 'embed' will be in the url"
+ ),
+ ),
+ (
+ "height",
+ wagtail.blocks.IntegerBlock(
+ default=533,
+ help_text="The pixel height on desktop view, usually copied from the Airtable embed code",
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "datawrapper",
+ wagtail.embeds.blocks.EmbedBlock(
+ help_text='Enter the "visualization only" link of the Datawrapper chart. It looks something like this: https://datawrapper.dwcdn.net/KwSKp/1/',
+ icon="image",
+ template="wagtailpages/blocks/datawrapper_block.html",
+ ),
+ ),
+ (
+ "callout",
+ wagtail.blocks.RichTextBlock(
+ features=["bold", "italic", "link", "h2", "h3", "h4", "ul", "ol"],
+ template="wagtailpages/blocks/article_blockquote_block.html",
+ ),
+ ),
+ (
+ "card_grid",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "cards",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ ("image", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "alt_text",
+ wagtail.blocks.CharBlock(
+ help_text="Alt text for card's image.", required=False
+ ),
+ ),
+ ("title", wagtail.blocks.CharBlock(help_text="Heading for the card.")),
+ ("body", wagtail.blocks.TextBlock(help_text="Body text of the card.")),
+ (
+ "link",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ ("label", wagtail.blocks.CharBlock()),
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ label="Page", required=False
+ ),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File", required=False
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone", max_length=30, required=False
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window", required=False
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional link that this card should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ ]
+ ),
+ help_text="Please use a minimum of 2 cards.",
+ ),
+ )
+ ]
+ ),
+ ),
+ (
+ "content",
+ networkapi.wagtailpages.pagemodels.customblocks.articles.ArticleRichText(
+ features=[
+ "bold",
+ "italic",
+ "link",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "ol",
+ "ul",
+ "hr",
+ "document-link",
+ "large",
+ "image",
+ "footnotes",
+ ]
+ ),
+ ),
+ (
+ "image",
+ wagtail.blocks.StructBlock(
+ [
+ ("image", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "caption",
+ wagtail.blocks.RichTextBlock(
+ features=["bold", "italic", "link"], label="Image caption", required=False
+ ),
+ ),
+ ("alt_text", wagtail.blocks.CharBlock(required=False)),
+ (
+ "wide_image",
+ wagtail.blocks.BooleanBlock(
+ default=False,
+ help_text='Checking this will use a wider version of this image, but not full width. For an edge-to-edge image, use the "Wide Image" block.',
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "image_grid",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "grid_items",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ ("image", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "alt_text",
+ wagtail.blocks.CharBlock(
+ help_text="Alt text for this image.", required=False
+ ),
+ ),
+ (
+ "caption",
+ wagtail.blocks.CharBlock(
+ help_text="Please remember to properly attribute any images we use.",
+ required=False,
+ ),
+ ),
+ (
+ "link",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(
+ label="Page", required=False
+ ),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File", required=False
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone", max_length=30, required=False
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window", required=False
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional link that this figure should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ (
+ "square_image",
+ wagtail.blocks.BooleanBlock(
+ default=True,
+ help_text="If left checked, the image will be cropped to be square.",
+ required=False,
+ ),
+ ),
+ ]
+ )
+ ),
+ )
+ ]
+ ),
+ ),
+ (
+ "image_text",
+ wagtail.blocks.StructBlock(
+ [
+ ("image", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "altText",
+ wagtail.blocks.CharBlock(
+ help_text="Image description (for screen readers).", required=True
+ ),
+ ),
+ (
+ "text",
+ wagtail.blocks.RichTextBlock(
+ features=[
+ "bold",
+ "italic",
+ "link",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "ol",
+ "ul",
+ "hr",
+ "document-link",
+ ]
+ ),
+ ),
+ (
+ "url",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(label="Page", required=False),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File", required=False
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone", max_length=30, required=False
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window", required=False
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional URL that this image should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ (
+ "top_divider",
+ wagtail.blocks.BooleanBlock(
+ help_text="Optional divider above content block.", required=False
+ ),
+ ),
+ (
+ "bottom_divider",
+ wagtail.blocks.BooleanBlock(
+ help_text="Optional divider below content block.", required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "double_image",
+ wagtail.blocks.StructBlock(
+ [
+ ("image_1", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "image_1_caption",
+ wagtail.blocks.RichTextBlock(
+ features=["bold", "italic", "link"], label="Image caption", required=False
+ ),
+ ),
+ ("image_2", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "image_2_caption",
+ wagtail.blocks.RichTextBlock(
+ features=["bold", "italic", "link"], label="Image caption", required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "full_width_image",
+ wagtail.blocks.StructBlock(
+ [
+ ("image", wagtail.images.blocks.ImageChooserBlock()),
+ (
+ "image_height",
+ wagtail.blocks.IntegerBlock(
+ default=410,
+ help_text="A custom height for this image. The image will be 1400px wide by this height. Note: This may cause images to look pixelated. If the browser is wider than 1400px the height will scale vertically while the width scales horizontally",
+ ),
+ ),
+ (
+ "caption",
+ wagtail.blocks.RichTextBlock(
+ features=["bold", "italic", "link"], label="Image caption", required=False
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "iframe",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "url",
+ wagtail.blocks.CharBlock(
+ help_text="Please note that only URLs from allow-listed domains will work."
+ ),
+ ),
+ (
+ "height",
+ wagtail.blocks.IntegerBlock(
+ help_text="Optional integer pixel value for custom iFrame height",
+ required=False,
+ ),
+ ),
+ ("caption", wagtail.blocks.CharBlock(required=False)),
+ (
+ "caption_url",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(label="Page", required=False),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File", required=False
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone", max_length=30, required=False
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window", required=False
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional URL that this caption should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ (
+ "iframe_width",
+ wagtail.blocks.ChoiceBlock(
+ choices=[("normal", "Normal"), ("wide", "Wide"), ("full_width", "Full Width")],
+ help_text="Wide iframes are col-12, Full-Width iframes reach both ends of the screen",
+ ),
+ ),
+ (
+ "disable_scroll",
+ wagtail.blocks.BooleanBlock(
+ default=False,
+ help_text='Checking this will add "scrolling=no" to the iframe. Use this if your iframe is rendering an unnecessary scroll bar or whitespace below it.',
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "linkbutton",
+ wagtail.blocks.StructBlock(
+ [
+ ("label", wagtail.blocks.CharBlock()),
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[networkapi.wagtailpages.validators.RelativeURLValidator()],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[networkapi.wagtailpages.validators.AnchorLinkValidator()],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ ("file", wagtail.documents.blocks.DocumentChooserBlock(label="File", required=False)),
+ ("phone", wagtail.blocks.CharBlock(label="Phone", max_length=30, required=False)),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(label="Open in new window", required=False),
+ ),
+ (
+ "styling",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("btn-primary", "Primary button"),
+ ("btn-secondary", "Secondary button"),
+ ]
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "single_quote",
+ wagtail.blocks.StructBlock(
+ [
+ ("quote", wagtail.blocks.RichTextBlock(features=["bold"])),
+ ("attribution", wagtail.blocks.CharBlock(required=False)),
+ (
+ "attribution_info",
+ wagtail.blocks.RichTextBlock(features=["bold", "link", "large"], required=False),
+ ),
+ ]
+ ),
+ ),
+ (
+ "slider",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "title",
+ wagtail.blocks.CharBlock(help_text="Heading for the slider.", required=False),
+ ),
+ (
+ "slides",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "slide",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "title",
+ wagtail.blocks.CharBlock(
+ help_text="Heading of the card.", required=False
+ ),
+ ),
+ (
+ "image",
+ wagtail.images.blocks.ImageChooserBlock(
+ help_text="The image associated with this event."
+ ),
+ ),
+ (
+ "caption",
+ wagtail.blocks.TextBlock(
+ help_text="Caption for slider image", required=False
+ ),
+ ),
+ (
+ "body",
+ wagtail.blocks.RichTextBlock(
+ blank=True,
+ features=["bold", "italic", "link", "large"],
+ required=False,
+ ),
+ ),
+ (
+ "buttons",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "internal",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "label",
+ wagtail.blocks.CharBlock(
+ help_text="Label for this link."
+ ),
+ ),
+ (
+ "link",
+ wagtail.blocks.PageChooserBlock(
+ help_text="Page that this should link out to."
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "external",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "label",
+ wagtail.blocks.CharBlock(
+ help_text="Label for this link."
+ ),
+ ),
+ (
+ "link",
+ wagtail.blocks.URLBlock(
+ help_text="URL that this should link out to."
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "document",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "label",
+ wagtail.blocks.CharBlock(
+ help_text="Label for this link."
+ ),
+ ),
+ (
+ "document",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ help_text="Document that this should link out to."
+ ),
+ ),
+ ],
+ help_text='An iCal document can be attached here for an "Add to Calendar" button.',
+ ),
+ ),
+ ],
+ help_text="A list of buttons that will appear at the bottom of the card.",
+ max_num=2,
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ )
+ ],
+ help_text="A list of slides.",
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "spacer",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "size",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("1", "quarter spacing"),
+ ("2", "half spacing"),
+ ("3", "single spacing"),
+ ("4", "one and a half spacing"),
+ ("5", "triple spacing"),
+ ]
+ ),
+ )
+ ]
+ ),
+ ),
+ (
+ "table",
+ wagtail.blocks.StructBlock(
+ [
+ ("table", wagtail.contrib.table_block.blocks.TableBlock()),
+ (
+ "wide",
+ wagtail.blocks.BooleanBlock(
+ default=True,
+ help_text="If checked, the table will render wider than the other page body content.",
+ required=False,
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "video",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "url",
+ wagtail.blocks.URLBlock(
+ help_text="For YouTube: go to your YouTube video and click “Share,” then “Embed,” and then copy and paste the provided URL only. For example: https://www.youtube.com/embed/3FIVXBawyQw For Vimeo: follow similar steps to grab the embed URL. For example: https://player.vimeo.com/video/9004979",
+ label="Embed URL",
+ ),
+ ),
+ ("caption", wagtail.blocks.CharBlock(required=False)),
+ (
+ "caption_url",
+ wagtail.blocks.ListBlock(
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "link_to",
+ wagtail.blocks.ChoiceBlock(
+ choices=[
+ ("page", "Page"),
+ ("external_url", "External URL"),
+ ("relative_url", "Relative URL"),
+ ("email", "Email"),
+ ("anchor", "Anchor"),
+ ("file", "File"),
+ ("phone", "Phone"),
+ ],
+ label="Link to",
+ ),
+ ),
+ (
+ "page",
+ wagtail.blocks.PageChooserBlock(label="Page", required=False),
+ ),
+ (
+ "external_url",
+ wagtail.blocks.URLBlock(
+ help_text="Enter a full URL including http:// or https://",
+ label="External URL",
+ max_length=300,
+ required=False,
+ ),
+ ),
+ (
+ "relative_url",
+ wagtail.blocks.CharBlock(
+ help_text='A path relative to this domain. For example, "/foo/bar" or "?foo=bar".',
+ label="Relative URL",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.RelativeURLValidator()
+ ],
+ ),
+ ),
+ (
+ "anchor",
+ wagtail.blocks.CharBlock(
+ help_text='An id attribute of an element on the current page. For example, "#section-1"',
+ label="#",
+ max_length=300,
+ required=False,
+ validators=[
+ networkapi.wagtailpages.validators.AnchorLinkValidator()
+ ],
+ ),
+ ),
+ ("email", wagtail.blocks.EmailBlock(required=False)),
+ (
+ "file",
+ wagtail.documents.blocks.DocumentChooserBlock(
+ label="File", required=False
+ ),
+ ),
+ (
+ "phone",
+ wagtail.blocks.CharBlock(
+ label="Phone", max_length=30, required=False
+ ),
+ ),
+ (
+ "new_window",
+ wagtail.blocks.BooleanBlock(
+ label="Open in new window", required=False
+ ),
+ ),
+ ]
+ ),
+ help_text="Optional URL that this caption should link out to.",
+ max_num=1,
+ min_num=0,
+ ),
+ ),
+ (
+ "video_width",
+ wagtail.blocks.ChoiceBlock(
+ choices=[("normal", "Normal"), ("wide", "Wide"), ("full_width", "Full Width")],
+ help_text="Wide videos are col-12, Full-Width videos reach both ends of the screen.",
+ ),
+ ),
+ ]
+ ),
+ ),
+ (
+ "advanced_table",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "header",
+ wagtail.blocks.BooleanBlock(
+ help_text="Display the first row as a header.", required=False
+ ),
+ ),
+ (
+ "column",
+ wagtail.blocks.BooleanBlock(
+ help_text="Display the first column as a header.", required=False
+ ),
+ ),
+ (
+ "caption",
+ wagtail.blocks.CharBlock(
+ help_text="A heading that identifies the overall topic of the table, and is useful for screen reader users",
+ required=False,
+ ),
+ ),
+ (
+ "wide",
+ wagtail.blocks.BooleanBlock(
+ default=True,
+ help_text="If this is checked, the table will render wider than the other page body content.",
+ required=False,
+ ),
+ ),
+ (
+ "table",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "row",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "cell",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "centered_text",
+ wagtail.blocks.BooleanBlock(required=False),
+ ),
+ (
+ "column_width",
+ wagtail.blocks.IntegerBlock(
+ default=1,
+ help_text="Enter the number of extra cell columns you want to merge together. Merging a cell column will expand a cell to the right. To merge two cells together, set the column width to 2. For 3, set 3. Default is 1. Min 1. Max 20.",
+ validators=[
+ django.core.validators.MaxValueValidator(
+ 20
+ ),
+ django.core.validators.MinValueValidator(
+ 1
+ ),
+ ],
+ ),
+ ),
+ (
+ "content",
+ wagtail.blocks.RichTextBlock(
+ features=[
+ "bold",
+ "italic",
+ "link",
+ "ul",
+ "ol",
+ ]
+ ),
+ ),
+ ]
+ ),
+ )
+ ]
+ ),
+ )
+ ]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ],
+ use_json_field=True,
+ ),
+ ),
+ MigrateStreamData(
+ app_name="wagtailpages",
+ model_name="ArticlePage",
+ field_name="body",
+ operations_and_block_paths=[
+ (AlterStreamChildBlockDataOperation(block="table", operation=migrate_tableblock), ""),
+ ],
+ ),
+ ]
diff --git a/network-api/networkapi/wagtailpages/pagemodels/article_fields.py b/network-api/networkapi/wagtailpages/pagemodels/article_fields.py
index 1925be94cc9..c6afece6844 100644
--- a/network-api/networkapi/wagtailpages/pagemodels/article_fields.py
+++ b/network-api/networkapi/wagtailpages/pagemodels/article_fields.py
@@ -3,7 +3,6 @@
"""
from wagtail import blocks
-from wagtail.contrib.table_block.blocks import TableBlock
from . import customblocks
from .customblocks.base_rich_text_options import base_rich_text_options
@@ -37,7 +36,7 @@
("single_quote", customblocks.SingleQuoteBlock()),
("slider", customblocks.FoundationSliderBlock()),
("spacer", customblocks.BootstrapSpacerBlock()),
- ("table", TableBlock(template="wagtailpages/blocks/article_table_block.html")),
+ ("table", customblocks.WideTableBlock()),
("video", customblocks.VideoBlock()),
("advanced_table", customblocks.AdvancedTableBlock()),
]
diff --git a/network-api/networkapi/wagtailpages/pagemodels/customblocks/__init__.py b/network-api/networkapi/wagtailpages/pagemodels/customblocks/__init__.py
index eb9cb5dc0b4..54e7ab37fbe 100644
--- a/network-api/networkapi/wagtailpages/pagemodels/customblocks/__init__.py
+++ b/network-api/networkapi/wagtailpages/pagemodels/customblocks/__init__.py
@@ -51,6 +51,7 @@
from .session_slider_block import SessionSliderBlock
from .single_quote_block import SingleQuoteBlock
from .spaces_block import SpacesBlock
+from .table_block import WideTableBlock
from .text_only_teaser import TextOnlyTeaserBlock
from .tito_widget import TitoWidgetBlock
from .video_block import ExternalVideoBlock, VideoBlock, WagtailVideoChooserBlock
diff --git a/network-api/networkapi/wagtailpages/pagemodels/customblocks/table_block.py b/network-api/networkapi/wagtailpages/pagemodels/customblocks/table_block.py
new file mode 100644
index 00000000000..d2d77328c79
--- /dev/null
+++ b/network-api/networkapi/wagtailpages/pagemodels/customblocks/table_block.py
@@ -0,0 +1,37 @@
+from wagtail import blocks
+from wagtail.contrib.table_block.blocks import TableBlock
+
+
+class WideTableBlock(blocks.StructBlock):
+ """
+ A custom block that contains a table and an option to render it wider than the page body content.
+ """
+
+ table = TableBlock()
+
+ wide = blocks.BooleanBlock(
+ required=False,
+ default=True,
+ help_text="If checked, the table will render wider than the other page body content.",
+ )
+
+ def get_context(self, value, parent_context=None):
+ """
+ Override get_context to add the table body logic to the template context.
+ If `first_row_is_table_header` is True, it slices the data to skip the first row.
+ Otherwise, it returns the full data set.
+ """
+ context = super().get_context(value, parent_context=parent_context)
+
+ # Check if the first row is a header and slice the data accordingly
+ if value.get("table").get("first_row_is_table_header") is True:
+ context["table_body"] = value["table"]["data"][1:] # Skip the first row
+ else:
+ context["table_body"] = value["table"]["data"] # Return the full data set
+
+ return context
+
+ class Meta:
+ icon = "table"
+ label = "Table"
+ template = "wagtailpages/blocks/wide_table_block.html"
diff --git a/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_table_block.html b/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_table_block.html
deleted file mode 100644
index cdce4d92e6a..00000000000
--- a/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_table_block.html
+++ /dev/null
@@ -1,67 +0,0 @@
-{% extends "./base_streamfield_block.html" %}
-{% load wagtailcore_tags table_block_tags %}
-
-{% block block_content %}
-
- {% comment %}
- Modified table template comes from "table_block/blocks/table.html"
- {% endcomment %}
-
- {% if table_caption %}
- {{ table_caption }}
- {% endif %}
- {% if table_header %}
-
-
- {% for column in table_header %}
- {% with forloop.counter0 as col_index %}
-
- {% if column.strip %}
- {% if html_renderer %}
- {{ column.strip|safe|linebreaksbr }}
- {% else %}
- {{ column.strip|linebreaksbr }}
- {% endif %}
- {% endif %}
- |
- {% endwith %}
- {% endfor %}
-
-
- {% endif %}
-
- {% for row in data %}
- {% with forloop.counter0 as row_index %}
-
- {% for column in row %}
- {% with forloop.counter0 as col_index %}
- {% if first_col_is_header and forloop.first %}
-
- {% if column.strip %}
- {% if html_renderer %}
- {{ column.strip|safe|linebreaksbr }}
- {% else %}
- {{ column.strip|linebreaksbr }}
- {% endif %}
- {% endif %}
- |
- {% else %}
-
- {% if column.strip %}
- {% if html_renderer %}
- {{ column.strip|safe|linebreaksbr }}
- {% else %}
- {{ column.strip|linebreaksbr }}
- {% endif %}
- {% endif %}
- |
- {% endif %}
- {% endwith %}
- {% endfor %}
-
- {% endwith %}
- {% endfor %}
-
-
-
-{% endblock %}
diff --git a/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/wide_table_block.html b/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/wide_table_block.html
new file mode 100644
index 00000000000..a5e81539802
--- /dev/null
+++ b/network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/wide_table_block.html
@@ -0,0 +1,59 @@
+{% extends "./base_streamfield_block.html" %}
+{% load wagtailcore_tags table_block_tags %}
+
+{% block block_content %}
+ {% with self.table as table %}
+
+ {% comment %}
+ Modified table template comes from "table_block/blocks/table.html"
+ {% endcomment %}
+
+ {% if table.table_caption %}
+ {{ table.table_caption }}
+ {% endif %}
+ {% if table.first_row_is_table_header %}
+
+
+ {% for column in table.data.0 %}
+ {{ column }} |
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% for row in table_body %}
+ {% with forloop.counter0 as row_index %}
+
+ {% for column in row %}
+ {% with forloop.counter0 as col_index %}
+ {% if table.first_col_is_header and forloop.first %}
+
+ {% if column.strip %}
+ {% if html_renderer %}
+ {{ column.strip|safe|linebreaksbr }}
+ {% else %}
+ {{ column.strip|linebreaksbr }}
+ {% endif %}
+ {% endif %}
+ |
+ {% else %}
+
+ {% if column.strip %}
+ {% if html_renderer %}
+ {{ column.strip|safe|linebreaksbr }}
+ {% else %}
+ {{ column.strip|linebreaksbr }}
+ {% endif %}
+ {% endif %}
+ |
+ {% endif %}
+ {% endwith %}
+ {% endfor %}
+
+ {% endwith %}
+ {% endfor %}
+
+
+
+ {% endwith %}
+{% endblock block_content %}
diff --git a/pyproject.toml b/pyproject.toml
index 810352ab920..fb745a13eea 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -123,7 +123,6 @@ ignore="H017"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_double_image_block.html" = "H006,T003"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_full_width_image_block.html" = "H006,T003"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_image_block.html" = "H006,T003"
-"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_table_block.html" = "T003"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/article_teaser_block.html" = "H006,T003"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/audio_block.html" = "T002,T003"
"network-api/networkapi/wagtailpages/templates/wagtailpages/blocks/base_streamfield_block.html" = "T003"