From bd12f564b1bd9337a55c6657f517c4506e69596a Mon Sep 17 00:00:00 2001 From: Alex Bridge Date: Wed, 16 Oct 2024 10:31:54 +0100 Subject: [PATCH 1/5] Support/redis config update (#1056) * Update Redis cache configuration with new Heroku-compatible settings * Update version of Redis used by local dev environments --- docker-compose.yml | 2 +- rca/settings/base.py | 33 +++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7280fc6e4..0d8a0f8c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -79,7 +79,7 @@ services: - ./database_dumps:/database_dumps redis: - image: redis:6 + image: redis:7.2 expose: - 6379 logging: diff --git a/rca/settings/base.py b/rca/settings/base.py index 94e34ddfe..91434808e 100644 --- a/rca/settings/base.py +++ b/rca/settings/base.py @@ -176,15 +176,40 @@ # usually use Redis in production and database backend on staging and dev. In # order to use database cache backend you need to run # "django-admin createcachetable" to create a table for the cache. - +# # Do not use the same Redis instance for other things like Celery! -if "REDIS_URL" in env: + +# Prefer the TLS connection URL over non +REDIS_URL = env.get("REDIS_TLS_URL", env.get("REDIS_URL")) + +if REDIS_URL: + connection_pool_kwargs = {} + + if REDIS_URL.startswith("rediss"): + # Heroku Redis uses self-signed certificates for secure redis conections. https://stackoverflow.com/a/66286068 + # When using TLS, we need to disable certificate validation checks. + connection_pool_kwargs["ssl_cert_reqs"] = None + + redis_options = { + "IGNORE_EXCEPTIONS": True, + "SOCKET_CONNECT_TIMEOUT": 2, # seconds + "SOCKET_TIMEOUT": 2, # seconds + "CONNECTION_POOL_KWARGS": connection_pool_kwargs, + } + CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": env["REDIS_URL"], - } + "LOCATION": REDIS_URL + "/0", + "OPTIONS": redis_options, + }, + "renditions": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": REDIS_URL + "/1", + "OPTIONS": redis_options, + }, } + DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True else: CACHES = { "default": { From c8f1c43c74daaec56533afab9daca26983553624 Mon Sep 17 00:00:00 2001 From: Patrick Gan Date: Wed, 16 Oct 2024 20:31:48 +0800 Subject: [PATCH 2/5] Create and add accordion block to guide pages --- .../molecules/accordion/accordion.html | 4 ++-- .../streamfield/blocks/accordion_block.html | 12 ++++++++++ .../streamfield/blocks/accordion_block.yaml | 22 +++++++++++++++++++ .../sass/components/_accordion-block.scss | 8 +++++++ rca/utils/blocks/content.py | 10 +++++++++ rca/utils/blocks/story.py | 10 ++++++++- 6 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.html create mode 100644 rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.yaml diff --git a/rca/project_styleguide/templates/patterns/molecules/accordion/accordion.html b/rca/project_styleguide/templates/patterns/molecules/accordion/accordion.html index ff0421f59..5c806ee8c 100644 --- a/rca/project_styleguide/templates/patterns/molecules/accordion/accordion.html +++ b/rca/project_styleguide/templates/patterns/molecules/accordion/accordion.html @@ -42,11 +42,11 @@ {% endif %} {{ accordion.body|richtext|linebreaks }} {% if accordion.link.url and accordion.link.title %} - {% include "patterns/atoms/link-primary/link-primary--link.html" with href=accordion.link.url text=accordion.link.title %} + {% include "patterns/atoms/link-primary/link-primary--link.html" with href=accordion.link.url text=accordion.link.title %} {% endif %} {# Accordion can also use a snippet which has different link fields #} {% if accordion.link_url and accordion.link_title %} - {% include "patterns/atoms/link-primary/link-primary--link.html" with href=accordion.link_url text=accordion.link_title %} + {% include "patterns/atoms/link-primary/link-primary--link.html" with href=accordion.link_url text=accordion.link_title %} {% endif %} {% if scholarship %} {# Scholarship fields #} diff --git a/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.html b/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.html new file mode 100644 index 000000000..4235efe32 --- /dev/null +++ b/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.html @@ -0,0 +1,12 @@ +
+

{{ value.heading }}

+ +
+ {% for item in value.items %} + {% include "patterns/molecules/accordion/accordion.html" with accordion=item %} + {% endfor %} +
+
+ + + diff --git a/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.yaml b/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.yaml new file mode 100644 index 000000000..540cc78a1 --- /dev/null +++ b/rca/project_styleguide/templates/patterns/molecules/streamfield/blocks/accordion_block.yaml @@ -0,0 +1,22 @@ +context: + value: + heading: Unpredictable Knowledge Expander + items: + - heading: Pixel Artistry + preview_text: Diving into a pixelated universe, reshaping the boundaries of digital canvases + body: The realm of creations defies conventional borders, blending ancient techniques with digital frontiers. Learners embark on quests of discovery, drawing essence from a spectrum of disciplines, intertwining cutting-edge practices with traditional roots across the expansive corridors of our academy. + link: + url: '#' + title: 'Explore the Pixels' + - heading: Culinary Magic + preview_text: Stirring the pot of traditional and futuristic culinary arts + body: Our gastronomic journey knows no bounds, melding the forgotten alchemy of ancient recipes with the science of tomorrow's flavors. Aspiring chefs and food architects unite under our banner, forging paths that dazzle taste buds and challenge the essence of dining, all within the nurturing grounds of our community. + link: + url: '#' + title: 'Uncover the Secrets' + - heading: Echoes of Music + preview_text: Composing the future, resonating through the echoes of time + body: Our musical odyssey is a tapestry of sounds, embracing the silent whispers of the past and the roaring anthems of tomorrow. Participants are the maestros of their journey, orchestrating harmonies that bridge worlds, encouraged by our collective to explore, innovate, and redefine the symphony of life. + link: + url: '#' + title: 'Orchestrate Your Journey' \ No newline at end of file diff --git a/rca/static_src/sass/components/_accordion-block.scss b/rca/static_src/sass/components/_accordion-block.scss index e3671010a..88025140c 100644 --- a/rca/static_src/sass/components/_accordion-block.scss +++ b/rca/static_src/sass/components/_accordion-block.scss @@ -32,6 +32,14 @@ margin-top: 0; } + &--streamfield { + overflow: unset; + + .link--primary { + color: $color--orange !important; + } + } + &__heading { grid-column: 1 / span 2; margin-bottom: $gutter; diff --git a/rca/utils/blocks/content.py b/rca/utils/blocks/content.py index 0a39e05d5..d76bbaf2a 100644 --- a/rca/utils/blocks/content.py +++ b/rca/utils/blocks/content.py @@ -22,6 +22,7 @@ "InfoBlock", "StepBlock", "AccordionBlockWithTitle", + "AccordionBlock", "CustomTeaserBlock", "RelatedPageListBlockPage", "RelatedPageListBlock", @@ -248,6 +249,15 @@ def clean(self, value): return result +class AccordionBlock(blocks.StructBlock): + heading = blocks.CharBlock() + items = blocks.ListBlock(AccordionBlockWithTitle()) + + class Meta: + icon = "list-ul" + template = "patterns/molecules/streamfield/blocks/accordion_block.html" + + class CustomTeaserBlock(blocks.StructBlock): title = blocks.CharBlock(required=False) meta = blocks.CharBlock( diff --git a/rca/utils/blocks/story.py b/rca/utils/blocks/story.py index 6f31cd8aa..42c54ca1f 100644 --- a/rca/utils/blocks/story.py +++ b/rca/utils/blocks/story.py @@ -2,7 +2,14 @@ from wagtail.embeds.blocks import EmbedBlock from wagtail.snippets.blocks import SnippetChooserBlock -from .content import CTALinkBlock, DocumentBlock, ImageBlock, QuoteBlock, TableBlock +from .content import ( + AccordionBlock, + CTALinkBlock, + DocumentBlock, + ImageBlock, + QuoteBlock, + TableBlock, +) from .embeds import CookieSnippetBlock, JWPLayerBlock, VepplePanoramaBlock __all__ = [ @@ -57,6 +64,7 @@ class GuideBlock(blocks.StreamBlock): vepple_panorama = VepplePanoramaBlock() cookie_snippet_block = CookieSnippetBlock("utils.CookieButtonSnippet") cta_link = CTALinkBlock() + accordion = AccordionBlock() class Meta: template = "patterns/molecules/streamfield/stream_block.html" From c89d32e5acbc6932d97852dfd57a484c425eb026 Mon Sep 17 00:00:00 2001 From: Patrick Gan Date: Wed, 16 Oct 2024 20:32:24 +0800 Subject: [PATCH 3/5] Create and add accordion block to guide pages --- rca/static_src/sass/components/_accordion-block.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/rca/static_src/sass/components/_accordion-block.scss b/rca/static_src/sass/components/_accordion-block.scss index 88025140c..2ee455646 100644 --- a/rca/static_src/sass/components/_accordion-block.scss +++ b/rca/static_src/sass/components/_accordion-block.scss @@ -35,6 +35,7 @@ &--streamfield { overflow: unset; + // Currently, background will always be black when used as a streamfield .link--primary { color: $color--orange !important; } From 8e4f1fb36ef44e0fc5f26c6135d0e3828c92bef7 Mon Sep 17 00:00:00 2001 From: Patrick Gan Date: Wed, 16 Oct 2024 20:32:42 +0800 Subject: [PATCH 4/5] Generate migrations --- .../0028_accordion_streamfield_block.py | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 rca/guides/migrations/0028_accordion_streamfield_block.py diff --git a/rca/guides/migrations/0028_accordion_streamfield_block.py b/rca/guides/migrations/0028_accordion_streamfield_block.py new file mode 100644 index 000000000..d8f61b920 --- /dev/null +++ b/rca/guides/migrations/0028_accordion_streamfield_block.py @@ -0,0 +1,261 @@ +# Generated by Django 4.2.11 on 2024-10-16 12:32 + +from django.db import migrations +import rca.navigation.models +import rca.utils.blocks.embeds +import wagtail.blocks +import wagtail.contrib.typed_table_block.blocks +import wagtail.embeds.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ("guides", "0027_link_block_url_label"), + ] + + operations = [ + migrations.AlterField( + model_name="guidepage", + name="body", + field=wagtail.fields.StreamField( + [ + ( + "anchor_heading", + wagtail.blocks.CharBlock( + form_classname="full title", + icon="title", + template="patterns/molecules/streamfield/blocks/anchor_heading_block.html", + ), + ), + ( + "heading", + wagtail.blocks.CharBlock( + form_classname="full title", + icon="title", + template="patterns/molecules/streamfield/blocks/heading_block.html", + ), + ), + ("paragraph", wagtail.blocks.RichTextBlock()), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock()), + ("caption", wagtail.blocks.CharBlock(required=False)), + ( + "decorative", + wagtail.blocks.BooleanBlock( + help_text="Toggle to make image decorative so they can be ignored by assistive technologies.", + required=False, + ), + ), + ] + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "quote", + wagtail.blocks.CharBlock( + form_classname="title", + help_text="Enter quote text only, there is no need to add quotation marks", + ), + ), + ("author", wagtail.blocks.CharBlock(required=False)), + ("job_title", wagtail.blocks.CharBlock(required=False)), + ] + ), + ), + ( + "embed", + wagtail.embeds.blocks.EmbedBlock( + help_text="Add a URL from these providers: YouTube, Vimeo, SoundCloud, Twitter.", + label="Embed media", + ), + ), + ( + "table", + wagtail.blocks.StructBlock( + [ + ( + "table", + wagtail.contrib.typed_table_block.blocks.TypedTableBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + features=["bold", "italic", "link"] + ), + ) + ] + ), + ), + ( + "first_row_is_header", + wagtail.blocks.BooleanBlock( + default=True, + label="The first row of columns are headers", + required=False, + ), + ), + ( + "first_col_is_header", + wagtail.blocks.BooleanBlock( + default=False, + label="The first column of each row is a header", + required=False, + ), + ), + ] + ), + ), + ( + "jw_video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Optional title to identify the video. Not shown on the page.", + required=False, + ), + ), + ( + "video_url", + wagtail.blocks.URLBlock( + help_text="The URL of the video to show.", + max_length=1000, + ), + ), + ( + "poster_image", + wagtail.images.blocks.ImageChooserBlock( + help_text="The poster image to show as a placeholder for the video. For best results use an image 1920x1080 pixels" + ), + ), + ] + ), + ), + ( + "vepple_panorama", + wagtail.blocks.StructBlock( + [ + ( + "post_id", + wagtail.blocks.IntegerBlock( + help_text='NOTE: This is the number from the post="X" part of the embed code provided by Vepple. Wagtail only needs this ID, and will generate the rest of the embed code for you.', + label="Post ID", + ), + ) + ] + ), + ), + ( + "cookie_snippet_block", + rca.utils.blocks.embeds.CookieSnippetBlock( + "utils.CookieButtonSnippet" + ), + ), + ( + "cta_link", + wagtail.blocks.StructBlock( + [ + ( + "url", + rca.navigation.models.URLOrRelativeURLBLock( + label="URL", required=False + ), + ), + ( + "page", + wagtail.blocks.PageChooserBlock(required=False), + ), + ( + "title", + wagtail.blocks.CharBlock( + help_text="Leave blank to use the page's own title, required if using a URL", + required=False, + ), + ), + ] + ), + ), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("heading", wagtail.blocks.CharBlock()), + ( + "items", + wagtail.blocks.ListBlock( + wagtail.blocks.StructBlock( + [ + ( + "heading", + wagtail.blocks.CharBlock( + help_text="A large heading diplayed next to the block", + required=False, + ), + ), + ( + "preview_text", + wagtail.blocks.CharBlock( + help_text="The text to display when the accordion is collapsed", + required=False, + ), + ), + ( + "body", + wagtail.blocks.RichTextBlock( + features=[ + "h2", + "h3", + "bold", + "italic", + "image", + "embed", + "ul", + "ol", + "link", + ], + help_text="The content shown when the accordion expanded", + ), + ), + ( + "link", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + required=False + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + required=False + ), + ), + ], + help_text="An optional link to display below the expanded content", + required=False, + ), + ), + ] + ) + ), + ), + ] + ), + ), + ], + blank=True, + ), + ), + ] From c300ad1bd643fd7371312b49f97cd3a84bc4c4bc Mon Sep 17 00:00:00 2001 From: Patrick Gan Date: Fri, 18 Oct 2024 09:57:44 +0800 Subject: [PATCH 5/5] Update migration order --- ...n_streamfield_block.py => 0029_accordion_streamfield_block.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rca/guides/migrations/{0028_accordion_streamfield_block.py => 0029_accordion_streamfield_block.py} (100%) diff --git a/rca/guides/migrations/0028_accordion_streamfield_block.py b/rca/guides/migrations/0029_accordion_streamfield_block.py similarity index 100% rename from rca/guides/migrations/0028_accordion_streamfield_block.py rename to rca/guides/migrations/0029_accordion_streamfield_block.py