From 64ca28f5e8fc297bd2011ae8a517719cf5fe9599 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Fri, 1 Nov 2024 11:33:00 +0000 Subject: [PATCH 01/41] Standard_pages app creation --- cms/standard_pages/__init__.py | 0 cms/standard_pages/admin.py | 3 +++ cms/standard_pages/apps.py | 6 ++++++ cms/standard_pages/migrations/__init__.py | 0 cms/standard_pages/models.py | 3 +++ cms/standard_pages/tests.py | 3 +++ cms/standard_pages/views.py | 3 +++ 7 files changed, 18 insertions(+) create mode 100644 cms/standard_pages/__init__.py create mode 100644 cms/standard_pages/admin.py create mode 100644 cms/standard_pages/apps.py create mode 100644 cms/standard_pages/migrations/__init__.py create mode 100644 cms/standard_pages/models.py create mode 100644 cms/standard_pages/tests.py create mode 100644 cms/standard_pages/views.py diff --git a/cms/standard_pages/__init__.py b/cms/standard_pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/standard_pages/admin.py b/cms/standard_pages/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/cms/standard_pages/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/cms/standard_pages/apps.py b/cms/standard_pages/apps.py new file mode 100644 index 0000000..18e97a9 --- /dev/null +++ b/cms/standard_pages/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class StandardPagesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'standard_pages' diff --git a/cms/standard_pages/migrations/__init__.py b/cms/standard_pages/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/cms/standard_pages/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/cms/standard_pages/tests.py b/cms/standard_pages/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/cms/standard_pages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/cms/standard_pages/views.py b/cms/standard_pages/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/cms/standard_pages/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From d4a31ec7d8b7f984bc6a6131f6f279e5710ab834 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 4 Nov 2024 13:32:22 +0000 Subject: [PATCH 02/41] Addition of InformationPage class in models --- cms/standard_pages/models.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 71a8362..59349a6 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -1,3 +1,32 @@ -from django.db import models +from django.db.models import DateField +from openpyxl.drawing.text import TextField +from wagtail.admin.panels import FieldPanel +from wagtail.blocks import RichTextBlock +from wagtail.documents.blocks import DocumentChooserBlock +from wagtail.images.blocks import ImageChooserBlock +from wagtail.models import Page -# Create your models here. +from cms.core.blocks import ONSEmbedBlock, RelatedLinksBlock +from cms.core.fields import StreamField + + +class InformationPage(Page): + + description = TextField(max_lenth=255) + last_updated = DateField(blank=True) + content = StreamField( + [ + ('Heading', RichTextBlock()), + ('Paragraph', RichTextBlock()), + ('Image', ImageChooserBlock()), + ('Document', DocumentChooserBlock()), + ('Embed', ONSEmbedBlock()),#ONSEmbedBlock + ('Related_links', RelatedLinksBlock()), + ] + ) + + content_panels = Page.content_panels + [ + FieldPanel('description'), + FieldPanel('last_updated'), + FieldPanel('content') + ] From a7cd0514f89f78e47cfc5a9b1d81df9a25889191 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 4 Nov 2024 14:31:49 +0000 Subject: [PATCH 03/41] Changes to InformationPage class models --- cms/standard_pages/models.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 59349a6..e6c7a7d 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -1,5 +1,5 @@ +from django.db import models from django.db.models import DateField -from openpyxl.drawing.text import TextField from wagtail.admin.panels import FieldPanel from wagtail.blocks import RichTextBlock from wagtail.documents.blocks import DocumentChooserBlock @@ -12,17 +12,19 @@ class InformationPage(Page): - description = TextField(max_lenth=255) - last_updated = DateField(blank=True) + description = models.CharField(max_length=255) + last_updated = models.DateField(blank=True, null=True) content = StreamField( [ - ('Heading', RichTextBlock()), - ('Paragraph', RichTextBlock()), - ('Image', ImageChooserBlock()), - ('Document', DocumentChooserBlock()), - ('Embed', ONSEmbedBlock()),#ONSEmbedBlock - ('Related_links', RelatedLinksBlock()), - ] + ('heading', RichTextBlock()), + ('paragraph', RichTextBlock()), + ('image', ImageChooserBlock()), + ('document', DocumentChooserBlock()), + ('embed', ONSEmbedBlock()), + ('related_links', RelatedLinksBlock()), + ], + blank=True, + null=True, ) content_panels = Page.content_panels + [ From ae1fcc9b1f2135dc49ee80eda69ff274f01575d4 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 4 Nov 2024 14:34:45 +0000 Subject: [PATCH 04/41] Front end creation --- cms/jinja2/templates/pages/information_page.html | 1 + cms/standard_pages/models.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 cms/jinja2/templates/pages/information_page.html diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html new file mode 100644 index 0000000..22ef36a --- /dev/null +++ b/cms/jinja2/templates/pages/information_page.html @@ -0,0 +1 @@ +{% extends "templates/base_page.html" %} diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index e6c7a7d..2db8c75 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -1,5 +1,4 @@ from django.db import models -from django.db.models import DateField from wagtail.admin.panels import FieldPanel from wagtail.blocks import RichTextBlock from wagtail.documents.blocks import DocumentChooserBlock From ea79424ff8abac01e279f7635a8e15fb845314e2 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 4 Nov 2024 16:32:26 +0000 Subject: [PATCH 05/41] Addition of template location and minor changes on information_page template for testing --- cms/jinja2/templates/pages/information_page.html | 2 ++ cms/standard_pages/models.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 22ef36a..981403c 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -1 +1,3 @@ {% extends "templates/base_page.html" %} +

{{ page.title }}

