diff --git a/.gitignore b/.gitignore index c89290c9..e04e06b4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ apps/guide/settings/local.py /static/ .coverage .ruff_cache +.idea/ diff --git a/apps/core/blocks.py b/apps/core/blocks.py index eae2f08e..7faa6d84 100644 --- a/apps/core/blocks.py +++ b/apps/core/blocks.py @@ -2,12 +2,52 @@ from wagtail import blocks from wagtail.blocks import RichTextBlock +WAGTAIL_VERSIONS = [ + "4.1", + "4.2", + "5.0", + "5.1", + "5.2", + "6.0", + "6.1", + "6.2", + "6.3", + "6.4", + "7.0", + "7.1", + "7.2", + "7.3", + "7.4", + "8.0", +] + class TextBlock(RichTextBlock): class Meta: template = "core/blocks/text.html" +class TextBlockVersioned(blocks.StructBlock): + content = RichTextBlock(features=["bold", "italic", "link"]) + version = blocks.ChoiceBlock(choices=[(v, v) for v in WAGTAIL_VERSIONS]) + change_type = blocks.ChoiceBlock( + choices=[ + ("added", _("Added")), + ("changed", _("Changed")), + ("removed", _("Removed")), + ] + ) + + class Meta: + template = "core/blocks/text_versioned.html" + icon = "pilcrow" + label = _("Text (versioned)") + form_layout = blocks.BlockGroup( + children=["content"], + settings=["version", "change_type"], + ) + + class SectionStructValue(blocks.StructValue): def icon(self): return f"core/svg/{self.get('section')}.svg" @@ -62,5 +102,40 @@ class Meta: value_class = AlertStructValue -CONTENT_BLOCKS = [("text", TextBlock()), ("alert", AlertBlock())] +class VersionNoteStructValue(blocks.StructValue): + def icon(self): + return f"core/svg/{self.get('change_type')}.svg" + + +class VersionNoteBlock(blocks.StructBlock): + version = blocks.ChoiceBlock( + choices=[(v, v) for v in WAGTAIL_VERSIONS], + label=_("Version"), + ) + change_type = blocks.ChoiceBlock( + choices=[ + ("added", _("Added")), + ("changed", _("Changed")), + ("removed", _("Removed")), + ], + label=_("Type of change"), + ) + content = RichTextBlock( + features=["bold", "italic", "link"], + label=_("Content"), + ) + + class Meta: + template = "core/blocks/version_note.html" + icon = "tag" + label = _("Version note") + value_class = VersionNoteStructValue + + +CONTENT_BLOCKS = [ + ("text", TextBlock()), + ("text_versioned", TextBlockVersioned()), + ("alert", AlertBlock()), + ("version_note", VersionNoteBlock()), +] HOME_BLOCKS = [("section_grid", SectionGridBlock())] diff --git a/apps/core/migrations/0012_alter_contentpage_body.py b/apps/core/migrations/0012_alter_contentpage_body.py new file mode 100644 index 00000000..d8eaaef4 --- /dev/null +++ b/apps/core/migrations/0012_alter_contentpage_body.py @@ -0,0 +1,19 @@ +# Generated by Django 6.0.6 on 2026-06-21 12:17 + +import wagtail.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_alter_footercontent_locale_alter_footeritem_locale'), + ] + + operations = [ + migrations.AlterField( + model_name='contentpage', + name='body', + field=wagtail.fields.StreamField([('text', 0), ('text_versioned', 4), ('alert', 6), ('version_note', 10)], block_lookup={0: ('apps.core.blocks.TextBlock', (), {}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('4.1', '4.1'), ('4.2', '4.2'), ('5.0', '5.0'), ('5.1', '5.1'), ('5.2', '5.2'), ('6.0', '6.0'), ('6.1', '6.1'), ('6.2', '6.2'), ('6.3', '6.3'), ('6.4', '6.4'), ('7.0', '7.0'), ('7.1', '7.1'), ('7.2', '7.2'), ('7.3', '7.3'), ('7.4', '7.4'), ('8.0', '8.0')]}), 3: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('added', 'Added'), ('changed', 'Changed'), ('removed', 'Removed')]}), 4: ('wagtail.blocks.StructBlock', [[('content', 1), ('version', 2), ('change_type', 3)]], {}), 5: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('Warning', 'Warning'), ('Note', 'Note')]}), 6: ('wagtail.blocks.StructBlock', [[('alert_type', 5), ('alert_body', 1)]], {}), 7: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('4.1', '4.1'), ('4.2', '4.2'), ('5.0', '5.0'), ('5.1', '5.1'), ('5.2', '5.2'), ('6.0', '6.0'), ('6.1', '6.1'), ('6.2', '6.2'), ('6.3', '6.3'), ('6.4', '6.4'), ('7.0', '7.0'), ('7.1', '7.1'), ('7.2', '7.2'), ('7.3', '7.3'), ('7.4', '7.4'), ('8.0', '8.0')], 'label': 'Version'}), 8: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('added', 'Added'), ('changed', 'Changed'), ('removed', 'Removed')], 'label': 'Type of change'}), 9: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link'], 'label': 'Content'}), 10: ('wagtail.blocks.StructBlock', [[('version', 7), ('change_type', 8), ('content', 9)]], {})}), + ), + ] diff --git a/apps/core/templates/core/blocks/text_versioned.html b/apps/core/templates/core/blocks/text_versioned.html new file mode 100644 index 00000000..cf35dbbb --- /dev/null +++ b/apps/core/templates/core/blocks/text_versioned.html @@ -0,0 +1,8 @@ +{% load wagtailcore_tags %} + +{% if value.version %} +
+ {{ value.change_type|title }} in Wagtail {{ value.version }} +
+{% endif %} +{{ value.content|richtext }} diff --git a/apps/core/templates/core/blocks/version_note.html b/apps/core/templates/core/blocks/version_note.html new file mode 100644 index 00000000..b65d5c3a --- /dev/null +++ b/apps/core/templates/core/blocks/version_note.html @@ -0,0 +1,17 @@ +{% load wagtailcore_tags i18n %} + +
+
+ {% if value.change_type %} +
+ {% include value.icon %} +
+ {% endif %} + + {% blocktrans with change_type=value.change_type|title version=value.version %}{{ change_type }} in Wagtail {{ version }}{% endblocktrans %} + +
+
+ {{ value.content|richtext }} +
+
diff --git a/apps/core/templates/core/svg/added.svg b/apps/core/templates/core/svg/added.svg new file mode 100644 index 00000000..66bf4dff --- /dev/null +++ b/apps/core/templates/core/svg/added.svg @@ -0,0 +1,3 @@ + diff --git a/apps/core/templates/core/svg/changed.svg b/apps/core/templates/core/svg/changed.svg new file mode 100644 index 00000000..e6ede22a --- /dev/null +++ b/apps/core/templates/core/svg/changed.svg @@ -0,0 +1,4 @@ + diff --git a/apps/core/templates/core/svg/removed.svg b/apps/core/templates/core/svg/removed.svg new file mode 100644 index 00000000..637fe162 --- /dev/null +++ b/apps/core/templates/core/svg/removed.svg @@ -0,0 +1,3 @@ + diff --git a/apps/frontend/static_src/scss/components/version-badge.scss b/apps/frontend/static_src/scss/components/version-badge.scss new file mode 100644 index 00000000..1f4e168f --- /dev/null +++ b/apps/frontend/static_src/scss/components/version-badge.scss @@ -0,0 +1,34 @@ +.version-badge { + display: inline-flex; + align-items: center; + margin-bottom: ($gutter * 0.5); + padding: 2px 10px; + border-radius: $border-radius--lg; + font-size: 14px; + font-weight: 600; + line-height: 1.5; + + &--added { + background-color: light-dark( + $color--extra-light-teal, + rgba($color--light-teal, 0.15) + ); + color: light-dark($color--teal, $color--light-teal); + } + + &--changed { + background-color: light-dark( + $color--extra-light-blue, + rgba($color--light-blue, 0.15) + ); + color: light-dark($color--blue, $color--light-blue); + } + + &--removed { + background-color: light-dark( + rgba($color--grey, 0.15), + rgba($color--light-grey, 0.1) + ); + color: light-dark($color--grey, $color--light-grey); + } +} diff --git a/apps/frontend/static_src/scss/components/version-note.scss b/apps/frontend/static_src/scss/components/version-note.scss new file mode 100644 index 00000000..52f5511d --- /dev/null +++ b/apps/frontend/static_src/scss/components/version-note.scss @@ -0,0 +1,82 @@ +.version-note { + $root: &; + box-shadow: 2px 2px 8px 0 rgba($color--black, 0.3); + border-radius: $border-radius--m; + margin-bottom: ($gutter * 3); + overflow: hidden; + + &__header { + display: flex; + align-items: center; + padding: ($gutter * 0.75) $gutter; + } + + &__label { + color: $color--white; + font-weight: 700; + } + + &__body { + padding: $gutter; + } + + &__icon { + display: flex; + align-items: center; + margin-inline-end: ($gutter * 0.5); + height: 20px; + + svg { + height: 100%; + } + } + + &--added { + #{$root}__header { + background-color: $color--primary; + } + + #{$root}__body { + background-color: light-dark( + $color--extra-light-teal, + $color--black + ); + } + } + + &--changed { + #{$root}__header { + background-color: $color--blue; + } + + #{$root}__body { + background-color: light-dark( + $color--extra-light-blue, + $color--black + ); + } + } + + &--removed { + #{$root}__header { + background-color: $color--grey; + } + + #{$root}__body { + background-color: light-dark( + rgba($color--grey, 0.15), + $color--black + ); + } + } + + // Extra specificity to override default StreamField values + .streamfield & { + #{$root}__label { + @include rem-font-size($base-font-size); + /* stylelint-disable-next-line declaration-no-important */ + margin: 0 !important; + line-height: 1; + } + } +} diff --git a/apps/frontend/static_src/scss/main.scss b/apps/frontend/static_src/scss/main.scss index 14b21fe5..7a6443a6 100644 --- a/apps/frontend/static_src/scss/main.scss +++ b/apps/frontend/static_src/scss/main.scss @@ -15,6 +15,8 @@ // Custom component styles @import './components/alert'; +@import './components/version-note'; +@import './components/version-badge'; @import './components/app'; @import './components/copy-button'; @import './components/autocomplete'; diff --git a/apps/llms_txt/jinja2/llms_txt/page.md.jinja b/apps/llms_txt/jinja2/llms_txt/page.md.jinja index 066ad3c0..7a4a7bb0 100644 --- a/apps/llms_txt/jinja2/llms_txt/page.md.jinja +++ b/apps/llms_txt/jinja2/llms_txt/page.md.jinja @@ -10,8 +10,12 @@ Page URL: {{ page.full_url }} {%- for block in page.body %} {%- if block.block_type == 'text' %} {{ block.value|richtext_markdown }} +{%- elif block.block_type == 'text_versioned' %} +{{ block.value.content|richtext_markdown }} {%- elif block.block_type == 'alert' %} {{block.value.alert_type}}: {{ block.value.alert_body|richtext_markdown }} +{%- elif block.block_type == 'version_note' %} +{{ block.value.change_type|capitalize }} in Wagtail {{ block.value.version }}: {{ block.value.content|richtext_markdown }} {%- endif %} {%- endfor %}