From 35a0bb42c0f0be43394435e477259de0b0a19975 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 6 Jun 2024 13:49:12 +0100 Subject: [PATCH 01/15] CTABlock --- etna/core/blocks/cta.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index c6461043b..812dafa85 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -29,3 +29,12 @@ def get_context(self, value, parent_context=None): link_pages.append(page_2.specific) context["link_pages"] = link_pages return context + +class CallToActionBlock(blocks.StructBlock): + heading = blocks.CharBlock(max_length=100) + body = blocks.RichTextBlock() + page = APIPageChooserBlock() + + class Meta: + icon = "arrow-right" + template = "blocks/call_to_action_block.html" \ No newline at end of file From e1295410981dc0b1daa57234906b1d88201aa44b Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 6 Jun 2024 14:48:17 +0100 Subject: [PATCH 02/15] Updated blocks --- etna/core/blocks/cta.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 812dafa85..67ea11bad 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -1,8 +1,10 @@ +from django.conf import settings from django.utils.translation import gettext_lazy as _ from wagtail import blocks from .page_chooser import APIPageChooserBlock +from .paragraph import APIRichTextBlock class LargeCardLinksBlock(blocks.StructBlock): @@ -31,9 +33,9 @@ def get_context(self, value, parent_context=None): return context class CallToActionBlock(blocks.StructBlock): - heading = blocks.CharBlock(max_length=100) - body = blocks.RichTextBlock() - page = APIPageChooserBlock() + body = APIRichTextBlock(max_length=100, features=settings.RESTRICTED_RICH_TEXT_FEATURES) + label = blocks.CharBlock() + link = APIPageChooserBlock() class Meta: icon = "arrow-right" From 0ca49d7bbeded39151410a26b861febb888f7f6d Mon Sep 17 00:00:00 2001 From: James Biggs Date: Tue, 11 Jun 2024 09:28:10 +0100 Subject: [PATCH 03/15] Add CTA block to ArticlePage block --- etna/articles/blocks.py | 2 ++ etna/core/blocks/__init__.py | 3 ++- etna/core/blocks/cta.py | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/etna/articles/blocks.py b/etna/articles/blocks.py index 5f6e28220..4b48ca29c 100644 --- a/etna/articles/blocks.py +++ b/etna/articles/blocks.py @@ -6,6 +6,7 @@ from etna.core.blocks import ( AuthorPromotedLinkBlock, + CallToActionBlock, ContentImageBlock, FeaturedRecordArticleBlock, NoCaptionImageBlock, @@ -183,6 +184,7 @@ class Meta: class SectionContentBlock(blocks.StreamBlock): + cta = CallToActionBlock() paragraph = ParagraphBlock() quote = QuoteBlock() sub_heading = SubHeadingBlock() diff --git a/etna/core/blocks/__init__.py b/etna/core/blocks/__init__.py index 75584f143..6d0f4fe55 100644 --- a/etna/core/blocks/__init__.py +++ b/etna/core/blocks/__init__.py @@ -1,5 +1,5 @@ from .base import SectionDepthAwareStructBlock -from .cta import LargeCardLinksBlock +from .cta import LargeCardLinksBlock, CallToActionBlock from .featured_content import FeaturedRecordArticleBlock from .image import ContentImageBlock, ImageBlock, NoCaptionImageBlock from .page_chooser import APIPageChooserBlock @@ -11,6 +11,7 @@ __all__ = [ "APIPageChooserBlock", + "CallToActionBlock", "ContentImageBlock", "FeaturedRecordArticleBlock", "ImageBlock", diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 67ea11bad..f16ee65dd 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -38,5 +38,6 @@ class CallToActionBlock(blocks.StructBlock): link = APIPageChooserBlock() class Meta: - icon = "arrow-right" - template = "blocks/call_to_action_block.html" \ No newline at end of file + icon = "link" + template = "blocks/call_to_action_block.html" + label = "Call to action" \ No newline at end of file From ce2364f087d0c07025db8a0881a1a5e1b3301951 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Wed, 12 Jun 2024 11:49:10 +0100 Subject: [PATCH 04/15] Added `ButtonBlock` to use in the CTA block, and use as a standalone block (DSRC-66) --- etna/articles/blocks.py | 2 ++ etna/core/blocks/__init__.py | 3 ++- etna/core/blocks/cta.py | 18 ++++++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/etna/articles/blocks.py b/etna/articles/blocks.py index 4b48ca29c..55a3245ad 100644 --- a/etna/articles/blocks.py +++ b/etna/articles/blocks.py @@ -6,6 +6,7 @@ from etna.core.blocks import ( AuthorPromotedLinkBlock, + ButtonBlock, CallToActionBlock, ContentImageBlock, FeaturedRecordArticleBlock, @@ -184,6 +185,7 @@ class Meta: class SectionContentBlock(blocks.StreamBlock): + button = ButtonBlock() cta = CallToActionBlock() paragraph = ParagraphBlock() quote = QuoteBlock() diff --git a/etna/core/blocks/__init__.py b/etna/core/blocks/__init__.py index 6d0f4fe55..1113203bb 100644 --- a/etna/core/blocks/__init__.py +++ b/etna/core/blocks/__init__.py @@ -1,5 +1,5 @@ from .base import SectionDepthAwareStructBlock -from .cta import LargeCardLinksBlock, CallToActionBlock +from .cta import ButtonBlock, CallToActionBlock, LargeCardLinksBlock from .featured_content import FeaturedRecordArticleBlock from .image import ContentImageBlock, ImageBlock, NoCaptionImageBlock from .page_chooser import APIPageChooserBlock @@ -11,6 +11,7 @@ __all__ = [ "APIPageChooserBlock", + "ButtonBlock", "CallToActionBlock", "ContentImageBlock", "FeaturedRecordArticleBlock", diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index f16ee65dd..763af6171 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -32,12 +32,22 @@ def get_context(self, value, parent_context=None): context["link_pages"] = link_pages return context -class CallToActionBlock(blocks.StructBlock): - body = APIRichTextBlock(max_length=100, features=settings.RESTRICTED_RICH_TEXT_FEATURES) + +class ButtonBlock(blocks.StructBlock): label = blocks.CharBlock() link = APIPageChooserBlock() class Meta: icon = "link" - template = "blocks/call_to_action_block.html" - label = "Call to action" \ No newline at end of file + label = "Button" + + +class CallToActionBlock(blocks.StructBlock): + body = APIRichTextBlock( + max_length=100, features=settings.RESTRICTED_RICH_TEXT_FEATURES + ) + button = ButtonBlock() + + class Meta: + icon = "link" + label = "Call to action" From 429403906c18c6593bb9d66ab0bf7aea86213a92 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Wed, 12 Jun 2024 13:49:44 +0100 Subject: [PATCH 05/15] Migrations --- ...page_body_alter_focusedarticlepage_body.py | 860 ++++++++++++++++++ 1 file changed, 860 insertions(+) create mode 100644 etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py diff --git a/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py new file mode 100644 index 000000000..a8a90f076 --- /dev/null +++ b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py @@ -0,0 +1,860 @@ +# Generated by Django 5.0.6 on 2024-06-12 10:53 +# etna:allowAlterField + +import etna.core.blocks.image +import etna.core.blocks.page_chooser +import etna.core.blocks.paragraph +import etna.media.blocks +import etna.records.blocks +import wagtail.blocks +import wagtail.fields +import wagtail.snippets.blocks +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0106_alter_articlepage_body_alter_focusedarticlepage_body"), + ] + + operations = [ + migrations.AlterField( + model_name="articlepage", + name="body", + field=wagtail.fields.StreamField( + [ + ( + "content_section", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Heading", max_length=100 + ), + ), + ( + "content", + wagtail.blocks.StreamBlock( + [ + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock(), + ), + ] + ), + ), + ( + "cta", + wagtail.blocks.StructBlock( + [ + ( + "body", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ], + max_length=100, + ), + ), + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock(), + ), + ] + ), + ), + ] + ), + ), + ( + "paragraph", + wagtail.blocks.StructBlock( + [ + ( + "text", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ] + ), + ) + ] + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "quote", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=True, + ), + ), + ( + "attribution", + wagtail.blocks.CharBlock( + max_length=100, + required=False, + ), + ), + ] + ), + ), + ( + "sub_heading", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Sub-heading", + max_length=100, + ), + ) + ] + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Check the guidance for tips on writing alt text.', + label="Alternative text", + max_length=100, + ), + ), + ( + "caption", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="If provided, displays directly below the image. Can be used to specify sources, transcripts or other useful metadata.", + label="Caption (optional)", + required=False, + ), + ), + ] + ), + ), + ( + "media", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="A descriptive title for the media block", + required=True, + ), + ), + ( + "background_image", + etna.core.blocks.image.APIImageChooserBlock( + help_text="A background image for the media block" + ), + ), + ( + "media", + etna.media.blocks.MediaChooserBlock(), + ), + ] + ), + ), + ( + "featured_record_article", + wagtail.blocks.StructBlock( + [ + ( + "page", + etna.core.blocks.page_chooser.APIPageChooserBlock( + label="Page", + page_type=[ + "articles.RecordArticlePage" + ], + required_api_fields=[ + "teaser_image" + ], + ), + ) + ] + ), + ), + ( + "promoted_item", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Title of the promoted page", + label="Title", + max_length=100, + ), + ), + ( + "category", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "blog", + "Blog post", + ), + ( + "podcast", + "Podcast", + ), + ("video", "Video"), + ( + "video-external", + "External video", + ), + ( + "external-link", + "External link", + ), + ], + label="Category", + ), + ), + ( + "publication_date", + wagtail.blocks.CharBlock( + help_text="This is a free text field. Please enter date as per agreed format: 14 April 2021", + required=False, + ), + ), + ( + "author", + wagtail.blocks.CharBlock( + required=False + ), + ), + ( + "duration", + wagtail.blocks.CharBlock( + help_text="Podcast or video duration.", + label="Duration", + max_length=50, + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL for the external page", + label="External URL", + ), + ), + ( + "target_blank", + wagtail.blocks.BooleanBlock( + label="Should this URL open in a new tab?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="The text displayed on the button for your URL. If your URL links to an external site, please add the name of the site users will land on, and what they will find on this page. For example 'Watch our short film about Shakespeare on YouTube'.", + label="Call to action label", + max_length=50, + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "decorative", + wagtail.blocks.BooleanBlock( + default=False, + help_text='Decorative images are used for visual effect and do not add information to the content of a page. "Check the guidance to see if your image is decorative.', + label="Is this image decorative?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Decorative images do not require alt text. Check the guidance for tips on writing alt text.', + label="Image alternative text", + max_length=100, + required=False, + ), + ), + ], + label="Teaser image", + template="articles/blocks/images/blog-embed__image-container.html", + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the promoted page", + ), + ), + ] + ), + ), + ( + "promoted_list", + wagtail.blocks.StructBlock( + [ + ( + "category", + wagtail.snippets.blocks.SnippetChooserBlock( + "categories.Category" + ), + ), + ( + "summary", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=False, + ), + ), + ( + "promoted_items", + wagtail.blocks.ListBlock( + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="The title of the target page", + max_length=100, + required=True, + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the target page", + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + required=True + ), + ), + ] + ) + ), + ), + ] + ), + ), + ( + "record_links", + wagtail.blocks.StructBlock( + [ + ( + "items", + wagtail.blocks.ListBlock( + etna.records.blocks.RecordLinkBlock, + label="Items", + ), + ) + ] + ), + ), + ], + required=False, + ), + ), + ] + ), + ) + ], + blank=True, + null=True, + ), + ), + migrations.AlterField( + model_name="focusedarticlepage", + name="body", + field=wagtail.fields.StreamField( + [ + ( + "content_section", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Heading", max_length=100 + ), + ), + ( + "content", + wagtail.blocks.StreamBlock( + [ + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock(), + ), + ] + ), + ), + ( + "cta", + wagtail.blocks.StructBlock( + [ + ( + "body", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ], + max_length=100, + ), + ), + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock(), + ), + ] + ), + ), + ] + ), + ), + ( + "paragraph", + wagtail.blocks.StructBlock( + [ + ( + "text", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ] + ), + ) + ] + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "quote", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=True, + ), + ), + ( + "attribution", + wagtail.blocks.CharBlock( + max_length=100, + required=False, + ), + ), + ] + ), + ), + ( + "sub_heading", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Sub-heading", + max_length=100, + ), + ) + ] + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Check the guidance for tips on writing alt text.', + label="Alternative text", + max_length=100, + ), + ), + ( + "caption", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="If provided, displays directly below the image. Can be used to specify sources, transcripts or other useful metadata.", + label="Caption (optional)", + required=False, + ), + ), + ] + ), + ), + ( + "media", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="A descriptive title for the media block", + required=True, + ), + ), + ( + "background_image", + etna.core.blocks.image.APIImageChooserBlock( + help_text="A background image for the media block" + ), + ), + ( + "media", + etna.media.blocks.MediaChooserBlock(), + ), + ] + ), + ), + ( + "featured_record_article", + wagtail.blocks.StructBlock( + [ + ( + "page", + etna.core.blocks.page_chooser.APIPageChooserBlock( + label="Page", + page_type=[ + "articles.RecordArticlePage" + ], + required_api_fields=[ + "teaser_image" + ], + ), + ) + ] + ), + ), + ( + "promoted_item", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Title of the promoted page", + label="Title", + max_length=100, + ), + ), + ( + "category", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "blog", + "Blog post", + ), + ( + "podcast", + "Podcast", + ), + ("video", "Video"), + ( + "video-external", + "External video", + ), + ( + "external-link", + "External link", + ), + ], + label="Category", + ), + ), + ( + "publication_date", + wagtail.blocks.CharBlock( + help_text="This is a free text field. Please enter date as per agreed format: 14 April 2021", + required=False, + ), + ), + ( + "author", + wagtail.blocks.CharBlock( + required=False + ), + ), + ( + "duration", + wagtail.blocks.CharBlock( + help_text="Podcast or video duration.", + label="Duration", + max_length=50, + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL for the external page", + label="External URL", + ), + ), + ( + "target_blank", + wagtail.blocks.BooleanBlock( + label="Should this URL open in a new tab?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="The text displayed on the button for your URL. If your URL links to an external site, please add the name of the site users will land on, and what they will find on this page. For example 'Watch our short film about Shakespeare on YouTube'.", + label="Call to action label", + max_length=50, + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "decorative", + wagtail.blocks.BooleanBlock( + default=False, + help_text='Decorative images are used for visual effect and do not add information to the content of a page. "Check the guidance to see if your image is decorative.', + label="Is this image decorative?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Decorative images do not require alt text. Check the guidance for tips on writing alt text.', + label="Image alternative text", + max_length=100, + required=False, + ), + ), + ], + label="Teaser image", + template="articles/blocks/images/blog-embed__image-container.html", + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the promoted page", + ), + ), + ] + ), + ), + ( + "promoted_list", + wagtail.blocks.StructBlock( + [ + ( + "category", + wagtail.snippets.blocks.SnippetChooserBlock( + "categories.Category" + ), + ), + ( + "summary", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=False, + ), + ), + ( + "promoted_items", + wagtail.blocks.ListBlock( + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="The title of the target page", + max_length=100, + required=True, + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the target page", + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + required=True + ), + ), + ] + ) + ), + ), + ] + ), + ), + ( + "record_links", + wagtail.blocks.StructBlock( + [ + ( + "items", + wagtail.blocks.ListBlock( + etna.records.blocks.RecordLinkBlock, + label="Items", + ), + ) + ] + ), + ), + ], + required=False, + ), + ), + ] + ), + ) + ], + blank=True, + null=True, + ), + ), + ] From 9c08bb9825854114daa970915a58e3f7266f7fe2 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 10:03:32 +0100 Subject: [PATCH 06/15] Return null/None if get_api_data is empty --- etna/core/blocks/page_chooser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/etna/core/blocks/page_chooser.py b/etna/core/blocks/page_chooser.py index 99be84c60..c6837a035 100644 --- a/etna/core/blocks/page_chooser.py +++ b/etna/core/blocks/page_chooser.py @@ -21,4 +21,7 @@ def __init__( ) def get_api_representation(self, value, context=None): - return get_api_data(object=value, required_api_fields=self.required_api_fields) + return ( + get_api_data(object=value, required_api_fields=self.required_api_fields) + or None + ) From b289ec4f1cfc5e1a4806967223bcf82977aada1b Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 10:21:44 +0100 Subject: [PATCH 07/15] Added validation to prevent two or no links being added --- etna/core/blocks/cta.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 763af6171..046a48333 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from wagtail import blocks @@ -35,7 +36,22 @@ def get_context(self, value, parent_context=None): class ButtonBlock(blocks.StructBlock): label = blocks.CharBlock() - link = APIPageChooserBlock() + link = APIPageChooserBlock(required=False) + external_link = blocks.URLBlock(required=False) + + def clean(self, value): + data = super().clean(value) + + if data.get("link") and data.get("external_link"): + raise ValidationError( + "You must provide either a page link or an external link, not both." + ) + elif not(data.get("link") or data.get("external_link")): + raise ValidationError( + "You must provide either a page link or an external link." + ) + + return data class Meta: icon = "link" From 5cd01881ef04d8f64f9065351a6f9d20bf8d16f1 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 10:24:54 +0100 Subject: [PATCH 08/15] Re-made migrations --- ...page_body_alter_focusedarticlepage_body.py | 42 ++++++++++++++++--- etna/core/blocks/cta.py | 4 +- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py index a8a90f076..50dbd35ea 100644 --- a/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py +++ b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.6 on 2024-06-12 10:53 +# Generated by Django 5.0.6 on 2024-06-13 09:22 # etna:allowAlterField import etna.core.blocks.image @@ -48,7 +48,15 @@ class Migration(migrations.Migration): ), ( "link", - etna.core.blocks.page_chooser.APIPageChooserBlock(), + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), ), ] ), @@ -80,7 +88,15 @@ class Migration(migrations.Migration): ), ( "link", - etna.core.blocks.page_chooser.APIPageChooserBlock(), + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), ), ] ), @@ -467,7 +483,15 @@ class Migration(migrations.Migration): ), ( "link", - etna.core.blocks.page_chooser.APIPageChooserBlock(), + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), ), ] ), @@ -499,7 +523,15 @@ class Migration(migrations.Migration): ), ( "link", - etna.core.blocks.page_chooser.APIPageChooserBlock(), + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), ), ] ), diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 046a48333..56a65665c 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -46,11 +46,11 @@ def clean(self, value): raise ValidationError( "You must provide either a page link or an external link, not both." ) - elif not(data.get("link") or data.get("external_link")): + elif not (data.get("link") or data.get("external_link")): raise ValidationError( "You must provide either a page link or an external link." ) - + return data class Meta: From 035614b929b96181fb1a831324e648a67746769d Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 10:31:17 +0100 Subject: [PATCH 09/15] return api_repr or None --- etna/core/blocks/page_chooser.py | 5 +---- etna/core/serializers/pages.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/etna/core/blocks/page_chooser.py b/etna/core/blocks/page_chooser.py index c6837a035..99be84c60 100644 --- a/etna/core/blocks/page_chooser.py +++ b/etna/core/blocks/page_chooser.py @@ -21,7 +21,4 @@ def __init__( ) def get_api_representation(self, value, context=None): - return ( - get_api_data(object=value, required_api_fields=self.required_api_fields) - or None - ) + return get_api_data(object=value, required_api_fields=self.required_api_fields) diff --git a/etna/core/serializers/pages.py b/etna/core/serializers/pages.py index c94cd4c93..44db26646 100644 --- a/etna/core/serializers/pages.py +++ b/etna/core/serializers/pages.py @@ -62,7 +62,7 @@ def get_api_data(object, required_api_fields: list = []) -> dict: api_representation[field.name] = get_field_data( object=specific, field=field ) - return api_representation + return api_representation or None class DefaultPageSerializer(serializers.Serializer): From ba31a5a41949fd61907ea27fd07f42a9375f848a Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 10:41:16 +0100 Subject: [PATCH 10/15] Updated fixtures --- etna/api/tests/expected_results/article.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etna/api/tests/expected_results/article.json b/etna/api/tests/expected_results/article.json index 0f2d06816..bffb59611 100644 --- a/etna/api/tests/expected_results/article.json +++ b/etna/api/tests/expected_results/article.json @@ -214,7 +214,7 @@ { "type": "featured_record_article", "value": { - "page": {} + "page": null }, "id": "b505f636-f3d1-4d4b-b368-69183e324e6e" }, From bebe3ac37439b78f6ca6f0181e186ce1d72e4153 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 11:27:57 +0100 Subject: [PATCH 11/15] API representation: Link(href) is one attribute, because it is conditional --- etna/core/blocks/cta.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 56a65665c..399268cfb 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -53,6 +53,14 @@ def clean(self, value): return data + def get_api_representation(self, value, context=None): + representation = { + "label": value["label"], + "href": value.get("external_link") or value["link"].full_url, + } + + return representation + class Meta: icon = "link" label = "Button" From ba4b706c147bfc1d63de69ef156ad18763532004 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Thu, 13 Jun 2024 11:46:41 +0100 Subject: [PATCH 12/15] Added `accented` field --- ...page_body_alter_focusedarticlepage_body.py | 34 ++++++++++++++++++- etna/core/blocks/cta.py | 6 ++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py index 50dbd35ea..54c256e95 100644 --- a/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py +++ b/etna/articles/migrations/0107_alter_articlepage_body_alter_focusedarticlepage_body.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.6 on 2024-06-13 09:22 +# Generated by Django 5.0.6 on 2024-06-13 10:46 # etna:allowAlterField import etna.core.blocks.image @@ -58,6 +58,14 @@ class Migration(migrations.Migration): required=False ), ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), ] ), ), @@ -98,6 +106,14 @@ class Migration(migrations.Migration): required=False ), ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), ] ), ), @@ -493,6 +509,14 @@ class Migration(migrations.Migration): required=False ), ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), ] ), ), @@ -533,6 +557,14 @@ class Migration(migrations.Migration): required=False ), ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), ] ), ), diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 399268cfb..9db775dfb 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -38,6 +38,11 @@ class ButtonBlock(blocks.StructBlock): label = blocks.CharBlock() link = APIPageChooserBlock(required=False) external_link = blocks.URLBlock(required=False) + accented = blocks.BooleanBlock( + required=False, + help_text="Use the accented button style", + label="Accented", + ) def clean(self, value): data = super().clean(value) @@ -57,6 +62,7 @@ def get_api_representation(self, value, context=None): representation = { "label": value["label"], "href": value.get("external_link") or value["link"].full_url, + "accented": value.get("accented") or False, } return representation From 272a0e5120d469ef6ebb83c49e5f750ec20da927 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Mon, 17 Jun 2024 12:08:07 +0100 Subject: [PATCH 13/15] Added blocks to GeneralPage --- etna/core/blocks/__init__.py | 4 +++- etna/generic_pages/blocks.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/etna/core/blocks/__init__.py b/etna/core/blocks/__init__.py index c335c6ac3..77fc168c2 100644 --- a/etna/core/blocks/__init__.py +++ b/etna/core/blocks/__init__.py @@ -1,5 +1,5 @@ from .base import SectionDepthAwareStructBlock -from .cta import LargeCardLinksBlock +from .cta import LargeCardLinksBlock, CallToActionBlock, ButtonBlock from .featured_content import ( FeaturedCollectionBlock, FeaturedRecordArticleBlock, @@ -22,6 +22,8 @@ __all__ = [ "APIPageChooserBlock", "AuthorPromotedPagesBlock", + "ButtonBlock", + "CallToActionBlock", "ContentImageBlock", "FeaturedRecordArticleBlock", "FeaturedCollectionBlock", diff --git a/etna/generic_pages/blocks.py b/etna/generic_pages/blocks.py index 4e391c76a..e964d24d8 100644 --- a/etna/generic_pages/blocks.py +++ b/etna/generic_pages/blocks.py @@ -1,6 +1,8 @@ from wagtail import blocks from etna.core.blocks import ( + ButtonBlock, + CallToActionBlock, ContentImageBlock, FeaturedRecordArticleBlock, ParagraphBlock, @@ -16,6 +18,8 @@ class SectionContentBlock(blocks.StreamBlock): + button = ButtonBlock() + call_to_action = CallToActionBlock() featured_record_article = FeaturedRecordArticleBlock() image = ContentImageBlock() media = MediaBlock() From 98c467d7c9c194af6767bc139e1fd1c6f5e90da9 Mon Sep 17 00:00:00 2001 From: James Biggs Date: Mon, 17 Jun 2024 12:29:00 +0100 Subject: [PATCH 14/15] Migrations + formatting --- etna/core/blocks/__init__.py | 2 +- .../migrations/0011_alter_generalpage_body.py | 473 ++++++++++++++++++ 2 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 etna/generic_pages/migrations/0011_alter_generalpage_body.py diff --git a/etna/core/blocks/__init__.py b/etna/core/blocks/__init__.py index 77fc168c2..3670b34c8 100644 --- a/etna/core/blocks/__init__.py +++ b/etna/core/blocks/__init__.py @@ -1,5 +1,5 @@ from .base import SectionDepthAwareStructBlock -from .cta import LargeCardLinksBlock, CallToActionBlock, ButtonBlock +from .cta import ButtonBlock, CallToActionBlock, LargeCardLinksBlock from .featured_content import ( FeaturedCollectionBlock, FeaturedRecordArticleBlock, diff --git a/etna/generic_pages/migrations/0011_alter_generalpage_body.py b/etna/generic_pages/migrations/0011_alter_generalpage_body.py new file mode 100644 index 000000000..193d94ef1 --- /dev/null +++ b/etna/generic_pages/migrations/0011_alter_generalpage_body.py @@ -0,0 +1,473 @@ +# Generated by Django 5.0.6 on 2024-06-17 11:08 +# etna:allowAlterField + +import etna.core.blocks.image +import etna.core.blocks.page_chooser +import etna.core.blocks.paragraph +import etna.media.blocks +import etna.records.blocks +import wagtail.blocks +import wagtail.fields +import wagtail.snippets.blocks +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("generic_pages", "0010_alter_generalpage_body"), + ] + + operations = [ + migrations.AlterField( + model_name="generalpage", + name="body", + field=wagtail.fields.StreamField( + [ + ( + "content_section", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Heading", max_length=100 + ), + ), + ( + "content", + wagtail.blocks.StreamBlock( + [ + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), + ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), + ] + ), + ), + ( + "call_to_action", + wagtail.blocks.StructBlock( + [ + ( + "body", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ], + max_length=100, + ), + ), + ( + "button", + wagtail.blocks.StructBlock( + [ + ( + "label", + wagtail.blocks.CharBlock(), + ), + ( + "link", + etna.core.blocks.page_chooser.APIPageChooserBlock( + required=False + ), + ), + ( + "external_link", + wagtail.blocks.URLBlock( + required=False + ), + ), + ( + "accented", + wagtail.blocks.BooleanBlock( + help_text="Use the accented button style", + label="Accented", + required=False, + ), + ), + ] + ), + ), + ] + ), + ), + ( + "featured_record_article", + wagtail.blocks.StructBlock( + [ + ( + "page", + etna.core.blocks.page_chooser.APIPageChooserBlock( + label="Page", + page_type=[ + "articles.RecordArticlePage" + ], + required_api_fields=[ + "teaser_image" + ], + ), + ) + ] + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Check the guidance for tips on writing alt text.', + label="Alternative text", + max_length=100, + ), + ), + ( + "caption", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="If provided, displays directly below the image. Can be used to specify sources, transcripts or other useful metadata.", + label="Caption (optional)", + required=False, + ), + ), + ] + ), + ), + ( + "media", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="A descriptive title for the media block", + required=True, + ), + ), + ( + "background_image", + etna.core.blocks.image.APIImageChooserBlock( + help_text="A background image for the media block" + ), + ), + ( + "media", + etna.media.blocks.MediaChooserBlock(), + ), + ] + ), + ), + ( + "paragraph", + wagtail.blocks.StructBlock( + [ + ( + "text", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + "ol", + "ul", + ] + ), + ) + ] + ), + ), + ( + "promoted_item", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Title of the promoted page", + label="Title", + max_length=100, + ), + ), + ( + "category", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "blog", + "Blog post", + ), + ( + "podcast", + "Podcast", + ), + ("video", "Video"), + ( + "video-external", + "External video", + ), + ( + "external-link", + "External link", + ), + ], + label="Category", + ), + ), + ( + "publication_date", + wagtail.blocks.CharBlock( + help_text="This is a free text field. Please enter date as per agreed format: 14 April 2021", + required=False, + ), + ), + ( + "author", + wagtail.blocks.CharBlock( + required=False + ), + ), + ( + "duration", + wagtail.blocks.CharBlock( + help_text="Podcast or video duration.", + label="Duration", + max_length=50, + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL for the external page", + label="External URL", + ), + ), + ( + "target_blank", + wagtail.blocks.BooleanBlock( + label="Should this URL open in a new tab?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="The text displayed on the button for your URL. If your URL links to an external site, please add the name of the site users will land on, and what they will find on this page. For example 'Watch our short film about Shakespeare on YouTube'.", + label="Call to action label", + max_length=50, + ), + ), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "image", + etna.core.blocks.image.APIImageChooserBlock( + rendition_size="max-900x900", + required=True, + ), + ), + ( + "decorative", + wagtail.blocks.BooleanBlock( + default=False, + help_text='Decorative images are used for visual effect and do not add information to the content of a page. "Check the guidance to see if your image is decorative.', + label="Is this image decorative?

Tick the box if 'yes'

", + required=False, + ), + ), + ( + "alt_text", + wagtail.blocks.CharBlock( + help_text='Alternative (alt) text describes images when they fail to load, and is read aloud by assistive technologies. Use a maximum of 100 characters to describe your image. Decorative images do not require alt text. Check the guidance for tips on writing alt text.', + label="Image alternative text", + max_length=100, + required=False, + ), + ), + ], + label="Teaser image", + template="articles/blocks/images/blog-embed__image-container.html", + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the promoted page", + ), + ), + ] + ), + ), + ( + "promoted_list", + wagtail.blocks.StructBlock( + [ + ( + "category", + wagtail.snippets.blocks.SnippetChooserBlock( + "categories.Category" + ), + ), + ( + "summary", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=False, + ), + ), + ( + "promoted_items", + wagtail.blocks.ListBlock( + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="The title of the target page", + max_length=100, + required=True, + ), + ), + ( + "description", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + help_text="A description of the target page", + required=False, + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + required=True + ), + ), + ] + ) + ), + ), + ] + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "quote", + etna.core.blocks.paragraph.APIRichTextBlock( + features=[ + "bold", + "italic", + "link", + ], + required=True, + ), + ), + ( + "attribution", + wagtail.blocks.CharBlock( + max_length=100, + required=False, + ), + ), + ] + ), + ), + ( + "record_links", + wagtail.blocks.StructBlock( + [ + ( + "items", + wagtail.blocks.ListBlock( + etna.records.blocks.RecordLinkBlock, + label="Items", + ), + ) + ] + ), + ), + ( + "sub_heading", + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + label="Sub-heading", + max_length=100, + ), + ) + ] + ), + ), + ], + required=False, + ), + ), + ] + ), + ) + ], + blank=True, + null=True, + ), + ), + ] From 1c70031058f973dc168c0ecf6f32580d9a57fa66 Mon Sep 17 00:00:00 2001 From: James Biggs <62654785+jamesbiggs@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:20:51 +0100 Subject: [PATCH 15/15] Update etna/core/blocks/cta.py Co-authored-by: Andrew Hosgood --- etna/core/blocks/cta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etna/core/blocks/cta.py b/etna/core/blocks/cta.py index 9db775dfb..575b6bfa1 100644 --- a/etna/core/blocks/cta.py +++ b/etna/core/blocks/cta.py @@ -62,7 +62,7 @@ def get_api_representation(self, value, context=None): representation = { "label": value["label"], "href": value.get("external_link") or value["link"].full_url, - "accented": value.get("accented") or False, + "accent": value.get("accented") or False, } return representation