+
{{ page.content|richtext }}
diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 2db8c75..e7d15be 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -8,13 +8,13 @@ from cms.core.blocks import ONSEmbedBlock, RelatedLinksBlock from cms.core.fields import StreamField - class InformationPage(Page): + template = "templates/pages/information_page.html" + description = models.CharField(max_length=255) last_updated = models.DateField(blank=True, null=True) - content = StreamField( - [ + content = StreamField([ ('heading', RichTextBlock()), ('paragraph', RichTextBlock()), ('image', ImageChooserBlock()), From 3dc29b9e70fd8054061940f8690fff74276b34a5 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Tue, 5 Nov 2024 08:54:56 +0000 Subject: [PATCH 06/41] Added cms.standard_pages to installed_apps --- cms/settings/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/settings/base.py b/cms/settings/base.py index 1a16658..a552a6b 100644 --- a/cms/settings/base.py +++ b/cms/settings/base.py @@ -60,6 +60,7 @@ "cms.images", "cms.release_calendar", "cms.users", + "cms.standard_pages" "wagtail.embeds", "wagtail.sites", "wagtail.users", From be34d8d66112f5be02bfefa1fbbe04182a1b7ac2 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Tue, 5 Nov 2024 09:08:48 +0000 Subject: [PATCH 07/41] Updated apps.py for standard_pages to be similar to pre-existing apps --- cms/settings/base.py | 2 +- cms/standard_pages/apps.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cms/settings/base.py b/cms/settings/base.py index a552a6b..b697222 100644 --- a/cms/settings/base.py +++ b/cms/settings/base.py @@ -60,7 +60,7 @@ "cms.images", "cms.release_calendar", "cms.users", - "cms.standard_pages" + "cms.standard_pages", "wagtail.embeds", "wagtail.sites", "wagtail.users", diff --git a/cms/standard_pages/apps.py b/cms/standard_pages/apps.py index 18e97a9..c0ccd0b 100644 --- a/cms/standard_pages/apps.py +++ b/cms/standard_pages/apps.py @@ -2,5 +2,7 @@ class StandardPagesConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'standard_pages' + """The standard_pages app config.""" + + default_auto_field = "django.db.models.BigAutoField" + name = "cms.standard_pages" From ef126423b3617c1e60cf6e1165730ce84152a819 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Tue, 5 Nov 2024 10:58:26 +0000 Subject: [PATCH 08/41] Modified InformationPage in models --- cms/standard_pages/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index e7d15be..87b1671 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -1,6 +1,6 @@ from django.db import models from wagtail.admin.panels import FieldPanel -from wagtail.blocks import RichTextBlock +from wagtail.blocks import RichTextBlock, URLBlock from wagtail.documents.blocks import DocumentChooserBlock from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page @@ -12,7 +12,7 @@ class InformationPage(Page): template = "templates/pages/information_page.html" - description = models.CharField(max_length=255) + description = models.TextField(max_length=255) last_updated = models.DateField(blank=True, null=True) content = StreamField([ ('heading', RichTextBlock()), @@ -20,7 +20,7 @@ class InformationPage(Page): ('image', ImageChooserBlock()), ('document', DocumentChooserBlock()), ('embed', ONSEmbedBlock()), - ('related_links', RelatedLinksBlock()), + ('related_links', RelatedLinksBlock(child_block= URLBlock())), ], blank=True, null=True, From daf14b3529b5f7635f5440bafe6908fff034d062 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Tue, 5 Nov 2024 11:47:06 +0000 Subject: [PATCH 09/41] Updated template for informationPage --- cms/jinja2/templates/pages/information_page.html | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 981403c..9dd484e 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -1,3 +1,14 @@ {% extends "templates/base_page.html" %} -

{{ page.title }}

-
{{ page.content|richtext }}
+ +{% block main %} +

{{ page.title }}

+ + {{page.description}} + + {% if page.last_updated %} + {{page.last_updated}} + {% endif %} + + {{page.content}} + +{% endblock %} From 1f54e2d5398cd87db6e4162a57b0f7b4fb11461f Mon Sep 17 00:00:00 2001 From: nehakerung Date: Tue, 5 Nov 2024 15:43:08 +0000 Subject: [PATCH 10/41] Modified models for standard_pages --- cms/standard_pages/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 87b1671..692f999 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -5,7 +5,10 @@ from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page -from cms.core.blocks import ONSEmbedBlock, RelatedLinksBlock +from cms.core.blocks import ( + ONSEmbedBlock, + RelatedLinksBlock, + RelatedContentBlock) from cms.core.fields import StreamField class InformationPage(Page): @@ -20,7 +23,7 @@ class InformationPage(Page): ('image', ImageChooserBlock()), ('document', DocumentChooserBlock()), ('embed', ONSEmbedBlock()), - ('related_links', RelatedLinksBlock(child_block= URLBlock())), + ('related_links', RelatedLinksBlock(RelatedContentBlock())), ], blank=True, null=True, From 5b87e7b06d6dd5db99232db7f8c519c74793fe52 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Wed, 6 Nov 2024 15:47:14 +0000 Subject: [PATCH 11/41] Modified front end --- cms/jinja2/templates/pages/information_page.html | 13 ++++++++++--- cms/standard_pages/models.py | 7 ++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 9dd484e..4a44033 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -1,14 +1,21 @@ {% extends "templates/base_page.html" %} +{% from "components/related-content/_macro.njk" import onsRelatedContent %} {% block main %} -

{{ page.title }}

- {{page.description}} +

{{ page.title }}

+ {% if page.description %} + {{ page.description }} + {% endif %} {% if page.last_updated %} {{page.last_updated}} {% endif %} - {{page.content}} + {% for block in page.content %} + {% if block.block_type == "related_links" %} + {{ page.content }} + {% endif %} + {% endfor %} {% endblock %} diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 692f999..8e9554b 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -8,7 +8,12 @@ from cms.core.blocks import ( ONSEmbedBlock, RelatedLinksBlock, - RelatedContentBlock) + RelatedContentBlock, +) + +#could use... +from cms.core.blocks.stream_blocks import CoreStoryBlock + from cms.core.fields import StreamField class InformationPage(Page): From 332a180b33a84280245495d2903e8e10efa6613f Mon Sep 17 00:00:00 2001 From: nehakerung Date: Thu, 7 Nov 2024 11:29:44 +0000 Subject: [PATCH 12/41] working branch --- cms/jinja2/templates/pages/information_page.html | 11 +++++------ cms/standard_pages/models.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 4a44033..b859dab 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -12,10 +12,9 @@

{{ page.title }}

{{page.last_updated}} {% endif %} - {% for block in page.content %} - {% if block.block_type == "related_links" %} - {{ page.content }} - {% endif %} - {% endfor %} - +{% if links_list %} + {% for link in links_list %} + {{ link.title }} + {% endfor %} +{% endif %} {% endblock %} diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 8e9554b..381817c 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -39,3 +39,13 @@ class InformationPage(Page): FieldPanel('last_updated'), FieldPanel('content') ] + + def get_context(self, request, *args, **kwargs): + context = super().get_context(request, *args, **kwargs) + + list =[] + for block in self.content: + if block.block_type == 'related_links': + list.extend(block.value) + context['links_list'] = list + return context From 6d8b2d393a5feffd1dd5e35f609d34d3c1b58838 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 14:12:53 +0000 Subject: [PATCH 13/41] Django models for information page updated and the template for the information page tweaked --- cms/core/blocks/stream_blocks.py | 1 + .../templates/pages/information_page.html | 29 ++++++---- cms/standard_pages/migrations/0001_initial.py | 31 ++++++++++ cms/standard_pages/models.py | 58 ++++++------------- 4 files changed, 68 insertions(+), 51 deletions(-) create mode 100644 cms/standard_pages/migrations/0001_initial.py diff --git a/cms/core/blocks/stream_blocks.py b/cms/core/blocks/stream_blocks.py index 8c9d2cc..d2595bc 100644 --- a/cms/core/blocks/stream_blocks.py +++ b/cms/core/blocks/stream_blocks.py @@ -27,5 +27,6 @@ class CoreStoryBlock(StreamBlock): equation = MathBlock(group="DataVis", icon="decimal") ons_embed = ONSEmbedBlock(group="DataVis", label="ONS General Embed") + # TODO - Do we need a paragraph block? class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods block_counts: ClassVar[dict[str, dict]] = {"related_links": {"max_num": 1}} diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index b859dab..cfa24a0 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -2,19 +2,24 @@ {% from "components/related-content/_macro.njk" import onsRelatedContent %} {% block main %} +

{{ page.title }}

-

{{ page.title }}

+ {% if page.introduction %} +

{{ page.introduction }}

+ {% endif %} - {% if page.description %} - {{ page.description }} - {% endif %} - {% if page.last_updated %} - {{page.last_updated}} - {% endif %} + {% include_block page.body %} -{% if links_list %} - {% for link in links_list %} - {{ link.title }} - {% endfor %} -{% endif %} + {% if related_pages %} + {{ onsRelatedContent({ + "ariaLabel": 'Related content', + "rows": [ + { + "id": 'related-content', + "title": 'Related content', + "itemsList": related_pages + } + ] + }) }} + {% endif %} {% endblock %} diff --git a/cms/standard_pages/migrations/0001_initial.py b/cms/standard_pages/migrations/0001_initial.py new file mode 100644 index 0000000..28c4006 --- /dev/null +++ b/cms/standard_pages/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.2 on 2024-11-07 13:17 + +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0094_alter_page_locale'), + ] + + operations = [ + migrations.CreateModel( + name='InformationPage', + 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')), + ('description', models.TextField(max_length=255, null=True)), + ('last_updated', models.DateField(blank=True, null=True)), + ('body', wagtail.fields.StreamField([('heading', 0), ('rich_text', 1), ('panel', 5), ('embed', 6), ('image', 7), ('documents', 12), ('related_links', 13), ('equation', 14), ('ons_embed', 17)], block_lookup={0: ('cms.core.blocks.markup.HeadingBlock', (), {}), 1: ('wagtail.blocks.RichTextBlock', (), {}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('announcement', 'Announcement'), ('bare', 'Bare'), ('branded', 'Branded'), ('error', 'Error'), ('ghost', 'Ghost'), ('success', 'Success'), ('warn-branded', 'Warn (branded)'), ('warn', 'Warn')]}), 3: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'document-link']}), 4: ('wagtail.blocks.CharBlock', (), {'label': 'Title (optional)', 'required': False}), 5: ('wagtail.blocks.StructBlock', [[('variant', 2), ('body', 3), ('title', 4)]], {}), 6: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 7: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 8: ('wagtail.documents.blocks.DocumentChooserBlock', (), {}), 9: ('wagtail.blocks.CharBlock', (), {'required': False}), 10: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'document-link'], 'required': False}), 11: ('wagtail.blocks.StructBlock', [[('document', 8), ('title', 9), ('description', 10)]], {}), 12: ('wagtail.blocks.StreamBlock', [[('document', 11)]], {}), 13: ('cms.core.blocks.related.RelatedLinksBlock', (wagtail.blocks.StructBlock([('page', wagtail.blocks.PageChooserBlock(required=False)), ('external_url', wagtail.blocks.URLBlock(label='or External Link', required=False)), ('title', wagtail.blocks.CharBlock(help_text="Populate when adding an external link. When choosing a page, you can leave it blank to use the page's own title", required=False)), ('description', wagtail.blocks.CharBlock(required=False))]),), {}), 14: ('wagtailmath.blocks.MathBlock', (), {'group': 'DataVis', 'icon': 'decimal'}), 15: ('wagtail.blocks.URLBlock', (), {'help_text': 'Must start with https://www.ons.gov.uk/visualisations/ to your URL.'}), 16: ('wagtail.blocks.CharBlock', (), {'default': 'Interactive chart'}), 17: ('wagtail.blocks.StructBlock', [[('url', 15), ('title', 16)]], {'group': 'DataVis', 'label': 'ONS General Embed'})})), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 381817c..a8c7d1c 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -1,51 +1,31 @@ +from typing import ClassVar + from django.db import models from wagtail.admin.panels import FieldPanel -from wagtail.blocks import RichTextBlock, URLBlock -from wagtail.documents.blocks import DocumentChooserBlock -from wagtail.images.blocks import ImageChooserBlock -from wagtail.models import Page -from cms.core.blocks import ( - ONSEmbedBlock, - RelatedLinksBlock, - RelatedContentBlock, -) +# from cms.core.models import BasePage +from wagtail.fields import StreamField +from wagtail.models import Page -#could use... from cms.core.blocks.stream_blocks import CoreStoryBlock -from cms.core.fields import StreamField class InformationPage(Page): - template = "templates/pages/information_page.html" - description = models.TextField(max_length=255) + description = models.TextField(max_length=255, null=True) last_updated = models.DateField(blank=True, null=True) - content = StreamField([ - ('heading', RichTextBlock()), - ('paragraph', RichTextBlock()), - ('image', ImageChooserBlock()), - ('document', DocumentChooserBlock()), - ('embed', ONSEmbedBlock()), - ('related_links', RelatedLinksBlock(RelatedContentBlock())), - ], - blank=True, - null=True, - ) - - content_panels = Page.content_panels + [ - FieldPanel('description'), - FieldPanel('last_updated'), - FieldPanel('content') + body = StreamField(CoreStoryBlock(), use_json_field=True) + + # content_panels = Page.content_panels + [ + # FieldPanel("description"), + # FieldPanel("last_updated"), + # FieldPanel("body"), + # ] + + content_panels: ClassVar[list[FieldPanel]] = [ + *Page.content_panels, + FieldPanel("description"), + FieldPanel("last_updated"), + FieldPanel("body"), ] - - def get_context(self, request, *args, **kwargs): - context = super().get_context(request, *args, **kwargs) - - list =[] - for block in self.content: - if block.block_type == 'related_links': - list.extend(block.value) - context['links_list'] = list - return context From 0da84ac7362d2bf0dd0923517c1f62bf0ae4fc01 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 14:20:54 +0000 Subject: [PATCH 14/41] Zombie code removed --- cms/standard_pages/models.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index a8c7d1c..a1500c5 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -17,12 +17,6 @@ class InformationPage(Page): last_updated = models.DateField(blank=True, null=True) body = StreamField(CoreStoryBlock(), use_json_field=True) - # content_panels = Page.content_panels + [ - # FieldPanel("description"), - # FieldPanel("last_updated"), - # FieldPanel("body"), - # ] - content_panels: ClassVar[list[FieldPanel]] = [ *Page.content_panels, FieldPanel("description"), From bb11fbc17e3f9b852f3255429696c6384f50a5a1 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 15:26:55 +0000 Subject: [PATCH 15/41] Unnused files deleted --- cms/standard_pages/admin.py | 3 --- cms/standard_pages/migrations/0001_initial.py | 17 +++++++++++------ cms/standard_pages/models.py | 16 +++++++--------- cms/standard_pages/tests.py | 3 --- cms/standard_pages/views.py | 3 --- 5 files changed, 18 insertions(+), 24 deletions(-) delete mode 100644 cms/standard_pages/admin.py delete mode 100644 cms/standard_pages/tests.py delete mode 100644 cms/standard_pages/views.py diff --git a/cms/standard_pages/admin.py b/cms/standard_pages/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/cms/standard_pages/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/cms/standard_pages/migrations/0001_initial.py b/cms/standard_pages/migrations/0001_initial.py index 28c4006..a8a6e10 100644 --- a/cms/standard_pages/migrations/0001_initial.py +++ b/cms/standard_pages/migrations/0001_initial.py @@ -1,8 +1,7 @@ -# Generated by Django 5.1.2 on 2024-11-07 13:17 +# Generated by Django 5.1.2 on 2024-11-07 15:23 +import cms.core.fields import django.db.models.deletion -import wagtail.blocks -import wagtail.fields from django.db import migrations, models @@ -11,6 +10,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('images', '0001_initial'), ('wagtailcore', '0094_alter_page_locale'), ] @@ -19,13 +19,18 @@ class Migration(migrations.Migration): name='InformationPage', 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')), - ('description', models.TextField(max_length=255, null=True)), + ('listing_title', models.CharField(blank=True, max_length=255)), + ('listing_summary', models.CharField(blank=True, max_length=255)), + ('social_text', models.CharField(blank=True, max_length=255)), + ('summary', models.TextField(max_length=255, null=True)), ('last_updated', models.DateField(blank=True, null=True)), - ('body', wagtail.fields.StreamField([('heading', 0), ('rich_text', 1), ('panel', 5), ('embed', 6), ('image', 7), ('documents', 12), ('related_links', 13), ('equation', 14), ('ons_embed', 17)], block_lookup={0: ('cms.core.blocks.markup.HeadingBlock', (), {}), 1: ('wagtail.blocks.RichTextBlock', (), {}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('announcement', 'Announcement'), ('bare', 'Bare'), ('branded', 'Branded'), ('error', 'Error'), ('ghost', 'Ghost'), ('success', 'Success'), ('warn-branded', 'Warn (branded)'), ('warn', 'Warn')]}), 3: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'document-link']}), 4: ('wagtail.blocks.CharBlock', (), {'label': 'Title (optional)', 'required': False}), 5: ('wagtail.blocks.StructBlock', [[('variant', 2), ('body', 3), ('title', 4)]], {}), 6: ('wagtail.embeds.blocks.EmbedBlock', (), {}), 7: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 8: ('wagtail.documents.blocks.DocumentChooserBlock', (), {}), 9: ('wagtail.blocks.CharBlock', (), {'required': False}), 10: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul', 'document-link'], 'required': False}), 11: ('wagtail.blocks.StructBlock', [[('document', 8), ('title', 9), ('description', 10)]], {}), 12: ('wagtail.blocks.StreamBlock', [[('document', 11)]], {}), 13: ('cms.core.blocks.related.RelatedLinksBlock', (wagtail.blocks.StructBlock([('page', wagtail.blocks.PageChooserBlock(required=False)), ('external_url', wagtail.blocks.URLBlock(label='or External Link', required=False)), ('title', wagtail.blocks.CharBlock(help_text="Populate when adding an external link. When choosing a page, you can leave it blank to use the page's own title", required=False)), ('description', wagtail.blocks.CharBlock(required=False))]),), {}), 14: ('wagtailmath.blocks.MathBlock', (), {'group': 'DataVis', 'icon': 'decimal'}), 15: ('wagtail.blocks.URLBlock', (), {'help_text': 'Must start with https://www.ons.gov.uk/visualisations/ to your URL.'}), 16: ('wagtail.blocks.CharBlock', (), {'default': 'Interactive chart'}), 17: ('wagtail.blocks.StructBlock', [[('url', 15), ('title', 16)]], {'group': 'DataVis', 'label': 'ONS General Embed'})})), + ('body', cms.core.fields.StreamField(block_lookup={})), + ('listing_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.customimage')), + ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.customimage')), ], options={ 'abstract': False, }, - bases=('wagtailcore.page',), + bases=('wagtailcore.page', models.Model), ), ] diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index a1500c5..418dba4 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -3,23 +3,21 @@ from django.db import models from wagtail.admin.panels import FieldPanel -# from cms.core.models import BasePage -from wagtail.fields import StreamField -from wagtail.models import Page - from cms.core.blocks.stream_blocks import CoreStoryBlock +from cms.core.fields import StreamField +from cms.core.models import BasePage -class InformationPage(Page): +class InformationPage(BasePage): template = "templates/pages/information_page.html" - description = models.TextField(max_length=255, null=True) + summary = models.TextField(max_length=255, null=True) last_updated = models.DateField(blank=True, null=True) - body = StreamField(CoreStoryBlock(), use_json_field=True) + body = StreamField(CoreStoryBlock()) content_panels: ClassVar[list[FieldPanel]] = [ - *Page.content_panels, - FieldPanel("description"), + *BasePage.content_panels, + FieldPanel("summary"), FieldPanel("last_updated"), FieldPanel("body"), ] diff --git a/cms/standard_pages/tests.py b/cms/standard_pages/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/cms/standard_pages/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/cms/standard_pages/views.py b/cms/standard_pages/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/cms/standard_pages/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. From b63e91d49b334748dc685d90adbdc16de8712a50 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 15:31:30 +0000 Subject: [PATCH 16/41] Mirgation file formatted --- cms/standard_pages/migrations/0001_initial.py | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/cms/standard_pages/migrations/0001_initial.py b/cms/standard_pages/migrations/0001_initial.py index a8a6e10..acbca03 100644 --- a/cms/standard_pages/migrations/0001_initial.py +++ b/cms/standard_pages/migrations/0001_initial.py @@ -1,36 +1,64 @@ # Generated by Django 5.1.2 on 2024-11-07 15:23 -import cms.core.fields import django.db.models.deletion from django.db import migrations, models +import cms.core.fields + class Migration(migrations.Migration): - initial = True dependencies = [ - ('images', '0001_initial'), - ('wagtailcore', '0094_alter_page_locale'), + ("images", "0001_initial"), + ("wagtailcore", "0094_alter_page_locale"), ] operations = [ migrations.CreateModel( - name='InformationPage', + name="InformationPage", 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')), - ('listing_title', models.CharField(blank=True, max_length=255)), - ('listing_summary', models.CharField(blank=True, max_length=255)), - ('social_text', models.CharField(blank=True, max_length=255)), - ('summary', models.TextField(max_length=255, null=True)), - ('last_updated', models.DateField(blank=True, null=True)), - ('body', cms.core.fields.StreamField(block_lookup={})), - ('listing_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.customimage')), - ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.customimage')), + ( + "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", + ), + ), + ("listing_title", models.CharField(blank=True, max_length=255)), + ("listing_summary", models.CharField(blank=True, max_length=255)), + ("social_text", models.CharField(blank=True, max_length=255)), + ("summary", models.TextField(max_length=255, null=True)), + ("last_updated", models.DateField(blank=True, null=True)), + ("body", cms.core.fields.StreamField(block_lookup={})), + ( + "listing_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "social_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page', models.Model), + bases=("wagtailcore.page", models.Model), ), ] From 34bf9c1e1a7723167a3d9bba632fd74f8ee65606 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 15:37:28 +0000 Subject: [PATCH 17/41] django-manager-missing ignore added --- cms/standard_pages/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 418dba4..d4d47c2 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -8,7 +8,7 @@ from cms.core.models import BasePage -class InformationPage(BasePage): +class InformationPage(BasePage): # type: ignore[django-manager-missing] template = "templates/pages/information_page.html" summary = models.TextField(max_length=255, null=True) From c1c49ddc8938eea5cd933e9c37af17e16257ead9 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 15:47:12 +0000 Subject: [PATCH 18/41] TODO comment removed and docstrings added --- cms/core/blocks/stream_blocks.py | 1 - cms/standard_pages/models.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cms/core/blocks/stream_blocks.py b/cms/core/blocks/stream_blocks.py index d2595bc..8c9d2cc 100644 --- a/cms/core/blocks/stream_blocks.py +++ b/cms/core/blocks/stream_blocks.py @@ -27,6 +27,5 @@ class CoreStoryBlock(StreamBlock): equation = MathBlock(group="DataVis", icon="decimal") ons_embed = ONSEmbedBlock(group="DataVis", label="ONS General Embed") - # TODO - Do we need a paragraph block? class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods block_counts: ClassVar[dict[str, dict]] = {"related_links": {"max_num": 1}} diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index d4d47c2..1a8980e 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -9,6 +9,16 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] + """A Django model representing an information page. + + Attributes: + template (str): The path to the template used to render this page. + summary (TextField): A brief summary of the page content, with a maximum length of 255 characters. + last_updated (DateField): The date when the page was last updated. + body (StreamField): The main content of the page, using a custom StreamField block. + content_panels (list[FieldPanel]): The list of content panels for the Wagtail admin interface. + """ + template = "templates/pages/information_page.html" summary = models.TextField(max_length=255, null=True) From a578042782c0b3b94c3966ed4739c49a08247d0c Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 17:43:31 +0000 Subject: [PATCH 19/41] Information code formatted --- .../templates/pages/information_page.html | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index cfa24a0..9264f57 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -11,15 +11,17 @@

{{ page.title }}

{% include_block page.body %} {% if related_pages %} - {{ onsRelatedContent({ - "ariaLabel": 'Related content', - "rows": [ - { - "id": 'related-content', - "title": 'Related content', - "itemsList": related_pages - } - ] - }) }} + {# fmt:off #} + {{- + onsRelatedContent({ + "ariaLabel": _('Related content'), + "rows": [{ + "id": 'related-content', + "title": _('Related content'), + "itemsList": related_pages + }] + }) + -}} + {# fmt:on #} {% endif %} {% endblock %} From cef401943af68bed15dc1348cfc35dc16579a8b8 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 17:47:28 +0000 Subject: [PATCH 20/41] Docstring rewritten to make it more concise --- cms/core/tests/test_context_processors.py | 1 + cms/core/tests/test_views.py | 1 + cms/standard_pages/models.py | 10 ++-------- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cms/core/tests/test_context_processors.py b/cms/core/tests/test_context_processors.py index 6efa977..8a610cc 100644 --- a/cms/core/tests/test_context_processors.py +++ b/cms/core/tests/test_context_processors.py @@ -7,6 +7,7 @@ class ContextProcessorTestCase(TestCase): """Tests for context processors.""" + def setUp(self): request_factory = RequestFactory() diff --git a/cms/core/tests/test_views.py b/cms/core/tests/test_views.py index d2c21fc..779cacf 100644 --- a/cms/core/tests/test_views.py +++ b/cms/core/tests/test_views.py @@ -5,6 +5,7 @@ class CSRFTestCase(TestCase): """Tests for CSRF enforcement.""" + def setUp(self): # Client is created with each test to avoid mutation side-effects self.client = Client(enforce_csrf_checks=True) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 1a8980e..b9cd267 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -9,14 +9,8 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] - """A Django model representing an information page. - - Attributes: - template (str): The path to the template used to render this page. - summary (TextField): A brief summary of the page content, with a maximum length of 255 characters. - last_updated (DateField): The date when the page was last updated. - body (StreamField): The main content of the page, using a custom StreamField block. - content_panels (list[FieldPanel]): The list of content panels for the Wagtail admin interface. + """A Django model for an information page with a template, summary, + last_updated date, body content, and content panels. """ template = "templates/pages/information_page.html" From 4a253ac8d2c47440f9cc18d993bf01c11bdfdff1 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Thu, 7 Nov 2024 20:33:31 +0000 Subject: [PATCH 21/41] Updated body to content --- .../templates/pages/information_page.html | 10 +++++----- ...0002_rename_body_informationpage_content.py | 18 ++++++++++++++++++ cms/standard_pages/models.py | 11 +++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 cms/standard_pages/migrations/0002_rename_body_informationpage_content.py diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 9264f57..48178f8 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -2,13 +2,13 @@ {% from "components/related-content/_macro.njk" import onsRelatedContent %} {% block main %} -

{{ page.title }}

- - {% if page.introduction %} -

{{ page.introduction }}

+ {% if page.summary %} +

{{ page.summary }}

{% endif %} - {% include_block page.body %} +
Last Updated: {{ page.last_updated }}
+ + {% include_block page.content %} {% if related_pages %} {# fmt:off #} diff --git a/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py b/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py new file mode 100644 index 0000000..2820170 --- /dev/null +++ b/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2024-11-07 17:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('standard_pages', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='informationpage', + old_name='body', + new_name='content', + ), + ] diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index b9cd267..d4f6a1f 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -2,6 +2,7 @@ from django.db import models from wagtail.admin.panels import FieldPanel +from wagtail.search import index from cms.core.blocks.stream_blocks import CoreStoryBlock from cms.core.fields import StreamField @@ -17,11 +18,17 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] summary = models.TextField(max_length=255, null=True) last_updated = models.DateField(blank=True, null=True) - body = StreamField(CoreStoryBlock()) + content = StreamField(CoreStoryBlock()) content_panels: ClassVar[list[FieldPanel]] = [ *BasePage.content_panels, FieldPanel("summary"), FieldPanel("last_updated"), - FieldPanel("body"), + FieldPanel("content"), + ] + + search_fields: ClassVar[list[index.SearchField | index.AutocompleteField]] = [ + *BasePage.search_fields, + index.SearchField("summary"), + index.SearchField("content"), ] From 8e63c600c65d34f6ec969f7b20e7d93439422ee8 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Fri, 8 Nov 2024 15:25:12 +0000 Subject: [PATCH 22/41] Conditional for last_updated added, InformationPage class docstring shortened and migration file regenerated --- .../templates/pages/information_page.html | 4 +++- cms/standard_pages/migrations/0001_initial.py | 4 ++-- ...0002_rename_body_informationpage_content.py | 18 ------------------ cms/standard_pages/models.py | 4 +--- 4 files changed, 6 insertions(+), 24 deletions(-) delete mode 100644 cms/standard_pages/migrations/0002_rename_body_informationpage_content.py diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 48178f8..af485c1 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -6,7 +6,9 @@

{{ page.summary }}

{% endif %} -
Last Updated: {{ page.last_updated }}
+ {% if page.last_updated %} +
Last Updated: {{ page.last_updated }}
+ {% endif %} {% include_block page.content %} diff --git a/cms/standard_pages/migrations/0001_initial.py b/cms/standard_pages/migrations/0001_initial.py index acbca03..73c0881 100644 --- a/cms/standard_pages/migrations/0001_initial.py +++ b/cms/standard_pages/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2024-11-07 15:23 +# Generated by Django 5.1.2 on 2024-11-08 15:12 import django.db.models.deletion from django.db import migrations, models @@ -34,7 +34,7 @@ class Migration(migrations.Migration): ("social_text", models.CharField(blank=True, max_length=255)), ("summary", models.TextField(max_length=255, null=True)), ("last_updated", models.DateField(blank=True, null=True)), - ("body", cms.core.fields.StreamField(block_lookup={})), + ("content", cms.core.fields.StreamField(block_lookup={})), ( "listing_image", models.ForeignKey( diff --git a/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py b/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py deleted file mode 100644 index 2820170..0000000 --- a/cms/standard_pages/migrations/0002_rename_body_informationpage_content.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.2 on 2024-11-07 17:55 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('standard_pages', '0001_initial'), - ] - - operations = [ - migrations.RenameField( - model_name='informationpage', - old_name='body', - new_name='content', - ), - ] diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index d4f6a1f..0c0cb03 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -10,9 +10,7 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] - """A Django model for an information page with a template, summary, - last_updated date, body content, and content panels. - """ + """A generic information page model.""" template = "templates/pages/information_page.html" From 54df00d8a431f72d0ed63a76649a7b54f9c1043d Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Fri, 8 Nov 2024 15:49:56 +0000 Subject: [PATCH 23/41] summary TextField null=true removed and the migration file regenerated to reflect the InformationPage model changes --- cms/standard_pages/migrations/0001_initial.py | 4 ++-- cms/standard_pages/models.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cms/standard_pages/migrations/0001_initial.py b/cms/standard_pages/migrations/0001_initial.py index 73c0881..4c4559d 100644 --- a/cms/standard_pages/migrations/0001_initial.py +++ b/cms/standard_pages/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2024-11-08 15:12 +# Generated by Django 5.1.2 on 2024-11-08 15:48 import django.db.models.deletion from django.db import migrations, models @@ -32,7 +32,7 @@ class Migration(migrations.Migration): ("listing_title", models.CharField(blank=True, max_length=255)), ("listing_summary", models.CharField(blank=True, max_length=255)), ("social_text", models.CharField(blank=True, max_length=255)), - ("summary", models.TextField(max_length=255, null=True)), + ("summary", models.TextField(max_length=255)), ("last_updated", models.DateField(blank=True, null=True)), ("content", cms.core.fields.StreamField(block_lookup={})), ( diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 0c0cb03..5ff1501 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -14,7 +14,7 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] template = "templates/pages/information_page.html" - summary = models.TextField(max_length=255, null=True) + summary = models.TextField(max_length=255) last_updated = models.DateField(blank=True, null=True) content = StreamField(CoreStoryBlock()) From e42d4589e4bb43962e073ece73761d2099aad259 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Fri, 8 Nov 2024 16:06:12 +0000 Subject: [PATCH 24/41] Removing the conditional as per requirement the summary field is required to be entered by the user --- cms/jinja2/templates/pages/information_page.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index af485c1..038aadc 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -2,9 +2,7 @@ {% from "components/related-content/_macro.njk" import onsRelatedContent %} {% block main %} - {% if page.summary %} -

{{ page.summary }}

- {% endif %} +

{{ page.summary }}

{% if page.last_updated %}
Last Updated: {{ page.last_updated }}
From 6628e9a7263bdbd49fe4706a277fc14cf81cc2d7 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Fri, 8 Nov 2024 16:41:05 +0000 Subject: [PATCH 25/41] InformationPage restrictions added --- cms/standard_pages/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 5ff1501..a7490fa 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -30,3 +30,8 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] index.SearchField("summary"), index.SearchField("content"), ] + + parent_page_types: ClassVar[list[str]] = [ + "home.HomePage", + "standard_pages.InformationPage", + ] From 1bc7bbfa5ef9b02424ec832cf4d0c3b9e1fe250c Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Fri, 8 Nov 2024 17:16:38 +0000 Subject: [PATCH 26/41] Code formatted --- cms/release_calendar/models.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cms/release_calendar/models.py b/cms/release_calendar/models.py index 4ae8140..29498db 100644 --- a/cms/release_calendar/models.py +++ b/cms/release_calendar/models.py @@ -48,14 +48,20 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] summary = RichTextField(features=settings.RICH_TEXT_BASIC) release_date = models.DateTimeField( - blank=True, null=True, help_text=_("Required once the release has been confirmed.") + blank=True, + null=True, + help_text=_("Required once the release has been confirmed."), ) release_date_text = models.CharField( - max_length=50, blank=True, help_text=_("Format: 'Month YYYY', or 'Month YYYY to Month YYYY'.") + max_length=50, + blank=True, + help_text=_("Format: 'Month YYYY', or 'Month YYYY to Month YYYY'."), ) next_release_date = models.DateTimeField(blank=True, null=True) next_release_text = models.CharField( - max_length=255, blank=True, help_text=_("Formats: 'DD Month YYYY Time' or 'To be confirmed'.") + max_length=255, + blank=True, + help_text=_("Formats: 'DD Month YYYY Time' or 'To be confirmed'."), ) notice = RichTextField( @@ -169,7 +175,12 @@ def table_of_contents(self) -> list[dict[str, str | object]]: items += block.block.to_table_of_contents_items(block.value) if self.status in NON_PROVISIONAL_STATUSES and self.changes_to_release_date: - items += [{"url": "#changes-to-release-date", "text": _("Changes to this release date")}] + items += [ + { + "url": "#changes-to-release-date", + "text": _("Changes to this release date"), + } + ] if self.status == ReleaseStatus.PUBLISHED and self.contact_details_id: text = _("Contact details") From f60e6370043931f37bf7556810c08e1673b8c052 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Mon, 11 Nov 2024 10:10:35 +0000 Subject: [PATCH 27/41] Comments added --- cms/standard_pages/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index a7490fa..811881b 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -32,6 +32,8 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] ] parent_page_types: ClassVar[list[str]] = [ + # Ensures that the information page can only be created under the home page "home.HomePage", + # Ensures that the information page can be created under another information page "standard_pages.InformationPage", ] From 7cd606bde46b83d5a3d42f4cd21522e630eb245b Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 11 Nov 2024 11:26:11 +0000 Subject: [PATCH 28/41] Moved parent_page code --- cms/standard_pages/models.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 811881b..39586bc 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -14,6 +14,13 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] template = "templates/pages/information_page.html" + parent_page_types: ClassVar[list[str]] = [ + # Ensures that the information page can only be created under the home page + "home.HomePage", + # Ensures that the information page can be created under another information page + "standard_pages.InformationPage", + ] + summary = models.TextField(max_length=255) last_updated = models.DateField(blank=True, null=True) content = StreamField(CoreStoryBlock()) @@ -31,9 +38,4 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] index.SearchField("content"), ] - parent_page_types: ClassVar[list[str]] = [ - # Ensures that the information page can only be created under the home page - "home.HomePage", - # Ensures that the information page can be created under another information page - "standard_pages.InformationPage", - ] + From 3e48738fd756283a99661d730bc974148a6d48cf Mon Sep 17 00:00:00 2001 From: nehakerung Date: Mon, 11 Nov 2024 11:57:31 +0000 Subject: [PATCH 29/41] Removed extra blank lines for lint.py --- cms/standard_pages/models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cms/standard_pages/models.py b/cms/standard_pages/models.py index 39586bc..499be4d 100644 --- a/cms/standard_pages/models.py +++ b/cms/standard_pages/models.py @@ -37,5 +37,3 @@ class InformationPage(BasePage): # type: ignore[django-manager-missing] index.SearchField("summary"), index.SearchField("content"), ] - - From 12563303701b32fccda651d3d046b271fdaaf612 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Mon, 11 Nov 2024 16:44:19 +0000 Subject: [PATCH 30/41] Custom CSS added to make video embeds responsive --- cms/static_src/sass/main.scss | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cms/static_src/sass/main.scss b/cms/static_src/sass/main.scss index 25dbf2f..e5afa76 100644 --- a/cms/static_src/sass/main.scss +++ b/cms/static_src/sass/main.scss @@ -2,3 +2,18 @@ body { display: block; } + +// Custom CSS from Wagtail doc to enable responsive video embeds +.responsive-object { + position: relative; +} + +.responsive-object iframe, +.responsive-object object, +.responsive-object embed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} From e70657e68ab6e75b649776bf7cd1f836f37070d5 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Mon, 11 Nov 2024 20:12:46 +0000 Subject: [PATCH 31/41] Formatting fixed --- cms/release_calendar/models.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cms/release_calendar/models.py b/cms/release_calendar/models.py index 29498db..235ed59 100644 --- a/cms/release_calendar/models.py +++ b/cms/release_calendar/models.py @@ -44,7 +44,9 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] subpage_types: ClassVar[list[str]] = [] # Fields - status = models.CharField(choices=ReleaseStatus.choices, default=ReleaseStatus.PROVISIONAL, max_length=32) + status = models.CharField( + choices=ReleaseStatus.choices, default=ReleaseStatus.PROVISIONAL, max_length=32 + ) summary = RichTextField(features=settings.RICH_TEXT_BASIC) release_date = models.DateTimeField( @@ -94,7 +96,9 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] is_census = models.BooleanField( _("Census"), default=False, - help_text=_("If ticked, will display an information block about the data being related to the Census."), + help_text=_( + "If ticked, will display an information block about the data being related to the Census." + ), ) changes_to_release_date = StreamField( @@ -102,7 +106,9 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] blank=True, help_text=_("Required if making changes to confirmed release dates."), ) - pre_release_access = StreamField(ReleaseCalendarPreReleaseAccessStoryBlock(), blank=True) + pre_release_access = StreamField( + ReleaseCalendarPreReleaseAccessStoryBlock(), blank=True + ) related_links = StreamField(ReleaseCalendarRelatedLinksStoryBlock(), blank=True) content_panels: ClassVar[list[FieldPanel]] = [ @@ -113,14 +119,18 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] FieldRowPanel( [ FieldPanel("release_date"), - FieldPanel("release_date_text", heading=_("Or, release date text")), + FieldPanel( + "release_date_text", heading=_("Or, release date text") + ), ], heading="", ), FieldRowPanel( [ FieldPanel("next_release_date"), - FieldPanel("next_release_text", heading=_("Or, next release text")), + FieldPanel( + "next_release_text", heading=_("Or, next release text") + ), ], heading="", ), @@ -196,6 +206,8 @@ def table_of_contents(self) -> list[dict[str, str | object]]: items += [{"url": f"#{slugify(text)}", "text": text}] if self.related_links: - items += [{"url": "#links", "text": _("You might also be interested in")}] + items += [ + {"url": "#links", "text": _("You might also be interested in")} + ] return items From 65b6bc91a15ef0e66f52abbaa0e328153a5ee6a9 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Tue, 12 Nov 2024 12:55:05 +0000 Subject: [PATCH 32/41] Ignore Unexpected property "left" property-disallowed-list rule --- cms/static_src/sass/main.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/static_src/sass/main.scss b/cms/static_src/sass/main.scss index e5afa76..e82d9fe 100644 --- a/cms/static_src/sass/main.scss +++ b/cms/static_src/sass/main.scss @@ -13,6 +13,7 @@ body { .responsive-object embed { position: absolute; top: 0; + /* stylelint-disable-next-line property-disallowed-list */ left: 0; width: 100%; height: 100%; From 189935216a2412055bf6688976fd8042238b7370 Mon Sep 17 00:00:00 2001 From: sanjeevz3009 Date: Tue, 12 Nov 2024 13:04:59 +0000 Subject: [PATCH 33/41] cms/release_calendar/models.py formating fixed --- cms/release_calendar/models.py | 43 ++++++++-------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/cms/release_calendar/models.py b/cms/release_calendar/models.py index 235ed59..4ae8140 100644 --- a/cms/release_calendar/models.py +++ b/cms/release_calendar/models.py @@ -44,26 +44,18 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] subpage_types: ClassVar[list[str]] = [] # Fields - status = models.CharField( - choices=ReleaseStatus.choices, default=ReleaseStatus.PROVISIONAL, max_length=32 - ) + status = models.CharField(choices=ReleaseStatus.choices, default=ReleaseStatus.PROVISIONAL, max_length=32) summary = RichTextField(features=settings.RICH_TEXT_BASIC) release_date = models.DateTimeField( - blank=True, - null=True, - help_text=_("Required once the release has been confirmed."), + blank=True, null=True, help_text=_("Required once the release has been confirmed.") ) release_date_text = models.CharField( - max_length=50, - blank=True, - help_text=_("Format: 'Month YYYY', or 'Month YYYY to Month YYYY'."), + max_length=50, blank=True, help_text=_("Format: 'Month YYYY', or 'Month YYYY to Month YYYY'.") ) next_release_date = models.DateTimeField(blank=True, null=True) next_release_text = models.CharField( - max_length=255, - blank=True, - help_text=_("Formats: 'DD Month YYYY Time' or 'To be confirmed'."), + max_length=255, blank=True, help_text=_("Formats: 'DD Month YYYY Time' or 'To be confirmed'.") ) notice = RichTextField( @@ -96,9 +88,7 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] is_census = models.BooleanField( _("Census"), default=False, - help_text=_( - "If ticked, will display an information block about the data being related to the Census." - ), + help_text=_("If ticked, will display an information block about the data being related to the Census."), ) changes_to_release_date = StreamField( @@ -106,9 +96,7 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] blank=True, help_text=_("Required if making changes to confirmed release dates."), ) - pre_release_access = StreamField( - ReleaseCalendarPreReleaseAccessStoryBlock(), blank=True - ) + pre_release_access = StreamField(ReleaseCalendarPreReleaseAccessStoryBlock(), blank=True) related_links = StreamField(ReleaseCalendarRelatedLinksStoryBlock(), blank=True) content_panels: ClassVar[list[FieldPanel]] = [ @@ -119,18 +107,14 @@ class ReleaseCalendarPage(BasePage): # type: ignore[django-manager-missing] FieldRowPanel( [ FieldPanel("release_date"), - FieldPanel( - "release_date_text", heading=_("Or, release date text") - ), + FieldPanel("release_date_text", heading=_("Or, release date text")), ], heading="", ), FieldRowPanel( [ FieldPanel("next_release_date"), - FieldPanel( - "next_release_text", heading=_("Or, next release text") - ), + FieldPanel("next_release_text", heading=_("Or, next release text")), ], heading="", ), @@ -185,12 +169,7 @@ def table_of_contents(self) -> list[dict[str, str | object]]: items += block.block.to_table_of_contents_items(block.value) if self.status in NON_PROVISIONAL_STATUSES and self.changes_to_release_date: - items += [ - { - "url": "#changes-to-release-date", - "text": _("Changes to this release date"), - } - ] + items += [{"url": "#changes-to-release-date", "text": _("Changes to this release date")}] if self.status == ReleaseStatus.PUBLISHED and self.contact_details_id: text = _("Contact details") @@ -206,8 +185,6 @@ def table_of_contents(self) -> list[dict[str, str | object]]: items += [{"url": f"#{slugify(text)}", "text": text}] if self.related_links: - items += [ - {"url": "#links", "text": _("You might also be interested in")} - ] + items += [{"url": "#links", "text": _("You might also be interested in")}] return items From cf5113278fe58bfa8f93b5a5c55ed23295e7538b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:02:24 +0000 Subject: [PATCH 34/41] Replace Docker Desktop setup with Colima (#24) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84208c7..39ee2fe 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Ensure you have the following installed: managing Python versions. 2. **[Poetry](https://python-poetry.org/)**: This is used to manage package dependencies and virtual environments. -3. **[Docker](https://docs.docker.com/engine/install/)** and **[Docker Compose](https://docs.docker.com/compose/)**. +3. **[Colima](https://github.com/ONSdigital/dp-compose/blob/main/setting-up-colima-locally.md)** for running the project in Docker containers. 4. **[PostgreSQL](https://www.postgresql.org/)** for the database. Provided as container via `docker-compose.yml` when using the Docker setup. 5. **[Node](https://nodejs.org/en)** and **[`nvm` (Node Version Manager)](https://github.com/nvm-sh/nvm)** for front-end tooling. 6. **Operation System**: Ubuntu/MacOS From 5a6f5c677cf5f072561b8ee9288c3d686425cd8e Mon Sep 17 00:00:00 2001 From: kacperpONS <113175775+kacperpONS@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:21:44 +0000 Subject: [PATCH 35/41] Add Cookie banner and Google Tag Manager snippet (#19) --- README.md | 4 +- cms/core/context_processors.py | 8 +-- cms/core/migrations/0003_delete_tracking.py | 15 ++++++ cms/core/models/settings.py | 12 ----- cms/core/tests/test_context_processors.py | 27 +++------- cms/core/tests/test_views.py | 14 +++++ cms/jinja2/templates/base.html | 57 ++++++++++++++++++++- cms/settings/base.py | 7 ++- cms/settings/test.py | 6 +++ 9 files changed, 107 insertions(+), 43 deletions(-) create mode 100644 cms/core/migrations/0003_delete_tracking.py diff --git a/README.md b/README.md index 39ee2fe..f4a4499 100644 --- a/README.md +++ b/README.md @@ -261,11 +261,11 @@ make megalint Wagtail is built on [Django](https://djangoproject.com/) and changes to its models may require generating and running schema migrations. For full details see the [Django documentation on migrations](https://docs.djangoproject.com/en/5.1/topics/migrations/) -Below are the commands you will most commonly use: +Below are the commands you will most commonly use, note that these have to be run inside the container. ```bash # Check if you need to generate any new migrations after changes to the model -poetru run django-admin makemigrations --check +poetry run django-admin makemigrations --check # Generate migrations poetry run django-admin makemigrations diff --git a/cms/core/context_processors.py b/cms/core/context_processors.py index 799b07b..d685c08 100644 --- a/cms/core/context_processors.py +++ b/cms/core/context_processors.py @@ -2,17 +2,17 @@ from django.conf import settings -from cms.core.models import Tracking - if TYPE_CHECKING: from django.http import HttpRequest def global_vars(request: "HttpRequest") -> dict[str, str | bool | None]: """Set our global variables to use in templates.""" - tracking = Tracking.for_request(request) return { - "GOOGLE_TAG_MANAGER_ID": getattr(tracking, "google_tag_manager_id", None), + "GOOGLE_TAG_MANAGER_CONTAINER_ID": settings.GOOGLE_TAG_MANAGER_CONTAINER_ID, + # Cookie banner settings + "ONS_COOKIE_BANNER_SERVICE_NAME": settings.ONS_COOKIE_BANNER_SERVICE_NAME, + "MANAGE_COOKIE_SETTINGS_URL": settings.MANAGE_COOKIE_SETTINGS_URL, "SEO_NOINDEX": settings.SEO_NOINDEX, "LANGUAGE_CODE": settings.LANGUAGE_CODE, "IS_EXTERNAL_ENV": settings.IS_EXTERNAL_ENV, diff --git a/cms/core/migrations/0003_delete_tracking.py b/cms/core/migrations/0003_delete_tracking.py new file mode 100644 index 0000000..351ff19 --- /dev/null +++ b/cms/core/migrations/0003_delete_tracking.py @@ -0,0 +1,15 @@ +# Generated by Django 5.1.3 on 2024-11-08 15:42 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0002_contactdetails"), + ] + + operations = [ + migrations.DeleteModel( + name="Tracking", + ), + ] diff --git a/cms/core/models/settings.py b/cms/core/models/settings.py index e8eb0d2..13f32b5 100644 --- a/cms/core/models/settings.py +++ b/cms/core/models/settings.py @@ -19,7 +19,6 @@ class BaseSiteSetting(_BaseSiteSetting, models.Model): __all__ = [ "SocialMediaSettings", "SystemMessagesSettings", - "Tracking", ] @@ -64,14 +63,3 @@ class Meta: panels: ClassVar[list[FieldPanel]] = [ MultiFieldPanel([FieldPanel("title_404"), FieldPanel("body_404")], "404 page") ] - - -@register_setting(icon="view") -class Tracking(BaseSiteSetting): - """Settings class for various trackers.""" - - google_tag_manager_id = models.CharField( - max_length=255, - blank=True, - help_text="Your Google Tag Manager ID", - ) diff --git a/cms/core/tests/test_context_processors.py b/cms/core/tests/test_context_processors.py index 8a610cc..1427396 100644 --- a/cms/core/tests/test_context_processors.py +++ b/cms/core/tests/test_context_processors.py @@ -1,8 +1,7 @@ +from django.conf import settings from django.test import RequestFactory, TestCase -from wagtail.models import Site from cms.core.context_processors import global_vars -from cms.core.models import Tracking class ContextProcessorTestCase(TestCase): @@ -14,28 +13,14 @@ def setUp(self): # Request is created with each test to avoid mutation side-effects self.request = request_factory.get("/") - def test_when_no_tracking_settings_defined(self): - """Check the global vars include sensible defaults when no Tracking settings defined.""" + def test_context_processor_picks_up_variables_from_env(self): + """Check that the context processor correctly picks up environment variables.""" self.assertEqual( global_vars(self.request), { - "GOOGLE_TAG_MANAGER_ID": "", - "SEO_NOINDEX": False, - "LANGUAGE_CODE": "en-gb", - "IS_EXTERNAL_ENV": False, - }, - ) - - def test_when_tracking_settings_defined(self): - """Confirm the global vars include Tracking settings when defined.""" - Tracking.objects.create( - site=Site.objects.get(is_default_site=True), - google_tag_manager_id="GTM-123456", - ) - self.assertEqual( - global_vars(self.request), - { - "GOOGLE_TAG_MANAGER_ID": "GTM-123456", + "GOOGLE_TAG_MANAGER_CONTAINER_ID": settings.GOOGLE_TAG_MANAGER_CONTAINER_ID, + "ONS_COOKIE_BANNER_SERVICE_NAME": settings.ONS_COOKIE_BANNER_SERVICE_NAME, + "MANAGE_COOKIE_SETTINGS_URL": settings.MANAGE_COOKIE_SETTINGS_URL, "SEO_NOINDEX": False, "LANGUAGE_CODE": "en-gb", "IS_EXTERNAL_ENV": False, diff --git a/cms/core/tests/test_views.py b/cms/core/tests/test_views.py index 779cacf..acc11f7 100644 --- a/cms/core/tests/test_views.py +++ b/cms/core/tests/test_views.py @@ -1,5 +1,6 @@ import logging +from django.conf import settings from django.test import Client, TestCase @@ -18,3 +19,16 @@ def test_csrf_token_mismatch_logs_an_error(self): self.client.post("/admin/login/", {}) self.assertIn("CSRF Failure: CSRF cookie", logs.output[0]) + + +class TestGoogleTagManagerTestCase(TestCase): + """Tests for the Google Tag Manager.""" + + def test_google_tag_manager_script_present(self): + """Check that the Google Tag Manager script and ID are present on the page. + Note: this doesn't check the functionality, only presence. + """ + response = self.client.get("/") + + self.assertIn("https://www.googletagmanager.com/gtm.js?id=", response.rendered_content) + self.assertIn(settings.GOOGLE_TAG_MANAGER_CONTAINER_ID, response.rendered_content) diff --git a/cms/jinja2/templates/base.html b/cms/jinja2/templates/base.html index b7fa321..723ce11 100644 --- a/cms/jinja2/templates/base.html +++ b/cms/jinja2/templates/base.html @@ -1,4 +1,5 @@ {% extends "layout/_template.njk" %} +{% from "components/cookies-banner/_macro.njk" import onsCookiesBanner %} {% set page_title %} {% if current_site and page.pk == current_site.root_page.pk and current_site.site_name %}{{ current_site.site_name }} | {% endif %}{% block title %}{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title}}{% endif %}{% endblock %}{% block title_suffix %}{% if current_site and page.pk != current_site.root_page.pk and current_site.site_name %} | {{ current_site.site_name }}{% endif %}{% endblock %} @@ -56,10 +57,42 @@ } } %} + {# fmt:on #} {% block head %} - -{% endblock %} + + + {% if GOOGLE_TAG_MANAGER_CONTAINER_ID and not request.is_preview %} + + {% endif %} + +{% endblock %} + {% block preMain %} {% if not IS_EXTERNAL_ENV %} @@ -70,3 +103,23 @@ {% block scripts %} {% endblock %} + +{% block preHeader %} + + {# + NOTE: + the "settingsLinkTextURL" setting currently doesn't match what's in the guidelines + that's because we're on version 70.0.17 and it has been renamed to "settingsLinkUrl" in 72.0.0 + See: https://github.com/ONSdigital/design-system/pull/3300 + #} + {# fmt:off #} + {{ + onsCookiesBanner({ + 'lang': "cy" if LANGUAGE_CODE == "cy" else "en", + 'serviceName': COOKIE_BANNER_SERVICE_NAME, + 'settingsLinkTextURL': MANAGE_COOKIE_SETTINGS_URL, + }) + }} + {# fmt:on #} + +{% endblock %} diff --git a/cms/settings/base.py b/cms/settings/base.py index b697222..5c7eaa8 100644 --- a/cms/settings/base.py +++ b/cms/settings/base.py @@ -737,8 +737,7 @@ DEFAULT_PER_PAGE = 20 # Google Tag Manager ID from env -GOOGLE_TAG_MANAGER_ID = env.get("GOOGLE_TAG_MANAGER_ID") - +GOOGLE_TAG_MANAGER_CONTAINER_ID = env.get("GOOGLE_TAG_MANAGER_CONTAINER_ID", "") # Allows us to toggle search indexing via an environment variable. SEO_NOINDEX = env.get("SEO_NOINDEX", "false").lower() == "true" @@ -779,3 +778,7 @@ DATETIME_FORMAT = "j F Y g:ia" # 1 November 2024, 1 p.m. ONS_EMBED_PREFIX = env.get("ONS_EMBED_PREFIX", "https://www.ons.gov.uk/visualisations/") + +# ONS Cookie banner settings +ONS_COOKIE_BANNER_SERVICE_NAME = env.get("ONS_COOKIE_BANNER_SERVICE_NAME", "www.ons.gov.uk") +MANAGE_COOKIE_SETTINGS_URL = env.get("MANAGE_COOKIE_SETTINGS_URL", "https://www.ons.gov.uk/cookies") diff --git a/cms/settings/test.py b/cms/settings/test.py index 3daa742..3f227d5 100644 --- a/cms/settings/test.py +++ b/cms/settings/test.py @@ -24,6 +24,12 @@ # Wagtail WAGTAILADMIN_BASE_URL = "http://testserver" +# Google Tag Manager +GOOGLE_TAG_MANAGER_CONTAINER_ID = "GTM-123456789" + +# Cookie banner config +ONS_COOKIE_BANNER_SERVICE_NAME = "example.ons.gov.uk" +MANAGE_COOKIE_SETTINGS_URL = "example.ons.gov.uk/cookies" # ############# # Performance From 5e49893ca7c91e56a7dbd2dd4789f2d6270c4afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Braghi=C8=99?= <31622+zerolab@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:41:26 +0000 Subject: [PATCH 36/41] Analysis page and series (#20) * Add skeleton models for Theme / Topic pages * Add base analysis series and page models * Use a custom icon for the contact detail snippet * Update the StreamField block template to pass the block ID to its children * Set parent/subpage types for the new page models * Wire in the analysis page template * Add series functionality (latest/previous releases) * Add tests and fail if coverage under 90% * Tidy up based on code review * Make the accredited / census blocks separate includes * Include changes to release dates when status is confirmed * Allow setting the release date when switching to confirmed * Only allow release date or text, not both * Prevent release calendar page deletion through the UI * Move the changes to the release date below contact details e.g. https://www.ons.gov.uk/releases/disabilitypaygapsintheuk2014to2023 * Switch to using a StreamField for the release page related links * Disable the "too-few-public-methods" pylint check globally * Make the equation/ONS embed check reusable --- .pylintrc | 4 + Makefile | 1 + cms/analysis/__init__.py | 0 cms/analysis/blocks.py | 59 +++++ cms/analysis/migrations/0001_initial.py | 104 ++++++++ cms/analysis/migrations/__init__.py | 0 cms/analysis/models.py | 233 ++++++++++++++++++ cms/analysis/tests/__init__.py | 0 cms/analysis/tests/factories.py | 73 ++++++ cms/analysis/tests/test_models.py | 219 ++++++++++++++++ cms/core/blocks/__init__.py | 2 + cms/core/blocks/embeddable.py | 8 +- cms/core/blocks/headline_figures.py | 25 ++ cms/core/blocks/markup.py | 8 +- cms/core/blocks/panels.py | 2 +- cms/core/blocks/related.py | 8 +- cms/core/blocks/stream_blocks.py | 2 +- cms/core/models/base.py | 29 +++ cms/core/models/snippets.py | 7 +- cms/core/tests/factories.py | 52 ++++ cms/core/wagtail_hooks.py | 35 +++ cms/jinja2/assets/icons/data-analysis.svg | 1 + cms/jinja2/assets/icons/identity.svg | 1 + cms/jinja2/assets/icons/news.svg | 1 + cms/jinja2/templates/base_page.html | 28 ++- .../streamfield/analysis_section_block.html | 5 + .../streamfield/headline_figures_block.html | 17 ++ .../components/streamfield/stream_block.html | 6 +- .../analysis_page--previous-releases.html | 12 + cms/jinja2/templates/pages/analysis_page.html | 140 +++++++++++ cms/jinja2/templates/pages/theme_page.html | 21 ++ cms/jinja2/templates/pages/topic_page.html | 21 ++ .../templates/partials/content_scripts.html | 10 + cms/release_calendar/blocks.py | 14 +- cms/release_calendar/tests/factories.py | 2 +- cms/release_calendar/tests/test_models.py | 20 +- cms/settings/base.py | 6 + cms/themes/__init__.py | 0 cms/themes/migrations/0001_initial.py | 61 +++++ cms/themes/migrations/__init__.py | 0 cms/themes/models.py | 24 ++ cms/themes/tests/factories.py | 16 ++ cms/topics/__init__.py | 0 cms/topics/migrations/0001_initial.py | 61 +++++ cms/topics/migrations/__init__.py | 0 cms/topics/models.py | 24 ++ cms/topics/tests/factories.py | 16 ++ 47 files changed, 1339 insertions(+), 39 deletions(-) create mode 100644 cms/analysis/__init__.py create mode 100644 cms/analysis/blocks.py create mode 100644 cms/analysis/migrations/0001_initial.py create mode 100644 cms/analysis/migrations/__init__.py create mode 100644 cms/analysis/models.py create mode 100644 cms/analysis/tests/__init__.py create mode 100644 cms/analysis/tests/factories.py create mode 100644 cms/analysis/tests/test_models.py create mode 100644 cms/core/blocks/headline_figures.py create mode 100644 cms/core/tests/factories.py create mode 100644 cms/core/wagtail_hooks.py create mode 100644 cms/jinja2/assets/icons/data-analysis.svg create mode 100644 cms/jinja2/assets/icons/identity.svg create mode 100644 cms/jinja2/assets/icons/news.svg create mode 100644 cms/jinja2/templates/components/streamfield/analysis_section_block.html create mode 100644 cms/jinja2/templates/components/streamfield/headline_figures_block.html create mode 100644 cms/jinja2/templates/pages/analysis_page--previous-releases.html create mode 100644 cms/jinja2/templates/pages/analysis_page.html create mode 100644 cms/jinja2/templates/pages/theme_page.html create mode 100644 cms/jinja2/templates/pages/topic_page.html create mode 100644 cms/jinja2/templates/partials/content_scripts.html create mode 100644 cms/themes/__init__.py create mode 100644 cms/themes/migrations/0001_initial.py create mode 100644 cms/themes/migrations/__init__.py create mode 100644 cms/themes/models.py create mode 100644 cms/themes/tests/factories.py create mode 100644 cms/topics/__init__.py create mode 100644 cms/topics/migrations/0001_initial.py create mode 100644 cms/topics/migrations/__init__.py create mode 100644 cms/topics/models.py create mode 100644 cms/topics/tests/factories.py diff --git a/.pylintrc b/.pylintrc index 5f67722..ec09010 100644 --- a/.pylintrc +++ b/.pylintrc @@ -435,12 +435,16 @@ disable=raw-checker-failed, wrong-import-order, missing-module-docstring, too-many-ancestors, + too-few-public-methods, + missing-class-docstring, fixme # note: # - wrong-import-order: covered by ruff # - missing-module-docstring: because they should be self-explanatory # - too-many-ancestors: because of our and Wagtail's use of mixins +# - too-few-public-methods: because of Django Meta classes +# - missing-class-docstring: mostly because of Django classes. # - fixme: because we want to leave TODO notes for future features. # Enable the message, report, category or checker with the given id(s). You can diff --git a/Makefile b/Makefile index 725592a..d5b7e24 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ lint: lint-py lint-html lint-frontend ## Run all linters (python, html, front-en .PHONY: lint-py lint-py: ## Run all Python linters (ruff/pylint/mypy). poetry run ruff check . + poetry run ruff format --check . find . -type f -name "*.py" | xargs poetry run pylint --reports=n --output-format=colorized --rcfile=.pylintrc --django-settings-module=cms.settings.production -j 0 make mypy diff --git a/cms/analysis/__init__.py b/cms/analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/analysis/blocks.py b/cms/analysis/blocks.py new file mode 100644 index 0000000..4a55728 --- /dev/null +++ b/cms/analysis/blocks.py @@ -0,0 +1,59 @@ +from typing import TYPE_CHECKING, ClassVar + +from django.utils.text import slugify +from wagtail.blocks import RichTextBlock, StreamBlock, StructBlock +from wagtail.embeds.blocks import EmbedBlock +from wagtail.images.blocks import ImageChooserBlock +from wagtailmath.blocks import MathBlock + +from cms.core.blocks import ( + DocumentsBlock, + HeadingBlock, + ONSEmbedBlock, + PanelBlock, + RelatedContentBlock, + RelatedLinksBlock, +) + +if TYPE_CHECKING: + from wagtail.blocks import StructValue + + +class SectionContentBlock(StreamBlock): + """The analysis section content StreamField block definition.""" + + rich_text = RichTextBlock() + panel = PanelBlock() + image = ImageChooserBlock(group="Media") + documents = DocumentsBlock(group="Media") + embed = EmbedBlock(group="Media") + equation = MathBlock(group="DataVis", icon="decimal") + ons_embed = ONSEmbedBlock(group="DataVis", label="ONS General Embed") + related_links = RelatedLinksBlock(RelatedContentBlock(), icon="link") + + class Meta: + template = "templates/components/streamfield/stream_block.html" + block_counts: ClassVar[dict[str, dict]] = {"related_links": {"max_num": 1}} + + +class SectionBlock(StructBlock): + """The analysis section block definition.""" + + title = HeadingBlock() + content = SectionContentBlock() + + def to_table_of_contents_items(self, value: "StructValue") -> list[dict[str, str]]: + """Convert the value to the table of contents component macro format.""" + return [{"url": "#" + slugify(value["title"]), "text": value["title"]}] + + class Meta: + template = "templates/components/streamfield/analysis_section_block.html" + + +class AnalysisStoryBlock(StreamBlock): + """The analysis StreamField block definition.""" + + section = SectionBlock() + + class Meta: + template = "templates/components/streamfield/stream_block.html" diff --git a/cms/analysis/migrations/0001_initial.py b/cms/analysis/migrations/0001_initial.py new file mode 100644 index 0000000..c794d46 --- /dev/null +++ b/cms/analysis/migrations/0001_initial.py @@ -0,0 +1,104 @@ +# Generated by Django 5.1.2 on 2024-11-04 17:20 + +import django.db.models.deletion +import wagtail.contrib.routable_page.models +import wagtail.fields +from django.db import migrations, models + +import cms.core.fields + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("core", "0002_contactdetails"), + ("images", "0002_customimage_description"), + ("wagtailcore", "0094_alter_page_locale"), + ] + + operations = [ + migrations.CreateModel( + name="AnalysisSeries", + 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", + ), + ), + ], + options={ + "abstract": False, + }, + bases=(wagtail.contrib.routable_page.models.RoutablePageMixin, "wagtailcore.page"), + ), + migrations.CreateModel( + name="AnalysisPage", + 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", + ), + ), + ("listing_title", models.CharField(blank=True, max_length=255)), + ("listing_summary", models.CharField(blank=True, max_length=255)), + ("social_text", models.CharField(blank=True, max_length=255)), + ("news_headline", models.CharField(blank=True, max_length=255)), + ("summary", wagtail.fields.RichTextField()), + ("main_points_summary", wagtail.fields.RichTextField()), + ("release_date", models.DateField()), + ("next_release_date", models.DateField(blank=True, null=True)), + ("is_accredited", models.BooleanField(default=False)), + ("is_census", models.BooleanField(default=False)), + ("headline_figures", cms.core.fields.StreamField(blank=True, block_lookup={})), + ("content", cms.core.fields.StreamField(block_lookup={})), + ("show_cite_this_page", models.BooleanField(default=True)), + ( + "contact_details", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="core.contactdetails", + ), + ), + ( + "listing_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "social_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page", models.Model), + ), + ] diff --git a/cms/analysis/migrations/__init__.py b/cms/analysis/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/analysis/models.py b/cms/analysis/models.py new file mode 100644 index 0000000..1199b3b --- /dev/null +++ b/cms/analysis/models.py @@ -0,0 +1,233 @@ +from typing import TYPE_CHECKING, Any, ClassVar, Optional + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db import models +from django.http import Http404 +from django.shortcuts import redirect +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ +from wagtail.admin.panels import FieldPanel, FieldRowPanel, HelpPanel, MultiFieldPanel, TitleFieldPanel +from wagtail.contrib.routable_page.models import RoutablePageMixin, path +from wagtail.fields import RichTextField +from wagtail.models import Page +from wagtail.search import index + +from cms.analysis.blocks import AnalysisStoryBlock +from cms.core.blocks import HeadlineFiguresBlock +from cms.core.fields import StreamField +from cms.core.models import BasePage + +if TYPE_CHECKING: + from django.http import HttpRequest + from django.http.response import HttpResponseRedirect + from django.template.response import TemplateResponse + from wagtail.admin.panels import Panel + + +class AnalysisSeries(RoutablePageMixin, Page): + """The analysis series model.""" + + parent_page_types: ClassVar[list[str]] = ["topics.TopicPage"] + subpage_types: ClassVar[list[str]] = ["AnalysisPage"] + preview_modes: ClassVar[list[str]] = [] # Disabling the preview mode due to it being a container page. + page_description = _("A container for Analysis series") + + content_panels: ClassVar[list["Panel"]] = [ + *Page.content_panels, + HelpPanel( + content=_( + "This is a container for Analysis series. It provides the /latest," + "/previous-release evergreen paths, as well as the actual analysis pages. " + "Add a new Analysis page under this container." + ) + ), + ] + + def get_latest(self) -> Optional["AnalysisPage"]: + """Returns the latest published analysis page.""" + latest: AnalysisPage | None = AnalysisPage.objects.live().child_of(self).order_by("-release_date").first() + return latest + + @path("") + def index(self, request: "HttpRequest") -> "HttpResponseRedirect": + """Redirect to /latest as this is a container page without its own content.""" + return redirect(self.get_url(request) + self.reverse_subpage("latest_release")) + + @path("latest/") + def latest_release(self, request: "HttpRequest") -> "TemplateResponse": + """Serves the latest analysis page in the series.""" + latest = self.get_latest() + if not latest: + raise Http404 + response: TemplateResponse = latest.serve(request) + return response + + @path("previous-releases/") + def previous_releases(self, request: "HttpRequest") -> "TemplateResponse": + """Render the previous releases template.""" + response: TemplateResponse = self.render( + request, + # TODO: update to include drafts when looking at previews holistically. + context_overrides={"pages": AnalysisPage.objects.live().child_of(self).order_by("-release_date")}, + template="templates/pages/analysis_page--previous-releases.html", + ) + return response + + +class AnalysisPage(BasePage): # type: ignore[django-manager-missing] + """The analysis page model.""" + + parent_page_types: ClassVar[list[str]] = ["AnalysisSeries"] + subpage_types: ClassVar[list[str]] = [] + template = "templates/pages/analysis_page.html" + + # Fields + news_headline = models.CharField(max_length=255, blank=True) + summary = RichTextField(features=settings.RICH_TEXT_BASIC) + + main_points_summary = RichTextField( + features=settings.RICH_TEXT_BASIC, help_text=_("Used when featured on a topic page.") + ) + + # Fields: dates + release_date = models.DateField() + next_release_date = models.DateField(blank=True, null=True) + + contact_details = models.ForeignKey( + "core.ContactDetails", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + ) + + # Fields: accredited/census. A bit of "about the data". + is_accredited = models.BooleanField( + _("Accredited Official Statistics"), + default=False, + help_text=_( + "If ticked, will display an information block about the data being accredited official statistics " + "and include the accredited logo." + ), + ) + is_census = models.BooleanField( + _("Census"), + default=False, + help_text=_("If ticked, will display an information block about the data being related to the Census."), + ) + + # Fields: content + headline_figures = StreamField([("figures", HeadlineFiguresBlock())], blank=True, max_num=1) + content = StreamField(AnalysisStoryBlock()) + + show_cite_this_page = models.BooleanField(default=True) + + content_panels: ClassVar[list["Panel"]] = [ + MultiFieldPanel( + [ + TitleFieldPanel("title", help_text=_("Also known as the release edition. e.g. 'November 2024'.")), + FieldPanel( + "news_headline", + help_text=( + "Use this as a news headline. When populated, replaces the title displayed on the page. " + "Note: the page slug is powered by the title field. " + "You can change the slug in the 'Promote' tab." + ), + icon="news", + ), + ], + heading="Title", + ), + FieldPanel("summary"), + MultiFieldPanel( + [ + FieldPanel("release_date", icon="calendar-date"), + FieldPanel( + "next_release_date", + help_text=_("If no next date is chosen, 'To be announced' will be displayed."), + ), + FieldRowPanel( + [FieldPanel("is_accredited"), FieldPanel("is_census")], + heading=_("About the data"), + ), + FieldPanel("contact_details"), + FieldPanel("show_cite_this_page"), + FieldPanel("main_points_summary"), + ], + heading=_("Metadata"), + icon="cog", + ), + FieldPanel("headline_figures", icon="data-analysis"), + FieldPanel("content", icon="list-ul"), + ] + + search_fields: ClassVar[list[index.SearchField | index.AutocompleteField]] = [ + *BasePage.search_fields, + index.SearchField("summary"), + index.SearchField("headline_figures"), + index.SearchField("content"), + index.SearchField("get_admin_display_title", boost=2), + index.AutocompleteField("get_admin_display_title"), + index.SearchField("news_headline"), + index.AutocompleteField("news_headline"), + ] + + def clean(self) -> None: + """Additional validation on save.""" + super().clean() + + if self.next_release_date and self.next_release_date <= self.release_date: + raise ValidationError({"next_release_date": _("The next release date must be after the release date.")}) + + def get_context(self, request: "HttpRequest", *args: Any, **kwargs: Any) -> dict: + """Additional context for the template.""" + context: dict = super().get_context(request, *args, **kwargs) + context["table_of_contents"] = self.table_of_contents + return context + + def get_admin_display_title(self) -> str: + """Changes the admin display title to include the parent title.""" + return f"{self.get_parent().title}: {self.draft_title or self.title}" + + @property + def display_title(self) -> str: + """Returns the page display title. If the news headline is set, it takes precedence over the series+title.""" + return self.news_headline.strip() or self.get_admin_display_title() + + @cached_property + def table_of_contents(self) -> list[dict[str, str | object]]: + """Table of contents formatted to Design System specs.""" + items = [] + for block in self.content: # pylint: disable=not-an-iterable,useless-suppression + if hasattr(block.block, "to_table_of_contents_items"): + items += block.block.to_table_of_contents_items(block.value) + if self.show_cite_this_page: + items += [{"url": "#cite-this-page", "text": _("Cite this analysis")}] + if self.contact_details_id: + items += [{"url": "#contact-details", "text": _("Contact details")}] + return items + + @property + def is_latest(self) -> bool: + """Returns True if the analysis page is latest in its series based on the release date.""" + latest_id = ( + AnalysisPage.objects.sibling_of(self).live().order_by("-release_date").values_list("id", flat=True).first() + ) + return bool(self.pk == latest_id) # to placate mypy + + @cached_property + def has_equations(self) -> bool: + """Checks if there are any equation blocks.""" + return any( + block.value["content"].first_block_by_name(block_name="equation") is not None + for block in self.content # pylint: disable=not-an-iterable + ) + + @cached_property + def has_ons_embed(self) -> bool: + """Checks if there are any ONS embed blocks.""" + return any( + block.value["content"].first_block_by_name(block_name="ons_embed") is not None + for block in self.content # pylint: disable=not-an-iterable + ) diff --git a/cms/analysis/tests/__init__.py b/cms/analysis/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/analysis/tests/factories.py b/cms/analysis/tests/factories.py new file mode 100644 index 0000000..c82fbf5 --- /dev/null +++ b/cms/analysis/tests/factories.py @@ -0,0 +1,73 @@ +from datetime import timedelta +from typing import ClassVar + +import factory +import wagtail_factories +from django.utils import timezone + +from cms.analysis.models import AnalysisPage, AnalysisSeries +from cms.core.tests.factories import ContactDetailsFactory, RichTextBlockFactory +from cms.topics.tests.factories import TopicPageFactory + + +class HeadlineFigureBlockFactory(wagtail_factories.StructBlockFactory): + """Factory for HeadlineFigure block.""" + + title = wagtail_factories.CharBlockFactory() + figure = wagtail_factories.CharBlockFactory() + trend = wagtail_factories.CharBlockFactory() + + +class SectionContentBlockFactory(wagtail_factories.StructBlockFactory): + """Factory for Section content block.""" + + title = factory.Faker("text", max_nb_chars=50) + content = wagtail_factories.StreamFieldFactory( + { + "rich_text": factory.SubFactory(RichTextBlockFactory), + } + ) + + +class SectionBlockFactory(wagtail_factories.StructBlockFactory): + """Factory for Section block.""" + + title = factory.Faker("text", max_nb_chars=50) + content = factory.SubFactory(SectionContentBlockFactory) + + +class AnalysisSeriesFactory(wagtail_factories.PageFactory): + """Factory for AnalysisSeries.""" + + class Meta: + model = AnalysisSeries + + title = factory.Faker("sentence", nb_words=4) + parent = factory.SubFactory(TopicPageFactory) + + +class AnalysisPageFactory(wagtail_factories.PageFactory): + """Factory for AnalysisPage.""" + + class Meta: + model = AnalysisPage + django_get_or_create: ClassVar[list[str]] = ["slug", "parent"] + + title = factory.Faker("sentence", nb_words=4) + parent = factory.SubFactory(AnalysisSeriesFactory) + + summary = factory.Faker("text", max_nb_chars=100) + news_headline = factory.Faker("text", max_nb_chars=50) + main_points_summary = factory.Faker("text", max_nb_chars=200) + release_date = factory.LazyFunction(lambda: timezone.now().date()) + next_release_date = factory.LazyAttribute(lambda o: o.release_date + timedelta(days=1)) + contact_details = factory.SubFactory(ContactDetailsFactory) + + headline_figures = wagtail_factories.StreamFieldFactory( + {"headline_figure": factory.SubFactory(HeadlineFigureBlockFactory)} + ) + content = wagtail_factories.StreamFieldFactory({"section": factory.SubFactory(SectionBlockFactory)}) + + is_accredited = False + is_census = False + show_cite_this_page = True diff --git a/cms/analysis/tests/test_models.py b/cms/analysis/tests/test_models.py new file mode 100644 index 0000000..af6524b --- /dev/null +++ b/cms/analysis/tests/test_models.py @@ -0,0 +1,219 @@ +from datetime import datetime + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.test import TestCase +from django.utils import timezone +from django.utils.formats import date_format +from wagtail.test.utils import WagtailTestUtils + +from cms.analysis.tests.factories import AnalysisPageFactory, AnalysisSeriesFactory +from cms.core.tests.factories import ContactDetailsFactory + + +class AnalysisSeriesTestCase(WagtailTestUtils, TestCase): + """Test AnalysisSeries model methods.""" + + @classmethod + def setUpTestData(cls): + cls.series = AnalysisSeriesFactory() + + def test_index_redirect_404_with_no_subpages(self): + """Test index path redirects to latest.""" + response = self.client.get(self.series.url) + self.assertRedirects( + response, self.series.url + self.series.reverse_subpage("latest_release"), target_status_code=404 + ) + + def test_index_redirects_to_latest(self): + """Checks that the series will redirect to /latest.""" + AnalysisPageFactory(parent=self.series) + response = self.client.get(self.series.url) + self.assertRedirects(response, self.series.url + self.series.reverse_subpage("latest_release")) + + def test_get_latest_no_subpages(self): + """Test get_latest returns None when no pages exist.""" + self.assertIsNone(self.series.get_latest()) + + def test_get_latest_with_subpages(self): + """Test get_latest returns the most recent analysis page.""" + AnalysisPageFactory(parent=self.series, release_date=timezone.now().date()) + latest_page = AnalysisPageFactory( + parent=self.series, + release_date=timezone.now().date() + timezone.timedelta(days=1), + ) + + self.assertEqual(self.series.get_latest(), latest_page) + + def test_latest_release_404(self): + """Test latest_release returns 404 when no pages exist.""" + response = self.client.get(self.series.url + "latest/") + self.assertEqual(response.status_code, 404) + + def test_latest_release_success(self): + """Test latest_release returns the latest page.""" + analysis_page = AnalysisPageFactory(parent=self.series) + series_response = self.client.get(self.series.url + "latest/") + self.assertEqual(series_response.status_code, 200) + + analysis_page_response = self.client.get(analysis_page.url) + self.assertEqual(analysis_page_response.status_code, 200) + + self.assertEqual(series_response.context, analysis_page_response.context) + + +class AnalysisPageTestCase(WagtailTestUtils, TestCase): + """Test AnalysisPage model properties and methods.""" + + def setUp(self): + self.page = AnalysisPageFactory( + parent__title="PSF", + title="November 2024", + news_headline="", + contact_details=None, + show_cite_this_page=False, + ) + + def test_display_title(self): + """Test display_title returns admin title when no news_headline.""" + self.assertEqual(self.page.display_title, self.page.get_admin_display_title()) + self.assertEqual(self.page.display_title, "PSF: November 2024") + self.assertEqual(self.page.display_title, f"{self.page.get_parent().title}: {self.page.title}") + + def test_display_title_with_news_headline(self): + """Test display_title returns news_headline when set.""" + self.page.news_headline = "Breaking News" + self.assertEqual(self.page.display_title, "Breaking News") + + def test_table_of_contents_with_content(self): + """Test table_of_contents with content blocks.""" + self.page.content = [ + {"type": "section", "value": {"title": "Test Section", "content": [{"type": "rich_text", "value": "text"}]}} + ] + + self.assertListEqual(self.page.table_of_contents, [{"url": "#test-section", "text": "Test Section"}]) + + def test_table_of_contents_with_cite_this_page(self): + """Test table_of_contents includes cite this page when enabled.""" + self.page.show_cite_this_page = True + + toc = self.page.table_of_contents + self.assertIn({"url": "#cite-this-page", "text": "Cite this analysis"}, toc) + + def test_table_of_contents_with_contact_details(self): + """Test table_of_contents includes contact details when present.""" + self.page.contact_details = ContactDetailsFactory() + toc = self.page.table_of_contents + self.assertIn({"url": "#contact-details", "text": "Contact details"}, toc) + + def test_is_latest(self): + """Test is_latest returns True for most recent page.""" + self.assertTrue(self.page.is_latest) + # now add a release, but make it older + AnalysisPageFactory( + parent=self.page.get_parent(), + release_date=self.page.release_date - timezone.timedelta(days=1), + ) + self.assertTrue(self.page.is_latest) + + def test_is_latest__unhappy_path(self): + """Test is_latest returns False when newer pages exist.""" + AnalysisPageFactory( + parent=self.page.get_parent(), + release_date=self.page.release_date + timezone.timedelta(days=1), + ) + self.assertFalse(self.page.is_latest) + + def test_has_equations(self): + """Test has_equations property.""" + self.assertFalse(self.page.has_equations) + self.page.content = [ + { + "type": "section", + "value": {"title": "Test Section", "content": [{"type": "equation", "value": "$$y = mx + b$$"}]}, + } + ] + del self.page.has_equations # clear cached property + self.assertTrue(self.page.has_equations) + + def test_has_ons_embed(self): + """Test has_ons_embed property.""" + self.assertFalse(self.page.has_ons_embed) + self.page.content = [ + { + "type": "section", + "value": { + "title": "Test Section", + "content": [{"type": "ons_embed", "value": {"url": "https://ons.gov.uk/embed"}}], + }, + } + ] + del self.page.has_ons_embed # clear cached property + self.assertTrue(self.page.has_ons_embed) + + def test_next_date_must_be_after_release_date(self): + """Tests the model validates next release date is after the release date.""" + self.page.next_release_date = self.page.release_date + with self.assertRaises(ValidationError) as info: + self.page.clean() + + self.assertListEqual(info.exception.messages, ["The next release date must be after the release date."]) + + +class AnalysisPageRenderTestCase(WagtailTestUtils, TestCase): + """Test AnalysisPage model properties and methods.""" + + def setUp(self): + self.basic_page = AnalysisPageFactory( + parent__title="PSF", + title="November 2024", + news_headline="", + contact_details=None, + show_cite_this_page=False, + next_release_date=None, + ) + self.basic_page_url = self.basic_page.url + + # a page with more of the fields filled in + self.page = AnalysisPageFactory( + parent=self.basic_page.get_parent(), + title="August 2024", + news_headline="Breaking News!", + release_date=datetime(2024, 8, 15), + ) + self.page_url = self.page.url + self.formatted_date = date_format(self.page.release_date, settings.DATE_FORMAT) + + def test_display_title(self): + """Check how the title is displayed on the front-end, with/without news headline.""" + response = self.client.get(self.basic_page_url) + self.assertContains(response, "PSF: November 2024") + + response = self.client.get(self.page_url) + self.assertNotContains(response, self.page.get_admin_display_title()) + self.assertContains(response, "Breaking News!") + + def test_next_release_date(self): + """Checks that when no next release date, the template shows 'To be announced'.""" + response = self.client.get(self.basic_page_url) + self.assertContains(response, "To be announced") + + response = self.client.get(self.page_url) + self.assertNotContains(response, "To be announced") + self.assertContains(response, self.formatted_date) + + def test_cite_this_page_is_shown_when_ticked(self): + """Test for the cite this page block.""" + expected = ( + f"Office for National Statistics (ONS), released { self.formatted_date }, " + f'ONS website, analysis, Breaking News!' + ) + response = self.client.get(self.page_url) + self.assertContains(response, expected) + + def test_cite_this_page_is_not_shown_when_unticked(self): + """Test for the cite this page block not present in the template.""" + expected = f"Office for National Statistics (ONS), released { self.formatted_date }, ONS website, analysis" + + response = self.client.get(self.basic_page_url) + self.assertNotContains(response, expected) diff --git a/cms/core/blocks/__init__.py b/cms/core/blocks/__init__.py index d460689..03191a8 100644 --- a/cms/core/blocks/__init__.py +++ b/cms/core/blocks/__init__.py @@ -1,4 +1,5 @@ from .embeddable import DocumentBlock, DocumentsBlock, ImageBlock, ONSEmbedBlock +from .headline_figures import HeadlineFiguresBlock from .markup import BasicTableBlock, HeadingBlock, QuoteBlock from .panels import PanelBlock from .related import LinkBlock, RelatedContentBlock, RelatedLinksBlock @@ -8,6 +9,7 @@ "DocumentBlock", "DocumentsBlock", "HeadingBlock", + "HeadlineFiguresBlock", "ImageBlock", "LinkBlock", "ONSEmbedBlock", diff --git a/cms/core/blocks/embeddable.py b/cms/core/blocks/embeddable.py index 33656ac..7e73d98 100644 --- a/cms/core/blocks/embeddable.py +++ b/cms/core/blocks/embeddable.py @@ -19,7 +19,7 @@ class ImageBlock(blocks.StructBlock): image = ImageChooserBlock() caption = blocks.CharBlock(required=False) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "image" template = "templates/components/streamfield/image_block.html" @@ -50,7 +50,7 @@ class DocumentBlock(blocks.StructBlock): title = blocks.CharBlock(required=False) description = blocks.RichTextBlock(features=settings.RICH_TEXT_BASIC, required=False) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "doc-full-inverse" value_class = DocumentBlockStructValue template = "templates/components/streamfield/document_block.html" @@ -67,7 +67,7 @@ def get_context(self, value: "StreamValue", parent_context: dict | None = None) context["macro_data"] = [document.value.as_macro_data() for document in value] return context - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: block_counts: ClassVar[dict[str, dict]] = {"document": {"min_num": 1}} icon = "doc-full-inverse" template = "templates/components/streamfield/documents_block.html" @@ -93,6 +93,6 @@ def clean(self, value: "StructValue") -> "StructValue": return super().clean(value) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "code" template = "templates/components/streamfield/ons_embed_block.html" diff --git a/cms/core/blocks/headline_figures.py b/cms/core/blocks/headline_figures.py new file mode 100644 index 0000000..5aeb321 --- /dev/null +++ b/cms/core/blocks/headline_figures.py @@ -0,0 +1,25 @@ +from typing import Any + +from django.utils.translation import gettext as _ +from wagtail.blocks import CharBlock, ListBlock, StructBlock + + +class HeadlineFiguresItemBlock(StructBlock): + """Represents a headline figure.""" + + title = CharBlock(label=_("Title"), max_length=60, required=True) + figure = CharBlock(label=_("Figure"), max_length=10, required=True) + supporting_text = CharBlock(label=_("Supporting text"), max_length=100, required=True) + + +class HeadlineFiguresBlock(ListBlock): + """A list of headline figures.""" + + def __init__(self, search_index: bool = True, **kwargs: Any) -> None: + kwargs.setdefault("max_num", 4) + super().__init__(HeadlineFiguresItemBlock, search_index=search_index, **kwargs) + + class Meta: + icon = "data-analysis" + label = _("Headline figures") + template = "templates/components/streamfield/headline_figures_block.html" diff --git a/cms/core/blocks/markup.py b/cms/core/blocks/markup.py index 83de183..b5304e4 100644 --- a/cms/core/blocks/markup.py +++ b/cms/core/blocks/markup.py @@ -13,7 +13,7 @@ class HeadingBlock(blocks.CharBlock): """Section heading block.""" - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "title" form_classname = "title" template = "templates/components/streamfield/heading_block.html" @@ -32,7 +32,7 @@ def get_context(self, value: Any, parent_context: dict[str, Any] | None = None) return context def to_table_of_contents_items(self, value: Any) -> list[dict]: - """Convert the value to the TOC macro format.""" + """Convert the value to the table of contents component macro format.""" return [{"url": "#" + slugify(value), "text": value}] @@ -42,7 +42,7 @@ class QuoteBlock(blocks.StructBlock): quote = blocks.CharBlock(form_classname="title") attribution = blocks.CharBlock(required=False) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "openquote" template = "templates/components/streamfield/quote_block.html" @@ -50,7 +50,7 @@ class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods class BasicTableBlock(WagtailTableBlock): """Provides a basic table block with data processed for Design System components.""" - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "table" template = "templates/components/streamfield/table_block.html" label = "Basic table" diff --git a/cms/core/blocks/panels.py b/cms/core/blocks/panels.py index b7104d1..39cf4c8 100644 --- a/cms/core/blocks/panels.py +++ b/cms/core/blocks/panels.py @@ -26,6 +26,6 @@ class PanelBlock(blocks.StructBlock): body = blocks.RichTextBlock(features=settings.RICH_TEXT_BASIC) title = blocks.CharBlock(required=False, label="Title (optional)") - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: label = "Warning or information panel" template = "templates/components/streamfield/panel_block.html" diff --git a/cms/core/blocks/related.py b/cms/core/blocks/related.py index 524aac9..5932559 100644 --- a/cms/core/blocks/related.py +++ b/cms/core/blocks/related.py @@ -1,8 +1,8 @@ -from functools import cached_property from typing import TYPE_CHECKING, Any from django.core.exceptions import ValidationError from django.forms.utils import ErrorList +from django.utils.functional import cached_property from django.utils.text import slugify from django.utils.translation import gettext as _ from wagtail.blocks import ( @@ -56,7 +56,7 @@ class LinkBlock(StructBlock): required=False, ) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "link" value_class = LinkBlockStructValue @@ -112,10 +112,10 @@ def get_context(self, value: "ListValue", parent_context: dict | None = None) -> return context - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: icon = "list-ul" template = "templates/components/streamfield/related_links_block.html" def to_table_of_contents_items(self, _value: "ListValue") -> list[dict[str, str]]: - """Returns the TOC macro data.""" + """Returns the table of contents component macro data.""" return [{"url": "#" + self.slug, "text": self.heading}] diff --git a/cms/core/blocks/stream_blocks.py b/cms/core/blocks/stream_blocks.py index 8c9d2cc..505b6a8 100644 --- a/cms/core/blocks/stream_blocks.py +++ b/cms/core/blocks/stream_blocks.py @@ -27,5 +27,5 @@ class CoreStoryBlock(StreamBlock): equation = MathBlock(group="DataVis", icon="decimal") ons_embed = ONSEmbedBlock(group="DataVis", label="ONS General Embed") - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: block_counts: ClassVar[dict[str, dict]] = {"related_links": {"max_num": 1}} diff --git a/cms/core/models/base.py b/cms/core/models/base.py index 0afe051..e4d442d 100644 --- a/cms/core/models/base.py +++ b/cms/core/models/base.py @@ -25,6 +25,9 @@ class BasePage(ListingFieldsMixin, SocialFieldsMixin, Page): # type: ignore[dja """Base page class with listing and social fields additions as well as cache decorators.""" show_in_menus_default = True + # Used to check for the existence of equation and ONS embed blocks. + # Update in your specific Page class if the StreamField using them is different. + content_field_name: str = "content" class Meta: abstract = True @@ -50,3 +53,29 @@ def related_pages(self) -> PageQuerySet: pks=ordered_page_pks, exclude_non_matches=True, ) + + @cached_property + def has_equations(self) -> bool: + """Checks if there are any equation blocks. + Override in your specific Page class if the StreamField structure is different. + """ + if streamfield := getattr(self, self.content_field_name): + try: + return streamfield.first_block_by_name(block_name="equation") is not None + except AttributeError: + return False + + return False + + @cached_property + def has_ons_embed(self) -> bool: + """Checks if there are any ONS embed blocks. + Override in your specific Page class if the StreamField structure is different. + """ + if streamfield := getattr(self, self.content_field_name): + try: + return streamfield.first_block_by_name(block_name="ons_embed") is not None + except AttributeError: + return False + + return False diff --git a/cms/core/models/snippets.py b/cms/core/models/snippets.py index c3508b1..33e0b86 100644 --- a/cms/core/models/snippets.py +++ b/cms/core/models/snippets.py @@ -3,12 +3,13 @@ from django.db import models from wagtail.admin.panels import FieldPanel from wagtail.search import index -from wagtail.snippets.models import register_snippet -@register_snippet class ContactDetails(index.Indexed, models.Model): - """A model for contact details.""" + """A model for contact details. + + Note that this is registered as a snippet in core.wagtail_hooks to allow customising the icon. + """ name = models.CharField(max_length=255) email = models.EmailField() diff --git a/cms/core/tests/factories.py b/cms/core/tests/factories.py new file mode 100644 index 0000000..fba8df4 --- /dev/null +++ b/cms/core/tests/factories.py @@ -0,0 +1,52 @@ +import factory +from wagtail import blocks +from wagtail.rich_text import RichText +from wagtail_factories.blocks import BlockFactory + +from cms.core.models import ContactDetails + + +class DateTimeBlockFactory(BlockFactory): + """A factory for DateTimeBlock.""" + + class Meta: + model = blocks.DateTimeBlock + + +class DateBlockFactory(BlockFactory): + """A factory for DateBlock.""" + + class Meta: + model = blocks.DateBlock + + +class URLBlockFactory(BlockFactory): + """A factory for URLBlock.""" + + class Meta: + model = blocks.URLBlock + + +class RichTextBlockFactory(BlockFactory): + """A factory for RichTextBlock.""" + + class Meta: + model = blocks.RichTextBlock + + @classmethod + def _construct_block(cls, block_class, *args, **kwargs): + if value := kwargs.get("value"): + if not isinstance(value, RichText): + value = RichText(value) + return block_class().clean(value) + return block_class().get_default() + + +class ContactDetailsFactory(factory.django.DjangoModelFactory): + """Factory for ContactDetails.""" + + class Meta: + model = ContactDetails + + name = factory.Faker("name") + email = factory.Faker("email") diff --git a/cms/core/wagtail_hooks.py b/cms/core/wagtail_hooks.py new file mode 100644 index 0000000..5954386 --- /dev/null +++ b/cms/core/wagtail_hooks.py @@ -0,0 +1,35 @@ +from wagtail import hooks +from wagtail.snippets.models import register_snippet +from wagtail.snippets.views.snippets import SnippetViewSet + +from cms.core.models import ContactDetails + + +@hooks.register("register_icons") +def register_icons(icons: list[str]) -> list[str]: + """Registers custom icons. + + Sources: + - https://service-manual.ons.gov.uk/brand-guidelines/iconography/icon-set + """ + return [ + *icons, + "data-analysis.svg", + "identity.svg", + "news.svg", + ] + + +class ContactDetailsViewSet(SnippetViewSet): + """A snippet viewset for ContactDetails. + + See: + - https://docs.wagtail.org/en/stable/topics/snippets/registering.html + - https://docs.wagtail.org/en/stable/topics/snippets/customizing.html#icon + """ + + model = ContactDetails + icon = "identity" + + +register_snippet(ContactDetailsViewSet) diff --git a/cms/jinja2/assets/icons/data-analysis.svg b/cms/jinja2/assets/icons/data-analysis.svg new file mode 100644 index 0000000..5d7018a --- /dev/null +++ b/cms/jinja2/assets/icons/data-analysis.svg @@ -0,0 +1 @@ + diff --git a/cms/jinja2/assets/icons/identity.svg b/cms/jinja2/assets/icons/identity.svg new file mode 100644 index 0000000..bf786e8 --- /dev/null +++ b/cms/jinja2/assets/icons/identity.svg @@ -0,0 +1 @@ + diff --git a/cms/jinja2/assets/icons/news.svg b/cms/jinja2/assets/icons/news.svg new file mode 100644 index 0000000..5c958d6 --- /dev/null +++ b/cms/jinja2/assets/icons/news.svg @@ -0,0 +1 @@ + diff --git a/cms/jinja2/templates/base_page.html b/cms/jinja2/templates/base_page.html index 8a99046..ed9f3e0 100644 --- a/cms/jinja2/templates/base_page.html +++ b/cms/jinja2/templates/base_page.html @@ -4,7 +4,7 @@ {% with current_site=wagtail_site() %} - + {% with social_image=page|social_image(current_site) %} {% if social_image %} @@ -23,7 +23,7 @@ {% endwith %} - + {% endwith %} @@ -33,3 +33,27 @@ {{ super() }} {% include "templates/components/navigation/breadcrumbs.html" %} {% endblock %} + +{% block pageContent %} + {# Adds a full-width section for the header area before the main page content #} + {# Changes the location of the
tag from the base template - ensures it surrounds the header area #} +
+ {# This will contain the hero on individual templates #} + {% block header_area %}{% endblock %} +
+
+
+ {# This is the main tag in the base template in the design system #} +
+ {% block main %}{% endblock %} +
+
+
+
+
+{% endblock %} + +{% block scripts %} + {{ super() }} + {% include "templates/partials/content_scripts.html" %} +{% endblock %} diff --git a/cms/jinja2/templates/components/streamfield/analysis_section_block.html b/cms/jinja2/templates/components/streamfield/analysis_section_block.html new file mode 100644 index 0000000..4d3801e --- /dev/null +++ b/cms/jinja2/templates/components/streamfield/analysis_section_block.html @@ -0,0 +1,5 @@ +
+

{{ value.title }}

+ + {% include_block value.content %} +
diff --git a/cms/jinja2/templates/components/streamfield/headline_figures_block.html b/cms/jinja2/templates/components/streamfield/headline_figures_block.html new file mode 100644 index 0000000..c84d365 --- /dev/null +++ b/cms/jinja2/templates/components/streamfield/headline_figures_block.html @@ -0,0 +1,17 @@ +
+

{{ _("Headline facts and figures") }}

+
+ {% for figure in value %} +
+
+

+ {{ figure.title }} +

+

{{ figure.figure }}

+ +

{{ figure.supporting_text }}

+
+
+ {% endfor %} +
+
diff --git a/cms/jinja2/templates/components/streamfield/stream_block.html b/cms/jinja2/templates/components/streamfield/stream_block.html index 8b9bb10..0ea3a95 100644 --- a/cms/jinja2/templates/components/streamfield/stream_block.html +++ b/cms/jinja2/templates/components/streamfield/stream_block.html @@ -1,3 +1,5 @@ -{% for block in value %} - {% include_block block %} +{% for content_block in value %} + {% with block_id = content_block.id %} + {% include_block content_block %} + {% endwith %} {% endfor %} diff --git a/cms/jinja2/templates/pages/analysis_page--previous-releases.html b/cms/jinja2/templates/pages/analysis_page--previous-releases.html new file mode 100644 index 0000000..15178b3 --- /dev/null +++ b/cms/jinja2/templates/pages/analysis_page--previous-releases.html @@ -0,0 +1,12 @@ +{% extends "templates/base_page.html" %} + +{% block main %} +

{{ _("Previous Releases") }}

+
    + {% for page in pages %} +
  • {{ page.title }} - {{ page.release_date }}
  • + {% else %} +

    {{ _("There are currently no releases") }}

    + {% endfor %} +
+{% endblock %} diff --git a/cms/jinja2/templates/pages/analysis_page.html b/cms/jinja2/templates/pages/analysis_page.html new file mode 100644 index 0000000..9730d52 --- /dev/null +++ b/cms/jinja2/templates/pages/analysis_page.html @@ -0,0 +1,140 @@ +{% extends "templates/base_page.html" %} + +{% from "components/panel/_macro.njk" import onsPanel %} +{% from "components/table-of-contents/_macro.njk" import onsTableOfContents %} +{% from "components/text-indent/_macro.njk" import onsTextIndent %} + +{% block header_area %} +
+
+ {% include "templates/components/navigation/breadcrumbs.html" %} +

{{ _("Analysis") }}

+ +
+
+

+ {{ page.display_title }} +

+ +

{{ page.summary|richtext() }}

+
+
+ {% if page.is_accredited %} + {% include "templates/components/accredited/accredited-logo.html" %} + {% endif %} +
+
+
+ + {% block release_meta %} +
+ {% if page.is_census %} + {% include "templates/components/census/census-logo.html" %} + {% endif %} +
+
+ {{ _("Release date") }}: + {{ page.release_date|date("DATE_FORMAT") }} +
+
+ {{ _("Edition") }}: + {{ _("Latest") }} +
+
+ {% if page.contact_details %} + {{ _("Contact") }}: + {{ page.contact_details.name }} + {% endif %} +
+
+
+
+ {{ _("Next release") }}: + {{ page.next_release_date|date("DATE_FORMAT") or _("To be announced") }} +
+
+ {{ _("Releases") }}: + {% if page.is_latest %} + {{ _("View previous releases") }} + {% else %} + {{ _("View latest release") }} + {% endif %} +
+
+
+ {% endblock %} +
+ + {% if page.headline_figures %} + {% include_block page.headline_figures %} + {% endif %} + + {{ super() }} + +{% endblock %} + +{% block main %} +
+
+
+ {% with toc_title=_("Contents"), toc_aria_label=_("Sections in this page") %} + {# fmt:off #} + {{- + onsTableOfContents({ + "title": toc_title, + "ariaLabel": toc_aria_label, + "itemsList": table_of_contents + }) + }} + {# fmt:on #} + {% endwith %} +
+ +
+ {# if there are no contact details on the page, we don't want the last + streamfield block to have a bottom margin. last_flush is used in stream_block.html #} + {% if page.contact_details %} + {% set last_flush = False %} + {% else %} + {% set last_flush = True %} + {% endif %} + + {% include_block page.content %} + + {% if page.show_cite_this_page %} +
+

{{ _("Cite this analysis") }}

+

+ {%- set cite_link -%} + {{ page.display_title }} + {% endset %} + {% trans trimmed release_date=page.release_date|date("DATE_FORMAT"), cite_link=cite_link %} + Office for National Statistics (ONS), released {{ release_date }}, ONS website, analysis, {{ cite_link }} + {% endtrans %} +

+
+ {% endif %} + + {% if page.contact_details %} +
+

{{ _("Contact details") }}

+ + {% call onsTextIndent() %} +

+ Name: {{ page.contact_details.name }} +

+

+ Email: {{ page.contact_details.email }} +

+ {% if page.contact_details.phone %} +

+ Telephone: {{ page.contact_details.phone }} +

+ {% endif %} + {% endcall %} +
+ {% endif %} +
+
+
+{% endblock %} diff --git a/cms/jinja2/templates/pages/theme_page.html b/cms/jinja2/templates/pages/theme_page.html new file mode 100644 index 0000000..df99834 --- /dev/null +++ b/cms/jinja2/templates/pages/theme_page.html @@ -0,0 +1,21 @@ +{% extends "templates/base_page.html" %} + +{% block header_area %} +
+
+ {% include "templates/components/navigation/breadcrumbs.html" %} +

{{ _("Theme") }}

+ +
+
+

+ {{ page.title }} +

+

{{ page.summary|richtext() }}

+
+
+
+
+ + {{ super() }} +{% endblock %} diff --git a/cms/jinja2/templates/pages/topic_page.html b/cms/jinja2/templates/pages/topic_page.html new file mode 100644 index 0000000..3b0e220 --- /dev/null +++ b/cms/jinja2/templates/pages/topic_page.html @@ -0,0 +1,21 @@ +{% extends "templates/base_page.html" %} + +{% block header_area %} +
+
+ {% include "templates/components/navigation/breadcrumbs.html" %} +

{{ _("Topic") }}

+ +
+
+

+ {{ page.title }} +

+

{{ page.summary|richtext() }}

+
+
+
+
+ + {{ super() }} +{% endblock %} diff --git a/cms/jinja2/templates/partials/content_scripts.html b/cms/jinja2/templates/partials/content_scripts.html new file mode 100644 index 0000000..b18cf67 --- /dev/null +++ b/cms/jinja2/templates/partials/content_scripts.html @@ -0,0 +1,10 @@ +{#- +This is a partial include to reuse on pages with equations/ONS embeds. +The conditionals should help with only adding the extra JavaScript when needed. +-#} +{% if page.has_equations %} + +{% endif %} +{% if page.has_ons_embed %} + +{% endif %} diff --git a/cms/release_calendar/blocks.py b/cms/release_calendar/blocks.py index d63705c..3d04da3 100644 --- a/cms/release_calendar/blocks.py +++ b/cms/release_calendar/blocks.py @@ -16,11 +16,11 @@ class ContentSectionBlock(blocks.StructBlock): title = blocks.CharBlock() links = blocks.ListBlock(RelatedContentBlock()) - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/release_content_section.html" def to_table_of_contents_items(self, value: "StructValue") -> list[dict[str, str]]: - """Convert the value to the TOC macro format.""" + """Convert the value to the table of contents component macro format.""" return [{"url": "#" + slugify(value["title"]), "text": value["title"]}] @@ -30,7 +30,7 @@ class ReleaseDateChangeBlock(blocks.StructBlock): previous_date = blocks.DateTimeBlock() reason_for_change = blocks.TextBlock() - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/release_date_change_block.html" @@ -39,7 +39,7 @@ class ReleaseCalendarStoryBlock(blocks.StreamBlock): release_content = ContentSectionBlock() - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/stream_block.html" @@ -48,7 +48,7 @@ class ReleaseCalendarChangesStoryBlock(blocks.StreamBlock): date_change_log = ReleaseDateChangeBlock() - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/stream_block.html" @@ -58,7 +58,7 @@ class ReleaseCalendarPreReleaseAccessStoryBlock(blocks.StreamBlock): description = blocks.RichTextBlock(features=settings.RICH_TEXT_BASIC) table = BasicTableBlock() - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/stream_block.html" block_counts: ClassVar[dict[str, dict[str, int]]] = {"description": {"max_num": 1}, "table": {"max_num": 1}} @@ -68,5 +68,5 @@ class ReleaseCalendarRelatedLinksStoryBlock(blocks.StreamBlock): link = LinkBlock() - class Meta: # pylint: disable=missing-class-docstring,too-few-public-methods + class Meta: template = "templates/components/streamfield/stream_block--related-links.html" diff --git a/cms/release_calendar/tests/factories.py b/cms/release_calendar/tests/factories.py index 966df39..1981e32 100644 --- a/cms/release_calendar/tests/factories.py +++ b/cms/release_calendar/tests/factories.py @@ -12,5 +12,5 @@ class Meta: parent = factory.LazyFunction(lambda: ReleaseCalendarIndex.objects.first()) # pylint: disable=unnecessary-lambda - title = factory.Faker("text", max_nb_chars=25) + title = factory.Faker("sentence", nb_words=4) summary = factory.Faker("text", max_nb_chars=100) diff --git a/cms/release_calendar/tests/test_models.py b/cms/release_calendar/tests/test_models.py index c9214fa..3ceabed 100644 --- a/cms/release_calendar/tests/test_models.py +++ b/cms/release_calendar/tests/test_models.py @@ -35,7 +35,7 @@ def test_template(self): ) def test_table_of_contents_pre_published__content(self): - """Check TOC in a pre-published state.""" + """Check table of contents in a pre-published state.""" for status in [ReleaseStatus.PROVISIONAL, ReleaseStatus.CONFIRMED, ReleaseStatus.CANCELLED]: with self.subTest(status=status): self.page.status = status @@ -61,7 +61,7 @@ def test_table_of_contents_pre_published__content(self): self.assertListEqual(self.page.get_context(self.request)["table_of_contents"], expected) def test_table_of_contents_pre_published__census(self): - """Check TOC in a pre-published state shows about the data when is census.""" + """Check table of contents in a pre-published state shows about the data when is census.""" for status in [ReleaseStatus.PROVISIONAL, ReleaseStatus.CONFIRMED, ReleaseStatus.CANCELLED]: with self.subTest(status=status): self.page.status = status @@ -94,7 +94,7 @@ def test_table_of_contents_pre_published__census(self): self.assertListEqual(self.page.get_context(self.request)["table_of_contents"], expected) def test_table_of_contents_pre_published__accredited(self): - """Check TOC in a pre-published state shows about the data when accredited.""" + """Check table of contents in a pre-published state shows about the data when accredited.""" for status in [ReleaseStatus.PROVISIONAL, ReleaseStatus.CONFIRMED, ReleaseStatus.CANCELLED]: with self.subTest(status=status): self.page.status = status @@ -120,7 +120,7 @@ def test_table_of_contents_pre_published__accredited(self): self.assertListEqual(self.page.get_context(self.request)["table_of_contents"], expected) def test_table_of_contents__published(self): - """Check TOC in a published state.""" + """Check table of contents in a published state.""" self.page.status = ReleaseStatus.PUBLISHED self.page.content = [ { @@ -141,7 +141,7 @@ def test_table_of_contents__published(self): ) def test_table_of_contents__changes_to_release_date(self): - """Check TOC for the changes to release date if added. Should be there for non-provisional.""" + """Check table of contents for the changes to release date if added. Should be there for non-provisional.""" cases = [ (ReleaseStatus.PROVISIONAL, False), (ReleaseStatus.CONFIRMED, True), @@ -163,7 +163,7 @@ def test_table_of_contents__changes_to_release_date(self): del self.page.table_of_contents # clear the cached property def test_table_of_contents__contact_details(self): - """Check TOC in a published state contains contact details if added.""" + """Check table of contents in a published state contains contact details if added.""" cases = [ (ReleaseStatus.PROVISIONAL, False), (ReleaseStatus.CONFIRMED, False), @@ -182,7 +182,7 @@ def test_table_of_contents__contact_details(self): del self.page.table_of_contents # clear the cached property def test_table_of_contents__pre_release_access(self): - """Check TOC in a published state has the pre-release access section if added.""" + """Check table of contents in a published state has the pre-release access section if added.""" cases = [ (ReleaseStatus.PROVISIONAL, False), (ReleaseStatus.CONFIRMED, False), @@ -200,7 +200,7 @@ def test_table_of_contents__pre_release_access(self): del self.page.table_of_contents # clear the cached property def test_table_of_contents_published__related_links(self): - """Check TOC in a published state has the related links section if added.""" + """Check table of contents in a published state has the related links section if added.""" self.page.status = ReleaseStatus.PUBLISHED self.page.related_links = [{"type": "link", "value": {"url": "https://ons.gov.uk", "text": "The link"}}] @@ -239,7 +239,7 @@ def setUp(self): self.page = ReleaseCalendarPageFactory() def test_rendered__content(self): - """Check TOC in a published state.""" + """Check table of contents in a published state.""" cases = [ (ReleaseStatus.PROVISIONAL, False), (ReleaseStatus.CONFIRMED, False), @@ -316,7 +316,7 @@ def test_rendered__contact_details(self): self.assertEqual("PSF team" in str(response.content), is_shown) def test_rendered__pre_release_access(self): - """Check TOC in a published state.""" + """Check table of contents in a published state.""" cases = [ (ReleaseStatus.PROVISIONAL, False), (ReleaseStatus.CONFIRMED, False), diff --git a/cms/settings/base.py b/cms/settings/base.py index 5c7eaa8..b9619af 100644 --- a/cms/settings/base.py +++ b/cms/settings/base.py @@ -54,11 +54,14 @@ # Application definition INSTALLED_APPS = [ + "cms.analysis", "cms.core", "cms.documents", "cms.home", "cms.images", "cms.release_calendar", + "cms.themes", + "cms.topics", "cms.users", "cms.standard_pages", "wagtail.embeds", @@ -166,6 +169,9 @@ "OPTIONS": { "context_processors": context_processors, }, + "DIRS": [ + PROJECT_DIR / "jinja2" / "assets" / "icons", # for icons registered with register_icons + ], }, ] diff --git a/cms/themes/__init__.py b/cms/themes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/themes/migrations/0001_initial.py b/cms/themes/migrations/0001_initial.py new file mode 100644 index 0000000..fc58f57 --- /dev/null +++ b/cms/themes/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 5.1.2 on 2024-11-04 16:09 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("images", "0002_customimage_description"), + ("wagtailcore", "0094_alter_page_locale"), + ] + + operations = [ + migrations.CreateModel( + name="ThemePage", + 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", + ), + ), + ("listing_title", models.CharField(blank=True, max_length=255)), + ("listing_summary", models.CharField(blank=True, max_length=255)), + ("social_text", models.CharField(blank=True, max_length=255)), + ("summary", wagtail.fields.RichTextField()), + ( + "listing_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "social_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page", models.Model), + ), + ] diff --git a/cms/themes/migrations/__init__.py b/cms/themes/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/themes/models.py b/cms/themes/models.py new file mode 100644 index 0000000..a316cab --- /dev/null +++ b/cms/themes/models.py @@ -0,0 +1,24 @@ +from typing import TYPE_CHECKING, ClassVar + +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from wagtail.admin.panels import FieldPanel +from wagtail.fields import RichTextField + +from cms.core.models import BasePage + +if TYPE_CHECKING: + from wagtail.admin.panels import Panel + + +class ThemePage(BasePage): # type: ignore[django-manager-missing] + """The Theme page model.""" + + template = "templates/pages/theme_page.html" + parent_page_types: ClassVar[list[str]] = ["home.HomePage", "ThemePage"] + subpage_types: ClassVar[list[str]] = ["ThemePage", "topics.TopicPage"] + page_description = _("A theme page, such as 'Economy'.") + + summary = RichTextField(features=settings.RICH_TEXT_BASIC) + + content_panels: ClassVar[list["Panel"]] = [*BasePage.content_panels, FieldPanel("summary")] diff --git a/cms/themes/tests/factories.py b/cms/themes/tests/factories.py new file mode 100644 index 0000000..6140a60 --- /dev/null +++ b/cms/themes/tests/factories.py @@ -0,0 +1,16 @@ +import factory +import wagtail_factories + +from cms.home.models import HomePage +from cms.themes.models import ThemePage + + +class ThemePageFactory(wagtail_factories.PageFactory): + """Factory for ThemePage.""" + + class Meta: + model = ThemePage + + title = factory.Faker("sentence", nb_words=4) + summary = factory.Faker("text", max_nb_chars=100) + parent = factory.LazyFunction(lambda: HomePage.objects.first()) # pylint: disable=unnecessary-lambda diff --git a/cms/topics/__init__.py b/cms/topics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/topics/migrations/0001_initial.py b/cms/topics/migrations/0001_initial.py new file mode 100644 index 0000000..59fdf47 --- /dev/null +++ b/cms/topics/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 5.1.2 on 2024-11-04 16:09 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("images", "0002_customimage_description"), + ("wagtailcore", "0094_alter_page_locale"), + ] + + operations = [ + migrations.CreateModel( + name="TopicPage", + 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", + ), + ), + ("listing_title", models.CharField(blank=True, max_length=255)), + ("listing_summary", models.CharField(blank=True, max_length=255)), + ("social_text", models.CharField(blank=True, max_length=255)), + ("summary", wagtail.fields.RichTextField()), + ( + "listing_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "social_image", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page", models.Model), + ), + ] diff --git a/cms/topics/migrations/__init__.py b/cms/topics/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cms/topics/models.py b/cms/topics/models.py new file mode 100644 index 0000000..cbbc25d --- /dev/null +++ b/cms/topics/models.py @@ -0,0 +1,24 @@ +from typing import TYPE_CHECKING, ClassVar + +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from wagtail.admin.panels import FieldPanel +from wagtail.fields import RichTextField + +from cms.core.models import BasePage + +if TYPE_CHECKING: + from wagtail.admin.panels import Panel + + +class TopicPage(BasePage): # type: ignore[django-manager-missing] + """The Topic page model.""" + + template = "templates/pages/topic_page.html" + parent_page_types: ClassVar[list[str]] = ["themes.ThemePage"] + subpage_types: ClassVar[list[str]] = ["analysis.AnalysisSeries"] + page_description = _("A specific topic page. e.g. 'Public sector finance' or 'Inflation and price indices'.") + + summary = RichTextField(features=settings.RICH_TEXT_BASIC) + + content_panels: ClassVar[list["Panel"]] = [*BasePage.content_panels, FieldPanel("summary")] diff --git a/cms/topics/tests/factories.py b/cms/topics/tests/factories.py new file mode 100644 index 0000000..b03590e --- /dev/null +++ b/cms/topics/tests/factories.py @@ -0,0 +1,16 @@ +import factory +import wagtail_factories + +from cms.themes.tests.factories import ThemePageFactory +from cms.topics.models import TopicPage + + +class TopicPageFactory(wagtail_factories.PageFactory): + """Factory for TopicPage.""" + + class Meta: + model = TopicPage + + title = factory.Faker("sentence", nb_words=4) + summary = factory.Faker("text", max_nb_chars=100) + parent = factory.SubFactory(ThemePageFactory) From 5e5999f512668c30dfcc65fc3402aa0f280fba97 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Thu, 14 Nov 2024 16:12:09 +0000 Subject: [PATCH 37/41] Addition of equation partials --- .../templates/pages/information_page.html | 11 +++++++++-- media/documents/file.txt | 1 + media/documents/file_CNRLbwg.txt | 1 + media/documents/file_HNziTZL.txt | 1 + media/documents/file_dOFolq3.txt | 1 + media/images/flower2.max-165x165.jpg | Bin 0 -> 7146 bytes media/images/flower2.original.jpg | Bin 0 -> 31239 bytes media/original_images/flower2.jpg | Bin 0 -> 29214 bytes 8 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 media/documents/file.txt create mode 100644 media/documents/file_CNRLbwg.txt create mode 100644 media/documents/file_HNziTZL.txt create mode 100644 media/documents/file_dOFolq3.txt create mode 100644 media/images/flower2.max-165x165.jpg create mode 100644 media/images/flower2.original.jpg create mode 100644 media/original_images/flower2.jpg diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 038aadc..4be66a4 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -12,7 +12,7 @@
Last Updated: {{ page.last_updated }}
{% if related_pages %} {# fmt:off #} - {{- + {{- onsRelatedContent({ "ariaLabel": _('Related content'), "rows": [{ @@ -20,8 +20,15 @@
Last Updated: {{ page.last_updated }}
"title": _('Related content'), "itemsList": related_pages }] - }) + }) -}} {# fmt:on #} {% endif %} + {% endblock %} +{% if page.has_equations %} + +{% endif %} +{% if page.has_ons_embed %} + +{% endif %} diff --git a/media/documents/file.txt b/media/documents/file.txt new file mode 100644 index 0000000..d3bc57e --- /dev/null +++ b/media/documents/file.txt @@ -0,0 +1 @@ +A boring example document \ No newline at end of file diff --git a/media/documents/file_CNRLbwg.txt b/media/documents/file_CNRLbwg.txt new file mode 100644 index 0000000..d3bc57e --- /dev/null +++ b/media/documents/file_CNRLbwg.txt @@ -0,0 +1 @@ +A boring example document \ No newline at end of file diff --git a/media/documents/file_HNziTZL.txt b/media/documents/file_HNziTZL.txt new file mode 100644 index 0000000..d3bc57e --- /dev/null +++ b/media/documents/file_HNziTZL.txt @@ -0,0 +1 @@ +A boring example document \ No newline at end of file diff --git a/media/documents/file_dOFolq3.txt b/media/documents/file_dOFolq3.txt new file mode 100644 index 0000000..d3bc57e --- /dev/null +++ b/media/documents/file_dOFolq3.txt @@ -0,0 +1 @@ +A boring example document \ No newline at end of file diff --git a/media/images/flower2.max-165x165.jpg b/media/images/flower2.max-165x165.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95a0ff0a2c0773d738872c0f0ca7ed0e66df3f22 GIT binary patch literal 7146 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 U$D{JgKY+uEo{zW3@_!cp3(w!i=>Px# literal 0 HcmV?d00001 diff --git a/media/images/flower2.original.jpg b/media/images/flower2.original.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5242904069eff13de5e38555d3d6e55212dec217 GIT binary patch literal 31239 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

9RE!u<4`n*_)B@Zru@lOxU&bx!ZxiTW8rIeK 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 literal 0 HcmV?d00001 diff --git a/media/original_images/flower2.jpg b/media/original_images/flower2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39cdc95c7f4b9c2cc5e4561649257bc423d61a80 GIT binary patch literal 29214 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

9RE!u<4`n*_)B@Zru@lOxU&bx!ZxiTW8rIeK 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 diff --git a/media/original_images/flower2.jpg b/media/original_images/flower2.jpg deleted file mode 100644 index 39cdc95c7f4b9c2cc5e4561649257bc423d61a80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29214 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

C!SLZg@&DQY2mt{A0Y4C^61|lx=+~u6l`2=Z zy=rDY>Xj?eQoSl9sFI~bmF%ffy(&~sZG8&#sb1>#KS3^6qbulCsZzZw)%CAe+Mh9kD>}F9sZzaD=+~`E^nEMQ^l#GT`d_-Gde^*+saTciUYz-rf*)A{{X{ZMI}V_uSE4eg-Vs} z5tbpiUSNb#`7u+NY_&su%yGm}?H$d=fpZPQOaB1leJk0|t12g|N{R2Nd&$*^fW� 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 Date: Fri, 15 Nov 2024 09:58:45 +0000 Subject: [PATCH 39/41] Repeated code remove, /media added to .gitignore, media file removed --- .gitignore | 3 +++ cms/jinja2/templates/pages/information_page.html | 7 ------- media/documents/file.txt | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 media/documents/file.txt diff --git a/.gitignore b/.gitignore index 310dba0..e6e39b4 100644 --- a/.gitignore +++ b/.gitignore @@ -187,3 +187,6 @@ static/ # mkdocs /site + +# Media files +/media diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 4be66a4..d17991d 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -25,10 +25,3 @@

Last Updated: {{ page.last_updated }}
{# fmt:on #} {% endif %} -{% endblock %} -{% if page.has_equations %} - -{% endif %} -{% if page.has_ons_embed %} - -{% endif %} diff --git a/media/documents/file.txt b/media/documents/file.txt deleted file mode 100644 index d3bc57e..0000000 --- a/media/documents/file.txt +++ /dev/null @@ -1 +0,0 @@ -A boring example document \ No newline at end of file From 0003ec4e5bba87b2600f4b1fbc8316ce11b0616f Mon Sep 17 00:00:00 2001 From: nehakerung Date: Fri, 15 Nov 2024 10:36:05 +0000 Subject: [PATCH 40/41] Check tests pass --- cms/jinja2/templates/pages/information_page.html | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index d17991d..2f58c1b 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -2,15 +2,15 @@ {% from "components/related-content/_macro.njk" import onsRelatedContent %} {% block main %} -

{{ page.summary }}

+

{{ page.summary }}

- {% if page.last_updated %} -
Last Updated: {{ page.last_updated }}
- {% endif %} +{% if page.last_updated %} +
Last Updated: {{ page.last_updated }}
+{% endif %} - {% include_block page.content %} +{% include_block page.content %} - {% if related_pages %} +{% if related_pages %} {# fmt:off #} {{- onsRelatedContent({ @@ -23,5 +23,4 @@
Last Updated: {{ page.last_updated }}
}) -}} {# fmt:on #} - {% endif %} - +{% endif %} From 597bf738ebb40a19d8efd214ee1d70b7daeec792 Mon Sep 17 00:00:00 2001 From: nehakerung Date: Fri, 15 Nov 2024 12:06:01 +0000 Subject: [PATCH 41/41] Custom sign commit --- cms/jinja2/templates/pages/information_page.html | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/jinja2/templates/pages/information_page.html b/cms/jinja2/templates/pages/information_page.html index 2f58c1b..28608ad 100644 --- a/cms/jinja2/templates/pages/information_page.html +++ b/cms/jinja2/templates/pages/information_page.html @@ -24,3 +24,4 @@
Last Updated: {{ page.last_updated }}
-}} {# fmt:on #} {% endif %} +

C!SLZg@&DQY2mt{A0Y4C^61|lx=+~u6l`2=Z zy=rDY>Xj?eQoSl9sFI~bmF%ffy(&~sZG8&#sb1>#KS3^6qbulCsZzZw)%CAe+Mh9kD>}F9sZzaD=+~`E^nEMQ^l#GT`d_-Gde^*+saTciUYz-rf*)A{{X{ZMI}V_uSE4eg-Vs} z5tbpiUSNb#`7u+NY_&su%yGm}?H$d=fpZPQOaB1leJk0|t12g|N{R2Nd&$*^fW� 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 Date: Thu, 14 Nov 2024 16:22:53 +0000 Subject: [PATCH 38/41] Deleted media files and made lint, format test pass --- media/documents/file_CNRLbwg.txt | 1 - media/documents/file_HNziTZL.txt | 1 - media/documents/file_dOFolq3.txt | 1 - media/images/flower2.max-165x165.jpg | Bin 7146 -> 0 bytes media/images/flower2.original.jpg | Bin 31239 -> 0 bytes media/original_images/flower2.jpg | Bin 29214 -> 0 bytes 6 files changed, 3 deletions(-) delete mode 100644 media/documents/file_CNRLbwg.txt delete mode 100644 media/documents/file_HNziTZL.txt delete mode 100644 media/documents/file_dOFolq3.txt delete mode 100644 media/images/flower2.max-165x165.jpg delete mode 100644 media/images/flower2.original.jpg delete mode 100644 media/original_images/flower2.jpg diff --git a/media/documents/file_CNRLbwg.txt b/media/documents/file_CNRLbwg.txt deleted file mode 100644 index d3bc57e..0000000 --- a/media/documents/file_CNRLbwg.txt +++ /dev/null @@ -1 +0,0 @@ -A boring example document \ No newline at end of file diff --git a/media/documents/file_HNziTZL.txt b/media/documents/file_HNziTZL.txt deleted file mode 100644 index d3bc57e..0000000 --- a/media/documents/file_HNziTZL.txt +++ /dev/null @@ -1 +0,0 @@ -A boring example document \ No newline at end of file diff --git a/media/documents/file_dOFolq3.txt b/media/documents/file_dOFolq3.txt deleted file mode 100644 index d3bc57e..0000000 --- a/media/documents/file_dOFolq3.txt +++ /dev/null @@ -1 +0,0 @@ -A boring example document \ No newline at end of file diff --git a/media/images/flower2.max-165x165.jpg b/media/images/flower2.max-165x165.jpg deleted file mode 100644 index 95a0ff0a2c0773d738872c0f0ca7ed0e66df3f22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7146 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 U$D{JgKY+uEo{zW3@_!cp3(w!i=>Px# diff --git a/media/images/flower2.original.jpg b/media/images/flower2.original.jpg deleted file mode 100644 index 5242904069eff13de5e38555d3d6e55212dec217..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31239 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