From 97b96dc333082cbfbc376013f72b8449c6b89c4a Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Wed, 6 Mar 2024 17:37:38 +0100 Subject: [PATCH 1/7] Update the ContentPage app to use the new base page class --- blog/managers.py | 7 - ...ogentrypage_header_color_class_and_more.py | 1081 +++++++++++++++++ ...entrypage_body_alter_blogindexpage_body.py | 957 +++++++++++++++ ..._blogentrypage_header_cta_text_and_more.py | 63 + blog/models.py | 14 +- blog/templates/blog/blog_entry_page.html | 2 +- blog/templates/blog/blog_index_page.html | 2 +- content_manager/abstract.py | 57 +- content_manager/blocks.py | 60 +- .../commands/create_sample_pages.py | 12 +- content_manager/managers.py | 8 + ...0015_alter_contentpage_options_and_more.py | 646 ++++++++++ .../migrations/0016_auto_20240305_1611.py | 59 + ...ag_contentpage_header_cta_text_and_more.py | 557 +++++++++ content_manager/models.py | 42 +- .../content_manager/blocks/blocks_stream.html | 20 +- .../content_manager/blocks/heading.html | 24 + .../content_manager/blocks/hero.html | 12 - .../content_manager/blocks/hero_image.html | 5 - .../content_manager/content_page.html | 47 + dashboard/templates/wagtailadmin/login.html | 5 +- poetry.lock | 6 +- pyproject.toml | 2 +- 23 files changed, 3561 insertions(+), 127 deletions(-) create mode 100644 blog/migrations/0003_blogentrypage_header_color_class_and_more.py create mode 100644 blog/migrations/0004_alter_blogentrypage_body_alter_blogindexpage_body.py create mode 100644 blog/migrations/0005_delete_tag_blogentrypage_header_cta_text_and_more.py create mode 100644 content_manager/managers.py create mode 100644 content_manager/migrations/0015_alter_contentpage_options_and_more.py create mode 100644 content_manager/migrations/0016_auto_20240305_1611.py create mode 100644 content_manager/migrations/0017_tag_contentpage_header_cta_text_and_more.py create mode 100644 content_manager/templates/content_manager/blocks/heading.html delete mode 100644 content_manager/templates/content_manager/blocks/hero_image.html diff --git a/blog/managers.py b/blog/managers.py index 50a93ac1..4c9a1b4d 100644 --- a/blog/managers.py +++ b/blog/managers.py @@ -1,11 +1,4 @@ from django.db import models -from django.db.models import Count - - -class TagManager(models.Manager): - def most_common(self, blog_page): - entries = blog_page.get_entries() - return self.filter(entrypage__in=entries).annotate(num_times=Count("entrypage")).order_by("-num_times") class CategoryManager(models.Manager): diff --git a/blog/migrations/0003_blogentrypage_header_color_class_and_more.py b/blog/migrations/0003_blogentrypage_header_color_class_and_more.py new file mode 100644 index 00000000..bc0b717d --- /dev/null +++ b/blog/migrations/0003_blogentrypage_header_color_class_and_more.py @@ -0,0 +1,1081 @@ +# Generated by Django 5.0.2 on 2024-03-05 15:11 + +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks +import wagtailmarkdown.blocks +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0002_alter_blogentrypage_body_alter_blogindexpage_body"), + ] + + operations = [ + migrations.AddField( + model_name="blogentrypage", + name="header_color_class", + field=models.CharField( + blank=True, + choices=[ + ("Couleurs primaires", [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")]), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Uses the French Design System colors", + null=True, + verbose_name="Background color", + ), + ), + migrations.AddField( + model_name="blogentrypage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="CTA Button label"), + ), + migrations.AddField( + model_name="blogentrypage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="CTA link"), + ), + migrations.AddField( + model_name="blogentrypage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background"), + ), + migrations.AddField( + model_name="blogentrypage", + name="header_large", + field=models.BooleanField(default=False, verbose_name="Full width"), + ), + migrations.AddField( + model_name="blogentrypage", + name="header_with_title", + field=models.BooleanField(default=False, verbose_name="Show title in header image?"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_color_class", + field=models.CharField( + blank=True, + choices=[ + ("Couleurs primaires", [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")]), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Uses the French Design System colors", + null=True, + verbose_name="Background color", + ), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="CTA Button label"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="CTA link"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_large", + field=models.BooleanField(default=False, verbose_name="Full width"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_with_title", + field=models.BooleanField(default=False, verbose_name="Show title in header image?"), + ), + migrations.AlterField( + model_name="blogentrypage", + name="body", + field=wagtail.fields.StreamField( + [ + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ("paragraphlarge", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme (large)")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + migrations.AlterField( + model_name="blogindexpage", + name="body", + field=wagtail.fields.StreamField( + [ + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ("paragraphlarge", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme (large)")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + ] diff --git a/blog/migrations/0004_alter_blogentrypage_body_alter_blogindexpage_body.py b/blog/migrations/0004_alter_blogentrypage_body_alter_blogindexpage_body.py new file mode 100644 index 00000000..1e1bfdb3 --- /dev/null +++ b/blog/migrations/0004_alter_blogentrypage_body_alter_blogindexpage_body.py @@ -0,0 +1,957 @@ +# Generated by Django 5.0.2 on 2024-03-05 15:56 + +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks +import wagtailmarkdown.blocks +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0003_blogentrypage_header_color_class_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="blogentrypage", + name="body", + field=wagtail.fields.StreamField( + [ + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + migrations.AlterField( + model_name="blogindexpage", + name="body", + field=wagtail.fields.StreamField( + [ + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + ] diff --git a/blog/migrations/0005_delete_tag_blogentrypage_header_cta_text_and_more.py b/blog/migrations/0005_delete_tag_blogentrypage_header_cta_text_and_more.py new file mode 100644 index 00000000..d8209645 --- /dev/null +++ b/blog/migrations/0005_delete_tag_blogentrypage_header_cta_text_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:04 + +import modelcluster.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0004_alter_blogentrypage_body_alter_blogindexpage_body"), + ] + + operations = [ + migrations.DeleteModel( + name="Tag", + ), + migrations.AddField( + model_name="blogentrypage", + name="header_cta_text", + field=models.CharField(blank=True, null=True, verbose_name="Call to action text"), + ), + migrations.AddField( + model_name="blogindexpage", + name="header_cta_text", + field=models.CharField(blank=True, null=True, verbose_name="Call to action text"), + ), + migrations.AlterField( + model_name="blogentrypage", + name="blog_categories", + field=modelcluster.fields.ParentalManyToManyField( + blank=True, through="blog.CategoryEntryPage", to="blog.category", verbose_name="Categories" + ), + ), + migrations.AlterField( + model_name="blogentrypage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="Call to action label"), + ), + migrations.AlterField( + model_name="blogentrypage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="Call to action link"), + ), + migrations.AlterField( + model_name="blogentrypage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background image"), + ), + migrations.AlterField( + model_name="blogindexpage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="Call to action label"), + ), + migrations.AlterField( + model_name="blogindexpage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="Call to action link"), + ), + migrations.AlterField( + model_name="blogindexpage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background image"), + ), + ] diff --git a/blog/models.py b/blog/models.py index b5539107..17c93fb7 100644 --- a/blog/models.py +++ b/blog/models.py @@ -12,15 +12,16 @@ from django.utils.translation import get_language, gettext_lazy as _ from modelcluster.fields import ParentalKey, ParentalManyToManyField from modelcluster.tags import ClusterTaggableManager -from taggit.models import Tag as TaggitTag, TaggedItemBase +from taggit.models import TaggedItemBase from wagtail.admin.panels import FieldPanel, FieldRowPanel, MultiFieldPanel, TitleFieldPanel from wagtail.admin.widgets.slug import SlugInput from wagtail.models.i18n import Locale, TranslatableMixin from wagtail.search import index from wagtail.snippets.models import register_snippet -from blog.managers import CategoryManager, TagManager +from blog.managers import CategoryManager from content_manager.abstract import SitesFacilesBasePage +from content_manager.models import Tag User = get_user_model() @@ -163,7 +164,6 @@ class BlogEntryPage(SitesFacilesBasePage): "Category", through="CategoryEntryPage", blank=True, - null=True, verbose_name=_("Categories"), ) date = models.DateTimeField(verbose_name=_("Post date"), default=timezone.now) @@ -266,11 +266,3 @@ def __str__(self): class TagEntryPage(TaggedItemBase): content_object = ParentalKey("BlogEntryPage", related_name="entry_tags") - - -@register_snippet -class Tag(TaggitTag): - objects = TagManager() - - class Meta: - proxy = True diff --git a/blog/templates/blog/blog_entry_page.html b/blog/templates/blog/blog_entry_page.html index a0a102e6..00db2de1 100644 --- a/blog/templates/blog/blog_entry_page.html +++ b/blog/templates/blog/blog_entry_page.html @@ -43,7 +43,7 @@ {% endblock social_media %} {% block content %} - {% include "content_manager/blocks/hero_image.html" %} + {% include "content_manager/blocks/heading.html" %} {% include "content_manager/blocks/messages.html" %}
diff --git a/blog/templates/blog/blog_index_page.html b/blog/templates/blog/blog_index_page.html index 7af6f26e..0be1f18e 100644 --- a/blog/templates/blog/blog_index_page.html +++ b/blog/templates/blog/blog_index_page.html @@ -43,7 +43,7 @@ {% endblock social_media %} {% block content %} - {% include "content_manager/blocks/hero_image.html" %} + {% include "content_manager/blocks/heading.html" %} {% include "content_manager/blocks/messages.html" %}
diff --git a/content_manager/abstract.py b/content_manager/abstract.py index 25982c1c..b71b843e 100644 --- a/content_manager/abstract.py +++ b/content_manager/abstract.py @@ -1,9 +1,11 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from wagtail.admin.panels import FieldPanel +from dsfr.constants import COLOR_CHOICES +from wagtail.admin.panels import FieldPanel, MultiFieldPanel from wagtail.fields import StreamField from wagtail.images import get_image_model_string from wagtail.models import Page +from wagtail.search import index from content_manager.blocks import STREAMFIELD_COMMON_BLOCKS @@ -19,6 +21,8 @@ class SitesFacilesBasePage(Page): blank=True, use_json_field=True, ) + header_with_title = models.BooleanField(_("Show title in header image?"), default=False) + header_image = models.ForeignKey( get_image_model_string(), null=True, @@ -28,11 +32,60 @@ class SitesFacilesBasePage(Page): verbose_name=_("Header image"), ) + header_color_class = models.CharField( + _("Background color"), + choices=COLOR_CHOICES, + null=True, + blank=True, + help_text=_("Uses the French Design System colors"), + ) + + header_large = models.BooleanField(_("Full width"), default=False) + header_darken = models.BooleanField(_("Darken background image"), default=False) + + header_cta_text = models.CharField( + _("Call to action text"), + null=True, + blank=True, + ) + + header_cta_label = models.CharField( + _("Call to action label"), + null=True, + blank=True, + ) + + header_cta_link = models.URLField( + _("Call to action link"), + null=True, + blank=True, + ) + content_panels = Page.content_panels + [ - FieldPanel("header_image"), FieldPanel("body", heading=_("Body")), ] + promote_panels = [ + MultiFieldPanel(Page.promote_panels, _("Common page configuration")), + MultiFieldPanel( + [ + FieldPanel("header_with_title"), + FieldPanel("header_image"), + FieldPanel("header_color_class"), + FieldPanel("header_large"), + FieldPanel("header_darken"), + FieldPanel("header_cta_text"), + FieldPanel("header_cta_label"), + FieldPanel("header_cta_link"), + ], + heading=_("Header options"), + ), + ] + + search_fields = Page.search_fields + [ + index.SearchField("body"), + ] + def get_absolute_url(self): return self.url diff --git a/content_manager/blocks.py b/content_manager/blocks.py index 33e0f660..39f18fb3 100644 --- a/content_manager/blocks.py +++ b/content_manager/blocks.py @@ -1,4 +1,6 @@ from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from dsfr.constants import COLOR_CHOICES from wagtail import blocks from wagtail.documents.blocks import DocumentChooserBlock from wagtail.images.blocks import ImageChooserBlock @@ -26,43 +28,28 @@ ## Meta blocks class BackgroundColorChoiceBlock(blocks.ChoiceBlock): - choices = [ - ( - "Couleurs primaires", - ( - ("blue-france", "Bleu France"), - ("red-marianne", "Rouge Marianne"), - ), - ), - ("Couleurs neutres", (("grey", "Gris"),)), - ( - "Couleurs système", - ( - ("green-tilleul-verveine", "Thilleul verveine"), - ("green-bourgeon", "Bourgeon"), - ("green-emeraude", "Émeraude"), - ("green-menthe", "Menthe"), - ("green-archipel", "Archipel"), - ("blue-ecume", "Écume"), - ("blue-cumulus", "Cumulus"), - ("purple-glycine", "Glycine"), - ("pink-macaron", "Macaron"), - ("pink-tuile", "Tuile"), - ("yellow-tournesol", "Tournesol"), - ("yellow-moutarde", "Moutarde"), - ("orange-terre-battue", "Terre battue"), - ("brown-cafe-creme", "Café crème"), - ("brown-caramel", "Caramel"), - ("brown-opera", "Opéra"), - ("beige-gris-galet", "Gris galet"), - ), - ), - ] + choices = COLOR_CHOICES class Meta: icon = "view" +class LinkStructValue(blocks.StructValue): + def url(self): + external_url = self.get("external_url") + page = self.get("page") + return external_url or page.url + + +class LinkBlock(blocks.StructBlock): + text = blocks.CharBlock(label=_("Link text"), required=False) + page = blocks.PageChooserBlock(label=_("Page"), required=False) + external_url = blocks.URLBlock(label=_("External URL"), required=False) + + class Meta: + value_class = LinkStructValue + + ## Basic blocks class AccordionBlock(blocks.StructBlock): title = blocks.CharBlock(label="Titre") @@ -139,7 +126,7 @@ class HeroBlock(blocks.StructBlock): bg_color_class = BackgroundColorChoiceBlock( label="Couleur d’arrière-plan", required=False, - help_text="Utilise les couleurs du système de design de l'État", + help_text="Utilise les couleurs du système de design de l’État", ) bg_color = blocks.RegexBlock( label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", @@ -188,6 +175,7 @@ class ImageAndTextBlock(blocks.StructBlock): default="3", ) text = blocks.RichTextBlock(label="Texte avec mise en forme") + link = LinkBlock(required=False) link_label = blocks.CharBlock( label="Titre du lien", help_text="Le lien apparait en bas du bloc de droite, avec une flèche", @@ -273,7 +261,7 @@ class MultiColumnsWithTitleBlock(blocks.StructBlock): bg_color_class = BackgroundColorChoiceBlock( label="Couleur d’arrière-plan", required=False, - help_text="Utilise les couleurs du système de design de l'État", + help_text="Utilise les couleurs du système de design de l’État", ) bg_color = blocks.RegexBlock( label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", @@ -293,10 +281,6 @@ class MultiColumnsWithTitleBlock(blocks.StructBlock): STREAMFIELD_COMMON_BLOCKS = [ ("paragraph", blocks.RichTextBlock(label="Texte avec mise en forme")), - ( - "paragraphlarge", - blocks.RichTextBlock(label="Texte avec mise en forme (large)"), - ), ("image", ImageBlock()), ( "imageandtext", diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py index 5d6883a1..b2bf9d71 100644 --- a/content_manager/management/commands/create_sample_pages.py +++ b/content_manager/management/commands/create_sample_pages.py @@ -29,7 +29,6 @@ def handle(self, *args, **kwargs): elif slug == "mentions-legales": title = "Mentions légales" body = [] - body.append(("title", {"title": title, "large": True})) alert_block = { "title": title, @@ -37,6 +36,7 @@ def handle(self, *args, **kwargs): Que doivent-elles obligatoirement contenir ?""", # noqa "level": "info", + "heading_tag": "h2", } body.append(("alert", alert_block)) @@ -51,13 +51,12 @@ def handle(self, *args, **kwargs):

Ces deux derniers peuvent pointer vers des pages à part entière ou des sections de cette page.

""" # noqa - body.append(("paragraphlarge", RichText(text_raw))) + body.append(("paragraph", RichText(text_raw))) self.create_page(slug=slug, title=title, body=body) elif slug == "accessibilite": title = "Déclaration d’accessibilité" body = [] - body.append(("title", {"title": title, "large": True})) alert_block = { "title": title, @@ -65,6 +64,7 @@ def handle(self, *args, **kwargs): Générateur de déclaration d’accessibilité""", "level": "info", + "heading_tag": "h2", } body.append(("alert", alert_block)) @@ -83,7 +83,7 @@ def create_homepage(self) -> None: # Create the page body = [] - body.append(("title", {"title": "Votre nouveau site avec le CMS Beta", "large": True})) + title = "Votre nouveau site avec le CMS Beta" image = import_image( full_path="staticfiles/dsfr/dist/artwork/pictograms/digital/coding.svg", @@ -115,10 +115,10 @@ def create_homepage(self) -> None:
  • Remplacer le contenu de cette page d’accueil.
  • """ - body.append(("paragraphlarge", RichText(text_2_raw))) + body.append(("paragraph", RichText(text_2_raw))) root = Page.objects.get(slug="root") - home_page = root.add_child(instance=ContentPage(title="Accueil", body=body, show_in_menus=True)) + home_page = root.add_child(instance=ContentPage(title=title, body=body, show_in_menus=True)) # Define it as default for the default site site = Site.objects.filter(is_default_site=True).first() diff --git a/content_manager/managers.py b/content_manager/managers.py new file mode 100644 index 00000000..54736295 --- /dev/null +++ b/content_manager/managers.py @@ -0,0 +1,8 @@ +from django.db import models +from django.db.models import Count + + +class TagManager(models.Manager): + def most_common(self, blog_page): + entries = blog_page.get_entries() + return self.filter(entrypage__in=entries).annotate(num_times=Count("entrypage")).order_by("-num_times") diff --git a/content_manager/migrations/0015_alter_contentpage_options_and_more.py b/content_manager/migrations/0015_alter_contentpage_options_and_more.py new file mode 100644 index 00000000..f0b6ec08 --- /dev/null +++ b/content_manager/migrations/0015_alter_contentpage_options_and_more.py @@ -0,0 +1,646 @@ +# Generated by Django 5.0.2 on 2024-03-05 15:11 + +import django.db.models.deletion +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks +import wagtailmarkdown.blocks +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("content_manager", "0014_alter_contentpage_body"), + ("wagtailimages", "0025_alter_image_file_alter_rendition_file"), + ] + + operations = [ + migrations.AlterModelOptions( + name="contentpage", + options={"verbose_name": "Content page"}, + ), + migrations.AddField( + model_name="contentpage", + name="header_color_class", + field=models.CharField( + blank=True, + choices=[ + ("Couleurs primaires", [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")]), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Uses the French Design System colors", + null=True, + verbose_name="Background color", + ), + ), + migrations.AddField( + model_name="contentpage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="CTA Button label"), + ), + migrations.AddField( + model_name="contentpage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="CTA link"), + ), + migrations.AddField( + model_name="contentpage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background"), + ), + migrations.AddField( + model_name="contentpage", + name="header_image", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="wagtailimages.image", + verbose_name="Header image", + ), + ), + migrations.AddField( + model_name="contentpage", + name="header_large", + field=models.BooleanField(default=False, verbose_name="Full width"), + ), + migrations.AddField( + model_name="contentpage", + name="header_with_title", + field=models.BooleanField(default=False, verbose_name="Show title in header image?"), + ), + migrations.AlterField( + model_name="contentpage", + name="body", + field=wagtail.fields.StreamField( + [ + ( + "hero", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("text", wagtail.blocks.CharBlock(label="Texte", required=False)), + ("cta_label", wagtail.blocks.CharBlock(label="Texte du bouton", required=False)), + ("cta_link", wagtail.blocks.URLBlock(label="Lien du bouton", required=False)), + ("large", wagtail.blocks.BooleanBlock(label="Large", required=False)), + ("darken", wagtail.blocks.BooleanBlock(label="Assombrir", required=False)), + ], + label="Section promotionnelle", + ), + ), + ( + "title", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("large", wagtail.blocks.BooleanBlock(label="Large", required=False)), + ], + label="Titre de page", + ), + ), + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ("paragraphlarge", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme (large)")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + ] diff --git a/content_manager/migrations/0016_auto_20240305_1611.py b/content_manager/migrations/0016_auto_20240305_1611.py new file mode 100644 index 00000000..a57c8de0 --- /dev/null +++ b/content_manager/migrations/0016_auto_20240305_1611.py @@ -0,0 +1,59 @@ +# Generated by Django 5.0.2 on 2024-03-04 14:54 + +from django.db import migrations +from wagtail.blocks.migrations.migrate_operation import MigrateStreamData +from wagtail.blocks.migrations.operations import RemoveStreamChildrenOperation, RenameStreamChildrenOperation + + +def update_header_fields(apps, schema_editor): + """ + Update the header settings for pages that had a hero block + in the stream + """ + + ContentPage = apps.get_model("content_manager", "ContentPage") + for page in ContentPage.objects.all(): + for block in page.body.raw_data: + if block["type"] == "hero": + hero_settings = block["value"] + page.header_with_title = True + + if "bg_image" in hero_settings: + page.header_image__id = hero_settings["bg_image"] + + if "bg_color_class" in hero_settings: + page.header_color_class = hero_settings["bg_color_class"] + + if "large" in hero_settings: + page.header_large = hero_settings["large"] + + if "darken" in hero_settings: + page.header_darken = hero_settings["darken"] + + if "cta_link" in hero_settings: + page.header_cta_link = hero_settings["cta_link"] + + if "cta_label" in hero_settings: + page.header_cta_label = hero_settings["cta_label"] + + page.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("content_manager", "0015_alter_contentpage_options_and_more"), + ] + + operations = [ + migrations.RunPython(update_header_fields), + MigrateStreamData( + app_name="content_manager", + model_name="ContentPage", + field_name="body", + operations_and_block_paths=[ + (RemoveStreamChildrenOperation(name="hero"), "body"), + (RemoveStreamChildrenOperation(name="title"), "body"), + (RenameStreamChildrenOperation(old_name="paragraphlarge", new_name="paragraph"), ""), + ], + ), + ] diff --git a/content_manager/migrations/0017_tag_contentpage_header_cta_text_and_more.py b/content_manager/migrations/0017_tag_contentpage_header_cta_text_and_more.py new file mode 100644 index 00000000..9e693b08 --- /dev/null +++ b/content_manager/migrations/0017_tag_contentpage_header_cta_text_and_more.py @@ -0,0 +1,557 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:04 + +import django.db.models.deletion +import modelcluster.contrib.taggit +import modelcluster.fields +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks +import wagtailmarkdown.blocks +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("content_manager", "0016_auto_20240305_1611"), + ("taggit", "0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx"), + ] + + operations = [ + migrations.CreateModel( + name="Tag", + fields=[], + options={ + "proxy": True, + "indexes": [], + "constraints": [], + }, + bases=("taggit.tag",), + ), + migrations.AddField( + model_name="contentpage", + name="header_cta_text", + field=models.CharField(blank=True, null=True, verbose_name="Call to action text"), + ), + migrations.AlterField( + model_name="contentpage", + name="body", + field=wagtail.fields.StreamField( + [ + ("paragraph", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", required=False + ), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende", required=False)), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ] + ), + ), + ( + "imageandtext", + wagtail.blocks.StructBlock( + [ + ("image", wagtail.images.blocks.ImageChooserBlock(label="Illustration")), + ( + "image_side", + wagtail.blocks.ChoiceBlock( + choices=[("left", "Gauche"), ("right", "Droite")], + label="Côté où afficher l’image", + ), + ), + ( + "image_ratio", + wagtail.blocks.ChoiceBlock( + choices=[("3", "3/12"), ("5", "5/12"), ("6", "6/12")], + label="Largeur de l’image", + ), + ), + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "link", + wagtail.blocks.StructBlock( + [ + ("text", wagtail.blocks.CharBlock(label="Link text", required=False)), + ("page", wagtail.blocks.PageChooserBlock(label="Page", required=False)), + ( + "external_url", + wagtail.blocks.URLBlock(label="External URL", required=False), + ), + ], + required=False, + ), + ), + ( + "link_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait en bas du bloc de droite, avec une flèche", + label="Titre du lien", + required=False, + ), + ), + ("page", wagtail.blocks.PageChooserBlock(label="Lien interne", required=False)), + ("link_url", wagtail.blocks.URLBlock(label="Lien externe", required=False)), + ], + label="Bloc image et texte", + ), + ), + ( + "alert", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre du message", required=False)), + ("description", wagtail.blocks.TextBlock(label="Texte du message", required=False)), + ( + "level", + wagtail.blocks.ChoiceBlock( + choices=[ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), + ], + label="Type de message", + ), + ), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Message d’alerte", + ), + ), + ( + "callout", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de la mise en vant", required=False)), + ("text", wagtail.blocks.TextBlock(label="Texte mis en avant", required=False)), + ( + "heading_tag", + wagtail.blocks.ChoiceBlock( + choices=[ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), + ], + help_text="À adapter à la structure de la page. Par défaut en-tête 3.", + label="Niveau de titre", + ), + ), + ], + label="Texte mise en avant", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ("author_name", wagtail.blocks.CharBlock(label="Nom de l’auteur")), + ("author_title", wagtail.blocks.CharBlock(label="Titre de l’auteur")), + ], + label="Citation", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "multicolumns", + wagtail.blocks.StructBlock( + [ + ( + "bg_image", + wagtail.images.blocks.ImageChooserBlock( + label="Image d’arrière plan", required=False + ), + ), + ( + "bg_color_class", + wagtail.blocks.ChoiceBlock( + choices=[ + ( + "Couleurs primaires", + [("blue-france", "Bleu France"), ("red-marianne", "Rouge Marianne")], + ), + ("Couleurs neutres", [("grey", "Gris")]), + ( + "Couleurs illustratives", + [ + ("green-tilleul-verveine", "Thilleul verveine"), + ("green-bourgeon", "Bourgeon"), + ("green-emeraude", "Émeraude"), + ("green-menthe", "Menthe"), + ("green-archipel", "Archipel"), + ("blue-ecume", "Écume"), + ("blue-cumulus", "Cumulus"), + ("purple-glycine", "Glycine"), + ("pink-macaron", "Macaron"), + ("pink-tuile", "Tuile"), + ("yellow-tournesol", "Tournesol"), + ("yellow-moutarde", "Moutarde"), + ("orange-terre-battue", "Terre battue"), + ("brown-cafe-creme", "Café crème"), + ("brown-caramel", "Caramel"), + ("brown-opera", "Opéra"), + ("beige-gris-galet", "Gris galet"), + ], + ), + ], + help_text="Utilise les couleurs du système de design de l’État", + label="Couleur d’arrière-plan", + required=False, + ), + ), + ( + "bg_color", + wagtail.blocks.RegexBlock( + error_messages={ + "invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe" + }, + help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", + label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", + regex="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", + required=False, + ), + ), + ("title", wagtail.blocks.CharBlock(label="Titre", required=False)), + ( + "columns", + wagtail.blocks.StreamBlock( + [ + ("text", wagtail.blocks.RichTextBlock(label="Texte avec mise en forme")), + ( + "image", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration" + ), + ), + ( + "alt", + wagtail.blocks.CharBlock( + label="Texte alternatif (description textuelle de l’image)", + required=False, + ), + ), + ( + "caption", + wagtail.blocks.CharBlock(label="Légende", required=False), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ], + label="Image", + ), + ), + ( + "video", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock(label="Titre", required=False), + ), + ("caption", wagtail.blocks.CharBlock(label="Légende")), + ( + "url", + wagtail.blocks.URLBlock( + help_text="URL au format «\xa0embed\xa0» (Ex. : https://www.youtube.com/embed/gLzXOViPX-0)", + label="Lien de la vidéo", + ), + ), + ], + label="Vidéo", + ), + ), + ( + "card", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("description", wagtail.blocks.TextBlock(label="Texte")), + ( + "image", + wagtail.images.blocks.ImageChooserBlock(label="Image"), + ), + ("url", wagtail.blocks.URLBlock(label="Lien", required=False)), + ( + "document", + wagtail.documents.blocks.DocumentChooserBlock( + help_text="Sélectionnez un document pour rendre la carte cliquable vers celui ci (si le champ «\xa0Lien\xa0» n’est pas renseigné).", + label="ou Document", + required=False, + ), + ), + ], + label="Carte", + ), + ), + ( + "quote", + wagtail.blocks.StructBlock( + [ + ( + "image", + wagtail.images.blocks.ImageChooserBlock( + label="Illustration (à gauche)", required=False + ), + ), + ("quote", wagtail.blocks.CharBlock(label="Citation")), + ( + "author_name", + wagtail.blocks.CharBlock(label="Nom de l’auteur"), + ), + ( + "author_title", + wagtail.blocks.CharBlock(label="Titre de l’auteur"), + ), + ], + label="Citation", + ), + ), + ( + "text_cta", + wagtail.blocks.StructBlock( + [ + ( + "text", + wagtail.blocks.RichTextBlock( + label="Texte avec mise en forme", required=False + ), + ), + ( + "cta_label", + wagtail.blocks.CharBlock( + help_text="Le lien apparait comme un bouton sous le bloc de texte", + label="Titre de l’appel à l’action", + required=False, + ), + ), + ( + "cta_url", + wagtail.blocks.CharBlock(label="Lien", required=False), + ), + ], + label="Texte et appel à l’action", + ), + ), + ( + "iframe", + wagtail.blocks.StructBlock( + [ + ( + "title", + wagtail.blocks.CharBlock( + help_text="Accessibilité : Le titre doit décrire, de façon claire et concise, le contenu embarqué.", + label="Titre", + ), + ), + ( + "url", + wagtail.blocks.URLBlock( + help_text="Exemple pour Tally : https://tally.so/embed/w2jMRa", + label="Lien du cadre intégré", + ), + ), + ( + "height", + wagtail.blocks.IntegerBlock(label="Hauteur en pixels"), + ), + ], + label="Cadre intégré", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ], + label="Multi-colonnes", + ), + ), + ( + "accordions", + wagtail.blocks.StreamBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ( + "accordion", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("content", wagtail.blocks.RichTextBlock(label="Contenu")), + ], + label="Accordéon", + max_num=15, + min_num=1, + ), + ), + ], + label="Accordéons", + ), + ), + ( + "stepper", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre")), + ("total", wagtail.blocks.IntegerBlock(label="Nombre d’étapes")), + ("current", wagtail.blocks.IntegerBlock(label="Étape en cours")), + ( + "steps", + wagtail.blocks.StreamBlock( + [ + ( + "step", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titre de l’étape")), + ("detail", wagtail.blocks.TextBlock(label="Détail")), + ], + label="Étape", + ), + ) + ], + label="Les étapes", + ), + ), + ], + label="Étapes", + ), + ), + ( + "separator", + wagtail.blocks.StructBlock( + [ + ( + "top_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement au dessus", max_value=15, min_value=0 + ), + ), + ( + "bottom_margin", + wagtail.blocks.IntegerBlock( + default=3, label="Espacement en dessous", max_value=15, min_value=0 + ), + ), + ], + label="Séparateur", + ), + ), + ("markdown", wagtailmarkdown.blocks.MarkdownBlock()), + ( + "html", + wagtail.blocks.RawHTMLBlock( + help_text="Avertissement : Utilisez le bloc HTML avec précaution.\n Un code malveillant peut compromettre la sécurité du site.", + readonly=True, + ), + ), + ], + blank=True, + ), + ), + migrations.AlterField( + model_name="contentpage", + name="header_cta_label", + field=models.CharField(blank=True, null=True, verbose_name="Call to action label"), + ), + migrations.AlterField( + model_name="contentpage", + name="header_cta_link", + field=models.URLField(blank=True, null=True, verbose_name="Call to action link"), + ), + migrations.AlterField( + model_name="contentpage", + name="header_darken", + field=models.BooleanField(default=False, verbose_name="Darken background image"), + ), + migrations.CreateModel( + name="TagContentPage", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "content_object", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="contentpage_tags", + to="content_manager.contentpage", + ), + ), + ( + "tag", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(app_label)s_%(class)s_items", + to="taggit.tag", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AddField( + model_name="contentpage", + name="tags", + field=modelcluster.contrib.taggit.ClusterTaggableManager( + blank=True, + help_text="A comma-separated list of tags.", + through="content_manager.TagContentPage", + to="taggit.Tag", + verbose_name="Tags", + ), + ), + ] diff --git a/content_manager/models.py b/content_manager/models.py index a290ba77..0a132e8a 100644 --- a/content_manager/models.py +++ b/content_manager/models.py @@ -1,32 +1,40 @@ from django.db import models from django.forms.widgets import Textarea +from django.utils.translation import gettext_lazy as _ +from modelcluster.fields import ParentalKey +from modelcluster.tags import ClusterTaggableManager +from taggit.models import Tag as TaggitTag, TaggedItemBase from wagtail.admin.panels import FieldPanel, MultiFieldPanel from wagtail.contrib.settings.models import BaseSiteSetting, register_setting -from wagtail.fields import StreamField -from wagtail.models import Page -from wagtail.search import index +from wagtail.snippets.models import register_snippet -from content_manager.blocks import STREAMFIELD_ALL_BLOCKS +from content_manager.abstract import SitesFacilesBasePage +from content_manager.managers import TagManager -class ContentPage(Page): - body = StreamField( - STREAMFIELD_ALL_BLOCKS, - blank=True, - use_json_field=True, - ) +class ContentPage(SitesFacilesBasePage): + tags = ClusterTaggableManager(through="TagContentPage", blank=True) - # Editor panels configuration - content_panels = Page.content_panels + [ - FieldPanel("body"), - ] + class Meta: + verbose_name = _("Content page") - # Inherit search_fields from Page and add body to index - search_fields = Page.search_fields + [ - index.SearchField("body"), + settings_panels = SitesFacilesBasePage.settings_panels + [ + FieldPanel("tags"), ] +class TagContentPage(TaggedItemBase): + content_object = ParentalKey("ContentPage", related_name="contentpage_tags") + + +@register_snippet +class Tag(TaggitTag): + objects = TagManager() + + class Meta: + proxy = True + + class MonospaceField(models.TextField): """ A TextField which renders as a large HTML textarea with monospace font. diff --git a/content_manager/templates/content_manager/blocks/blocks_stream.html b/content_manager/templates/content_manager/blocks/blocks_stream.html index 4f978a18..49ce52f2 100644 --- a/content_manager/templates/content_manager/blocks/blocks_stream.html +++ b/content_manager/templates/content_manager/blocks/blocks_stream.html @@ -1,19 +1,7 @@ {% load static dsfr_tags wagtailcore_tags wagtailimages_tags wagtailmarkdown %} {% for block in page.body %} - {% if block.block_type == 'hero' %} - {% include "content_manager/blocks/hero.html" %} -
    {% include "content_manager/blocks/breadcrumbs.html" %}
    - {% elif block.block_type == 'title' %} -
    -
    -
    - {% include "content_manager/blocks/breadcrumbs.html" %} -

    {{ block.value.title }}

    -
    -
    -
    - {% elif block.block_type == 'imageandtext' %} + {% if block.block_type == 'imageandtext' %} {% include "content_manager/blocks/imageandtext.html" %} {% elif block.block_type == 'alert' %} {% include "content_manager/blocks/alert.html" %} @@ -45,12 +33,6 @@

    {{ block.value.title }}

    {% elif block.block_type == 'multicolumns' %} {% include "content_manager/blocks/multicolumns.html" %} {% elif block.block_type == 'paragraph' %} -
    -
    -
    {{ block.value|richtext }}
    -
    -
    - {% elif block.block_type == 'paragraphlarge' %}
    {{ block.value|richtext }}
    diff --git a/content_manager/templates/content_manager/blocks/heading.html b/content_manager/templates/content_manager/blocks/heading.html new file mode 100644 index 00000000..606f0c7d --- /dev/null +++ b/content_manager/templates/content_manager/blocks/heading.html @@ -0,0 +1,24 @@ +{% load static wagtailimages_tags %} +{% if page.header_image or page.header_color_class %} + {% image page.header_image fill-1200x350 as bg_img %} +
    + {% if page.header_with_title %} +
    +
    +
    +

    {{ page.title }}

    + {% if page.header_cta_text %}

    {{ page.header_cta_text }}

    {% endif %} + {% if page.header_cta_link and page.header_cta_label %} + {{ page.header_cta_label }} + {% endif %} +
    +
    +
    + + {% endif %} +
    +{% endif %} diff --git a/content_manager/templates/content_manager/blocks/hero.html b/content_manager/templates/content_manager/blocks/hero.html index 73f00f59..e3e226cb 100644 --- a/content_manager/templates/content_manager/blocks/hero.html +++ b/content_manager/templates/content_manager/blocks/hero.html @@ -3,16 +3,4 @@
    -
    -
    -
    -

    {{ block.value.title }}

    - {% if block.value.text %}

    {{ block.value.text }}

    {% endif %} - {% if block.value.cta_link and block.value.cta_label %} - {{ block.value.cta_label }} - {% endif %} -
    -
    -
    diff --git a/content_manager/templates/content_manager/blocks/hero_image.html b/content_manager/templates/content_manager/blocks/hero_image.html deleted file mode 100644 index 9d283ac0..00000000 --- a/content_manager/templates/content_manager/blocks/hero_image.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load static wagtailimages_tags %} -{% image page.header_image fill-1200x350 as bg_img %} -
    diff --git a/content_manager/templates/content_manager/content_page.html b/content_manager/templates/content_manager/content_page.html index 2abc0548..34371e27 100644 --- a/content_manager/templates/content_manager/content_page.html +++ b/content_manager/templates/content_manager/content_page.html @@ -1,5 +1,7 @@ {% extends "base.html" %} +{% load static dsfr_tags wagtailcore_tags wagtailimages_tags i18n %} + {% block title %} {{ page.seo_title|default:page.title }} — {{ settings.content_manager.CmsDsfrConfig.site_title }} {% endblock title %} @@ -10,8 +12,53 @@ {% endblock description %} {% endif %} +{% block social_media %} + + + + + + + + {% if page.get_translations.live %} + {% for translation in page.get_translations.live %} + + {% endfor %} + {% endif %} + + + + + {% if page.header_image %} + + {% image page.header_image fill-1200x627 as header_image %} + + + {% else %} + + {% endif %} +{% endblock social_media %} + {% block content %} + {% include "content_manager/blocks/heading.html" %} +
    + {% include "content_manager/blocks/breadcrumbs.html" %} + {% if not page.header_with_title %}

    {{ page.title }}

    {% endif %} +
    + {% include "content_manager/blocks/messages.html" %} {% include "content_manager/blocks/blocks_stream.html" %} + +
    + {% for tag in page.tags.all|dictsort:"slug" %} + {{ tag }} + {% endfor %} +
    + {% endblock content %} diff --git a/dashboard/templates/wagtailadmin/login.html b/dashboard/templates/wagtailadmin/login.html index 979427a1..7f9e89c7 100644 --- a/dashboard/templates/wagtailadmin/login.html +++ b/dashboard/templates/wagtailadmin/login.html @@ -44,10 +44,7 @@

    Se connecter avec son compte

    {% url 'wagtailadmin_home' as home_url %}
    - + {{ form.username |add_class:"fr-input" }}
    diff --git a/poetry.lock b/poetry.lock index 932a040f..c6d48e55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -471,13 +471,13 @@ django = ">=4.2" [[package]] name = "django-dsfr" -version = "0.17.1" +version = "0.18.1" description = "Integrate the French government Design System into a Django app" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "django_dsfr-0.17.1-py3-none-any.whl", hash = "sha256:81407df3e8ea9140be097d21bce22f238385b43a298625aa603468c3a1edc191"}, - {file = "django_dsfr-0.17.1.tar.gz", hash = "sha256:a8ecdd8d3e06055fcf537326fd9c94400058c874f96346395971fb7552f3e820"}, + {file = "django_dsfr-0.18.1-py3-none-any.whl", hash = "sha256:8874b6ea9b6267c8d34179f0120670f7e11e8f8733ea592f29955672c67caeda"}, + {file = "django_dsfr-0.18.1.tar.gz", hash = "sha256:ceb19cb5a0ce3d4ae4091964b7ade56e5757f7e7499d6668804b12443c3f212c"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index b4194738..91edd274 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ readme = "README.md" python = "^3.10" django = "^5.0.2" wagtail = "^6.0" -django-dsfr = "^0.17.1" +django-dsfr = "^0.18.1" psycopg2-binary = "^2.9.9" python-dotenv = "^1.0.0" dj-database-url = "^2.1.0" From 313c4128068b8f3ca6347183db879c60732ab9d7 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Wed, 6 Mar 2024 19:07:50 +0100 Subject: [PATCH 2/7] Add back boto3 as a direct dependency because else the poetry update command runs indefinitely --- poetry.lock | 100 ++++++++++++++++++++++++------------------------- pyproject.toml | 1 + 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/poetry.lock b/poetry.lock index c6d48e55..f67c7b6f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -131,17 +131,17 @@ webencodings = "*" [[package]] name = "boto3" -version = "1.34.51" +version = "1.34.56" description = "The AWS SDK for Python" optional = false python-versions = ">= 3.8" files = [ - {file = "boto3-1.34.51-py3-none-any.whl", hash = "sha256:67732634dc7d0afda879bd9a5e2d0818a2c14a98bef766b95a3e253ea5104cb9"}, - {file = "boto3-1.34.51.tar.gz", hash = "sha256:2cd9463e738a184cbce8a6824027c22163c5f73e277a35ff5aa0fb0e845b4301"}, + {file = "boto3-1.34.56-py3-none-any.whl", hash = "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714"}, + {file = "boto3-1.34.56.tar.gz", hash = "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba"}, ] [package.dependencies] -botocore = ">=1.34.51,<1.35.0" +botocore = ">=1.34.56,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -150,13 +150,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.51" +version = "1.34.56" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">= 3.8" files = [ - {file = "botocore-1.34.51-py3-none-any.whl", hash = "sha256:01d5156247f991b3466a8404e3d7460a9ecbd9b214f9992d6ba797d9ddc6f120"}, - {file = "botocore-1.34.51.tar.gz", hash = "sha256:5086217442e67dd9de36ec7e87a0c663f76b7790d5fb6a12de565af95e87e319"}, + {file = "botocore-1.34.56-py3-none-any.whl", hash = "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec"}, + {file = "botocore-1.34.56.tar.gz", hash = "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f"}, ] [package.dependencies] @@ -315,12 +315,12 @@ files = [ [[package]] name = "cssbeautifier" -version = "1.14.11" +version = "1.15.1" description = "CSS unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "cssbeautifier-1.14.11.tar.gz", hash = "sha256:40544c2b62bbcb64caa5e7f37a02df95654e5ce1bcacadac4ca1f3dc89c31513"}, + {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"}, ] [package.dependencies] @@ -391,13 +391,13 @@ static3 = "*" [[package]] name = "django" -version = "5.0.2" +version = "5.0.3" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.10" files = [ - {file = "Django-5.0.2-py3-none-any.whl", hash = "sha256:56ab63a105e8bb06ee67381d7b65fe6774f057e41a8bab06c8020c8882d8ecd4"}, - {file = "Django-5.0.2.tar.gz", hash = "sha256:b5bb1d11b2518a5f91372a282f24662f58f66749666b0a286ab057029f728080"}, + {file = "Django-5.0.3-py3-none-any.whl", hash = "sha256:5c7d748ad113a81b2d44750ccc41edc14e933f56581683db548c9257e078cc83"}, + {file = "Django-5.0.3.tar.gz", hash = "sha256:5fb37580dcf4a262f9258c1f4373819aacca906431f505e4688e37f3a99195df"}, ] [package.dependencies] @@ -516,13 +516,13 @@ Django = ">=3.2" [[package]] name = "django-modelcluster" -version = "6.2.1" +version = "6.3" description = "Django extension to allow working with 'clusters' of models as a single unit, independently of the database" optional = false python-versions = ">=3.8" files = [ - {file = "django-modelcluster-6.2.1.tar.gz", hash = "sha256:3f53d47e1af7aec5e238724be16bbebdac9c518e4788b31429e773dcd8e8ea49"}, - {file = "django_modelcluster-6.2.1-py2.py3-none-any.whl", hash = "sha256:725c005de22191165b690ea05588b1c702ac34b1e9cd8a8b26fadc2a35fcd337"}, + {file = "django-modelcluster-6.3.tar.gz", hash = "sha256:0caed8a0e889f3abb92f144670878a466ef954ffa6c4c7b9c80e6426b720a49d"}, + {file = "django_modelcluster-6.3-py2.py3-none-any.whl", hash = "sha256:a8783d6565a0663f41cd6003ea361c3a5711e8a2a326160f1ec1eceb3e973d4f"}, ] [package.dependencies] @@ -838,13 +838,13 @@ lxml = ["lxml"] [[package]] name = "identify" -version = "2.5.34" +version = "2.5.35" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.34-py2.py3-none-any.whl", hash = "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed"}, - {file = "identify-2.5.34.tar.gz", hash = "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d"}, + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] [package.extras] @@ -863,13 +863,13 @@ files = [ [[package]] name = "ipython" -version = "8.21.0" +version = "8.22.2" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.21.0-py3-none-any.whl", hash = "sha256:1050a3ab8473488d7eee163796b02e511d0735cf43a04ba2a8348bd0f2eaf8a5"}, - {file = "ipython-8.21.0.tar.gz", hash = "sha256:48fbc236fbe0e138b88773fa0437751f14c3645fb483f1d4c5dee58b37e5ce73"}, + {file = "ipython-8.22.2-py3-none-any.whl", hash = "sha256:3c86f284c8f3d8f2b6c662f885c4889a91df7cd52056fd02b7d8d6195d7f56e9"}, + {file = "ipython-8.22.2.tar.gz", hash = "sha256:2dcaad9049f9056f1fef63514f176c7d41f930daa78d05b82a176202818f2c14"}, ] [package.dependencies] @@ -878,16 +878,16 @@ decorator = "*" exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" -traitlets = ">=5" +traitlets = ">=5.13.0" [package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.23)", "pandas", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,terminal]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] @@ -895,7 +895,7 @@ notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath", "trio"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] name = "isort" @@ -943,12 +943,12 @@ files = [ [[package]] name = "jsbeautifier" -version = "1.14.11" +version = "1.15.1" description = "JavaScript unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "jsbeautifier-1.14.11.tar.gz", hash = "sha256:6b632581ea60dd1c133cd25a48ad187b4b91f526623c4b0fb5443ef805250505"}, + {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"}, ] [package.dependencies] @@ -957,13 +957,13 @@ six = ">=1.13.0" [[package]] name = "json5" -version = "0.9.14" +version = "0.9.20" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "json5-0.9.14-py2.py3-none-any.whl", hash = "sha256:740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f"}, - {file = "json5-0.9.14.tar.gz", hash = "sha256:9ed66c3a6ca3510a976a9ef9b8c0787de24802724ab1860bc0153c7fdd589b02"}, + {file = "json5-0.9.20-py3-none-any.whl", hash = "sha256:f623485b37fad95783233bad9352d21526709cbd9a2ec41ddc3e950fca85b701"}, + {file = "json5-0.9.20.tar.gz", hash = "sha256:20a255981244081d5aaa4adc90d31cdbf05bed1863993cbf300b8e2cd2b6de88"}, ] [package.extras] @@ -1317,13 +1317,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest- [[package]] name = "pre-commit" -version = "3.6.1" +version = "3.6.2" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.6.1-py2.py3-none-any.whl", hash = "sha256:9fe989afcf095d2c4796ce7c553cf28d4d4a9b9346de3cda079bcf40748454a4"}, - {file = "pre_commit-3.6.1.tar.gz", hash = "sha256:c90961d8aa706f75d60935aba09469a6b0bcb8345f127c3fbee4bdc5f114cf4b"}, + {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, + {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, ] [package.dependencies] @@ -1492,13 +1492,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -1784,19 +1784,19 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "setuptools" -version = "69.1.0" +version = "69.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, - {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1931,13 +1931,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -1981,13 +1981,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] @@ -2142,4 +2142,4 @@ wand = ["Wand (>=0.6,<1.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "13a4633a715e64c7b244adc500fac0a784e2fd56f710e63010890dbad36c48da" +content-hash = "f194605b2d1b998008db76011a4bd48964fb24ac44bb4192755085ff838f6a0a" diff --git a/pyproject.toml b/pyproject.toml index 91edd274..2971ef21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ wagtail-modeladmin = "^2.0.0" wagtail-markdown = "^0.11.1" unidecode = "^1.3.8" django-storages = {extras = ["s3"], version = "^1.14.2"} +boto3 = "^1.34.56" [tool.poetry.group.dev.dependencies] From 8f1cec3d0a170fccd5844f1afd5419d444b4a936 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Thu, 7 Mar 2024 16:04:40 +0100 Subject: [PATCH 3/7] Remove Hero block --- content_manager/blocks.py | 53 +------------------ content_manager/constants.py | 16 ++++++ .../commands/create_sample_pages.py | 5 +- .../content_manager/blocks/heading.html | 43 +++++++-------- .../content_manager/blocks/hero.html | 6 --- 5 files changed, 44 insertions(+), 79 deletions(-) delete mode 100644 content_manager/templates/content_manager/blocks/hero.html diff --git a/content_manager/blocks.py b/content_manager/blocks.py index 39f18fb3..6c6dc041 100644 --- a/content_manager/blocks.py +++ b/content_manager/blocks.py @@ -6,24 +6,10 @@ from wagtail.images.blocks import ImageChooserBlock from wagtailmarkdown.blocks import MarkdownBlock +from content_manager.constants import HEADING_CHOICES, LEVEL_CHOICES -# Wagtail Block Documentation : https://docs.wagtail.org/en/stable/reference/streamfield/blocks.html - -HEADING_CHOICES = [ - ("h2", "En-tête 2"), - ("h3", "En-tête 3"), - ("h4", "En-tête 4"), - ("h5", "En-tête 5"), - ("h6", "En-tête 6"), - ("p", "Paragraphe"), -] -LEVEL_CHOICES = [ - ("error", "Erreur"), - ("success", "Succès"), - ("info", "Information"), - ("warning", "Attention"), -] +# Wagtail Block Documentation : https://docs.wagtail.org/en/stable/reference/streamfield/blocks.html ## Meta blocks @@ -121,28 +107,6 @@ class CardBlock(blocks.StructBlock): ) -class HeroBlock(blocks.StructBlock): - bg_image = ImageChooserBlock(label="Image d’arrière plan", required=False) - bg_color_class = BackgroundColorChoiceBlock( - label="Couleur d’arrière-plan", - required=False, - help_text="Utilise les couleurs du système de design de l’État", - ) - bg_color = blocks.RegexBlock( - label="Couleur d’arrière-plan au format hexa (Ex: #f5f5fe)", - regex=r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", - help_text="(Obsolète, sera retiré dans une future mise à jour. Remplacez-le par la couleur d’arrière-plan)", - error_messages={"invalid": "La couleur n’est pas correcte, le format doit être #fff ou #f5f5fe"}, - required=False, - ) - title = blocks.CharBlock(label="Titre") - text = blocks.CharBlock(label="Texte", required=False) - cta_label = blocks.CharBlock(label="Texte du bouton", required=False) - cta_link = blocks.URLBlock(label="Lien du bouton", required=False) - large = blocks.BooleanBlock(label="Large", required=False) - darken = blocks.BooleanBlock(label="Assombrir", required=False) - - class IframeBlock(blocks.StructBlock): title = blocks.CharBlock( label="Titre", @@ -231,11 +195,6 @@ class TextAndCTA(blocks.StructBlock): cta_url = blocks.CharBlock(label="Lien", required=False) -class TitleBlock(blocks.StructBlock): - title = blocks.CharBlock(label="Titre") - large = blocks.BooleanBlock(label="Large", required=False) - - class VideoBlock(blocks.StructBlock): title = blocks.CharBlock(label="Titre", required=False) caption = blocks.CharBlock(label="Légende") @@ -274,11 +233,6 @@ class MultiColumnsWithTitleBlock(blocks.StructBlock): columns = MultiColumnsBlock(label="Multi-colonnes") -STREAMFIELD_TITLE_BLOCKS = [ - ("hero", HeroBlock(label="Section promotionnelle")), - ("title", TitleBlock(label="Titre de page")), -] - STREAMFIELD_COMMON_BLOCKS = [ ("paragraph", blocks.RichTextBlock(label="Texte avec mise en forme")), ("image", ImageBlock()), @@ -312,6 +266,3 @@ class MultiColumnsWithTitleBlock(blocks.StructBlock): ), ) ] - - -STREAMFIELD_ALL_BLOCKS = STREAMFIELD_TITLE_BLOCKS + STREAMFIELD_COMMON_BLOCKS diff --git a/content_manager/constants.py b/content_manager/constants.py index d0c963fa..6f40be80 100644 --- a/content_manager/constants.py +++ b/content_manager/constants.py @@ -7,3 +7,19 @@ "subscript", "strikethrough", ] + +HEADING_CHOICES = [ + ("h2", "En-tête 2"), + ("h3", "En-tête 3"), + ("h4", "En-tête 4"), + ("h5", "En-tête 5"), + ("h6", "En-tête 6"), + ("p", "Paragraphe"), +] + +LEVEL_CHOICES = [ + ("error", "Erreur"), + ("success", "Succès"), + ("info", "Information"), + ("warning", "Attention"), +] diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py index b2bf9d71..d58e34e2 100644 --- a/content_manager/management/commands/create_sample_pages.py +++ b/content_manager/management/commands/create_sample_pages.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.core.management.base import BaseCommand from django.urls import reverse from wagtail.models import Page, Site @@ -97,11 +98,13 @@ def create_homepage(self) -> None:

    Vous pouvez maintenant vous connecter dans l’administration et personnaliser le site.

    """ + admin_url = f"{settings.WAGTAILADMIN_BASE_URL}{reverse('wagtailadmin_home')}" + image_and_text_block = { "image": image, "image_ratio": "3", "text": RichText(text_raw), - "link_url": reverse("wagtailadmin_home"), + "link_url": admin_url, "link_label": "Gérer le site", } diff --git a/content_manager/templates/content_manager/blocks/heading.html b/content_manager/templates/content_manager/blocks/heading.html index 606f0c7d..699a8757 100644 --- a/content_manager/templates/content_manager/blocks/heading.html +++ b/content_manager/templates/content_manager/blocks/heading.html @@ -1,24 +1,25 @@ {% load static wagtailimages_tags %} {% if page.header_image or page.header_color_class %} - {% image page.header_image fill-1200x350 as bg_img %} -
    - {% if page.header_with_title %} -
    -
    -
    -

    {{ page.title }}

    - {% if page.header_cta_text %}

    {{ page.header_cta_text }}

    {% endif %} - {% if page.header_cta_link and page.header_cta_label %} - {{ page.header_cta_label }} - {% endif %} -
    -
    -
    - - {% endif %} -
    + {% image page.header_image fill-1200x350 as bg_img %} +
    + {% if page.header_with_title or page.header_cta_link and page.header_cta_label %} +
    +
    +
    + {% if page.header_with_title %} +

    {{ page.title }}

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

    {{ page.header_cta_text }}

    {% endif %} + {% if page.header_cta_link and page.header_cta_label %} + {{ page.header_cta_label }} + {% endif %} +
    +
    +
    + {% endif %} +
    {% endif %} diff --git a/content_manager/templates/content_manager/blocks/hero.html b/content_manager/templates/content_manager/blocks/hero.html deleted file mode 100644 index e3e226cb..00000000 --- a/content_manager/templates/content_manager/blocks/hero.html +++ /dev/null @@ -1,6 +0,0 @@ -{% load static wagtailimages_tags %} -{% image block.value.bg_image original as bg_img %} -
    -
    From 08fad2f5e2e9b65db75d9b6ccd0658620a008f33 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Thu, 7 Mar 2024 18:25:26 +0100 Subject: [PATCH 4/7] Start working on the tag management for ContentPages --- blog/templates/blog/blog_index_page.html | 66 +++++++------- content_manager/managers.py | 5 +- .../content_manager/content_page.html | 4 +- .../templates/content_manager/tag_page.html | 86 +++++++++++++++++++ .../content_manager/tags_list_page.html | 40 +++++++++ content_manager/urls.py | 4 +- content_manager/views.py | 57 +++++++++++- 7 files changed, 219 insertions(+), 43 deletions(-) create mode 100644 content_manager/templates/content_manager/tag_page.html create mode 100644 content_manager/templates/content_manager/tags_list_page.html diff --git a/blog/templates/blog/blog_index_page.html b/blog/templates/blog/blog_index_page.html index 0be1f18e..36c98b64 100644 --- a/blog/templates/blog/blog_index_page.html +++ b/blog/templates/blog/blog_index_page.html @@ -67,45 +67,43 @@

    {{ page.title }}

    - {% if posts %} - {% for post in posts %} -
    - diff --git a/content_manager/managers.py b/content_manager/managers.py index 54736295..05c47d94 100644 --- a/content_manager/managers.py +++ b/content_manager/managers.py @@ -3,6 +3,5 @@ class TagManager(models.Manager): - def most_common(self, blog_page): - entries = blog_page.get_entries() - return self.filter(entrypage__in=entries).annotate(num_times=Count("entrypage")).order_by("-num_times") + def tags_with_usecount(self, min_count=0): + return self.annotate(usecount=Count("contentpage")).filter(usecount__gte=min_count) diff --git a/content_manager/templates/content_manager/content_page.html b/content_manager/templates/content_manager/content_page.html index 34371e27..a52e75f7 100644 --- a/content_manager/templates/content_manager/content_page.html +++ b/content_manager/templates/content_manager/content_page.html @@ -55,9 +55,7 @@
    {% for tag in page.tags.all|dictsort:"slug" %} - {{ tag }} + {{ tag }} {% endfor %}
    diff --git a/content_manager/templates/content_manager/tag_page.html b/content_manager/templates/content_manager/tag_page.html new file mode 100644 index 00000000..a1c3f506 --- /dev/null +++ b/content_manager/templates/content_manager/tag_page.html @@ -0,0 +1,86 @@ +{% extends "base.html" %} + +{% load static dsfr_tags wagtailcore_tags wagtailimages_tags i18n %} + +{% block title %} + {% blocktranslate %}Posts tagged with {{ tag }}{% endblocktranslate %} +{% endblock title %} + +{% if page.search_description %} + {% block description %} + + {% endblock description %} +{% endif %} + +{% block social_media %} + + + + + + + + {% if page.get_translations.live %} + {% for translation in page.get_translations.live %} + + {% endfor %} + {% endif %} + + + + + {% if page.header_image %} + + {% image page.header_image fill-1200x627 as header_image %} + + + {% else %} + + {% endif %} +{% endblock social_media %} + +{% block content %} + {% include "content_manager/blocks/messages.html" %} + +
    + {% dsfr_breadcrumb breadcrumb %} +

    {% blocktranslate %}Posts tagged with {{ tag }}{% endblocktranslate %}

    +
    + +
    +
    + {% for entry in object_list %} +
    + +
    + {% empty %} +

    Aucun article trouvé.

    + {% endfor %} +
    +
    + + {% if posts.paginator.num_pages > 1 %} +
    {% dsfr_pagination posts %}
    + {% endif %} + +{% endblock content %} diff --git a/content_manager/templates/content_manager/tags_list_page.html b/content_manager/templates/content_manager/tags_list_page.html new file mode 100644 index 00000000..b6302d4a --- /dev/null +++ b/content_manager/templates/content_manager/tags_list_page.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% load static dsfr_tags wagtailcore_tags wagtailimages_tags i18n %} + +{% block title %} + {% translate "All tags" %} — {{ settings.content_manager.CmsDsfrConfig.site_title }} +{% endblock title %} + +{% if page.search_description %} + {% block description %} + + {% endblock description %} +{% endif %} + +{% block content %} + {% include "content_manager/blocks/messages.html" %} + +
    + {% dsfr_breadcrumb breadcrumb %} +

    {% translate "Tags" %}

    + +
    + {% for first_letter in sorted_tags.keys|dictsort:0 %} + {{ first_letter }} + {% endfor %} +
    + + {% for first_letter, tags in sorted_tags.items|dictsort:0 %} +

    {{ first_letter }}

    + + {% endfor %} +
    + +{% endblock content %} diff --git a/content_manager/urls.py b/content_manager/urls.py index a67669c8..04aa12ad 100644 --- a/content_manager/urls.py +++ b/content_manager/urls.py @@ -3,7 +3,7 @@ from wagtail.admin import urls as wagtailadmin_urls from wagtail.documents import urls as wagtaildocs_urls -from content_manager.views import SearchResultsView +from content_manager.views import SearchResultsView, TagsListView, TagView urlpatterns = [ @@ -11,5 +11,7 @@ path("documents/", include(wagtaildocs_urls)), path("recherche/", SearchResultsView.as_view(), name="cms_search"), path("", include("blog.urls", namespace="blog")), + path("tags//", TagView.as_view(), name="global_tag"), + path("tags/", TagsListView.as_view(), name="global_tags_list"), path("", include(wagtail_urls)), ] diff --git a/content_manager/views.py b/content_manager/views.py index 946785db..7b116db7 100644 --- a/content_manager/views.py +++ b/content_manager/views.py @@ -1,6 +1,10 @@ -from django.views.generic import ListView +from django.shortcuts import get_object_or_404 +from django.utils.translation import gettext_lazy as _ +from django.views.generic import ListView, TemplateView +from unidecode import unidecode +from wagtail.models import Site -from content_manager.models import ContentPage +from content_manager.models import ContentPage, Tag class SearchResultsView(ListView): @@ -20,3 +24,52 @@ def get_context_data(self, **kwargs): context = super(SearchResultsView, self).get_context_data(**kwargs) context["query"] = self.request.GET.get("q") return context + + +class TagsListView(TemplateView): + template_name = "content_manager/tags_list_page.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + tags = Tag.objects.tags_with_usecount(1) + + tags_by_first_letter = {} + for tag in tags: + first_letter = unidecode(tag.slug[0].upper()) + if first_letter not in tags_by_first_letter: + tags_by_first_letter[first_letter] = [] + tags_by_first_letter[first_letter].append(tag) + + breadcrumb = { + "links": [], + "current": _("Tags"), + } + + context["sorted_tags"] = tags_by_first_letter + context["breadcrumb"] = breadcrumb + return context + + +class TagView(ListView): + template_name = "content_manager/tag_page.html" + model = ContentPage + paginate_by = 10 + + def get_queryset(self, **kwargs): + tag_slug = self.kwargs.get("tag") + return ContentPage.objects.filter(tags__slug=tag_slug) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + tag_slug = self.kwargs.get("tag") + + print(tag_slug) + + tag = get_object_or_404(Tag, slug=tag_slug) + context["tag"] = tag + + # Use the homepage for context + context["page"] = Site.objects.filter(is_default_site=True).first().root_page + + return context From 6badc6dca9c7d199054ddd93b14ce34647a59bcf Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Mon, 11 Mar 2024 12:47:31 +0100 Subject: [PATCH 5/7] Update tag-related pages --- .../templates/content_manager/tag_page.html | 45 ++++++------------- .../content_manager/tags_list_page.html | 24 +++++++--- content_manager/views.py | 27 +++++++---- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/content_manager/templates/content_manager/tag_page.html b/content_manager/templates/content_manager/tag_page.html index a1c3f506..750c9d36 100644 --- a/content_manager/templates/content_manager/tag_page.html +++ b/content_manager/templates/content_manager/tag_page.html @@ -3,43 +3,24 @@ {% load static dsfr_tags wagtailcore_tags wagtailimages_tags i18n %} {% block title %} - {% blocktranslate %}Posts tagged with {{ tag }}{% endblocktranslate %} + {{ title }} — {{ settings.content_manager.CmsDsfrConfig.site_title }} {% endblock title %} -{% if page.search_description %} - {% block description %} - - {% endblock description %} -{% endif %} +{% block description %} + +{% endblock description %} {% block social_media %} - - - - - + + + + + - {% if page.get_translations.live %} - {% for translation in page.get_translations.live %} - - {% endfor %} - {% endif %} - - - - - {% if page.header_image %} - - {% image page.header_image fill-1200x627 as header_image %} - - - {% else %} - - {% endif %} + + {% endblock social_media %} {% block content %} @@ -47,7 +28,7 @@
    {% dsfr_breadcrumb breadcrumb %} -

    {% blocktranslate %}Posts tagged with {{ tag }}{% endblocktranslate %}

    +

    {{ title }}

    diff --git a/content_manager/templates/content_manager/tags_list_page.html b/content_manager/templates/content_manager/tags_list_page.html index b6302d4a..68055a38 100644 --- a/content_manager/templates/content_manager/tags_list_page.html +++ b/content_manager/templates/content_manager/tags_list_page.html @@ -3,14 +3,25 @@ {% load static dsfr_tags wagtailcore_tags wagtailimages_tags i18n %} {% block title %} - {% translate "All tags" %} — {{ settings.content_manager.CmsDsfrConfig.site_title }} + {{ title }} — {{ settings.content_manager.CmsDsfrConfig.site_title }} {% endblock title %} -{% if page.search_description %} - {% block description %} - - {% endblock description %} -{% endif %} +{% block description %} + +{% endblock description %} + +{% block social_media %} + + + + + + + + + +{% endblock social_media %} {% block content %} {% include "content_manager/blocks/messages.html" %} @@ -36,5 +47,4 @@

    {{ first_letter }}

    {% endfor %}
    - {% endblock content %} diff --git a/content_manager/views.py b/content_manager/views.py index 7b116db7..c2cff10a 100644 --- a/content_manager/views.py +++ b/content_manager/views.py @@ -1,8 +1,8 @@ from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView, TemplateView from unidecode import unidecode -from wagtail.models import Site from content_manager.models import ContentPage, Tag @@ -41,13 +41,16 @@ def get_context_data(self, **kwargs): tags_by_first_letter[first_letter] = [] tags_by_first_letter[first_letter].append(tag) - breadcrumb = { + context["sorted_tags"] = tags_by_first_letter + + title = _("Tags") + context["title"] = title + context["breadcrumb"] = { "links": [], - "current": _("Tags"), + "current": title, } + context["search_description"] = _("List of all the tags.") - context["sorted_tags"] = tags_by_first_letter - context["breadcrumb"] = breadcrumb return context @@ -64,12 +67,18 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tag_slug = self.kwargs.get("tag") - print(tag_slug) - tag = get_object_or_404(Tag, slug=tag_slug) context["tag"] = tag - # Use the homepage for context - context["page"] = Site.objects.filter(is_default_site=True).first().root_page + title = _("Pages tagged with {tag}").format(tag=tag.name) + context["title"] = title + context["breadcrumb"] = { + "links": [ + {"url": reverse("global_tags_list"), "title": _("Tags")}, + ], + "current": title, + } + + context["search_description"] = _("List of pages tagged with {tag}").format(tag=tag.name) return context From 30a023edc8ff9122f4e1241aeb5b267e00a6b941 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Mon, 11 Mar 2024 12:48:49 +0100 Subject: [PATCH 6/7] Save an abstract of the text as a default search description --- content_manager/abstract.py | 8 ++++++ content_manager/utils.py | 49 +++++++++++++++++++++++++++++++++++++ poetry.lock | 2 +- pyproject.toml | 1 + 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/content_manager/abstract.py b/content_manager/abstract.py index b71b843e..07b96566 100644 --- a/content_manager/abstract.py +++ b/content_manager/abstract.py @@ -8,6 +8,7 @@ from wagtail.search import index from content_manager.blocks import STREAMFIELD_COMMON_BLOCKS +from content_manager.utils import get_streamfield_raw_text class SitesFacilesBasePage(Page): @@ -89,6 +90,13 @@ class SitesFacilesBasePage(Page): def get_absolute_url(self): return self.url + def save(self, *args, **kwargs): + if not self.search_description: + search_description = get_streamfield_raw_text(self.body, max_words=20) + if search_description: + self.search_description = search_description + return super().save(*args, **kwargs) + class Meta: abstract = True verbose_name = _("Base page") diff --git a/content_manager/utils.py b/content_manager/utils.py index a2439cab..75e80d4e 100644 --- a/content_manager/utils.py +++ b/content_manager/utils.py @@ -1,5 +1,8 @@ +import re +from html import unescape from io import BytesIO +from bs4 import BeautifulSoup from django.core.files.images import ImageFile from wagtail.images.models import Image from wagtail.models import Site @@ -33,3 +36,49 @@ def get_or_create_footer_menu() -> FlatMenu: footer_menu = FlatMenu.objects.create(title="Pied de page", handle="footer", site=default_site) return footer_menu + + +def get_streamblock_raw_text(block) -> str: + """ + Get the raw text of a streamblock. + """ + + # Remove entirely some block types + removable_blocks = ["image", "alert", "video", "stepper", "separator", "html", "iframe"] + + raw_text = "" + if block.block.name == "imageandtext": + raw_text += block.value["text"].source + elif block.block.name == "multicolumns": + for column in block.value["columns"]: + raw_text += get_streamblock_raw_text(column) + elif block.block.name not in removable_blocks: + raw_text += block.render() + + return raw_text + + +def get_streamfield_raw_text(streamfield, max_words: int | None = None) -> str: + """ + Get the raw text of a streamfield. Used to pre-fill the search description field + """ + + raw_html = "" + raw_text = "" + for block in streamfield: + raw_html += get_streamblock_raw_text(block) + + if not raw_html: + return "" + + soup = BeautifulSoup(raw_html, "html.parser") + raw_text += soup.get_text(" ") + + raw_text = unescape(raw_text) + raw_text = re.sub(r" +", " ", raw_text).strip() + + if max_words: + words = raw_text.split() + raw_text = " ".join(words[:max_words]) + " […]" + + return raw_text diff --git a/poetry.lock b/poetry.lock index f67c7b6f..4151da89 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2142,4 +2142,4 @@ wand = ["Wand (>=0.6,<1.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "f194605b2d1b998008db76011a4bd48964fb24ac44bb4192755085ff838f6a0a" +content-hash = "942287fdc0dffbf1d664db730af498c5bbdb87f2d84795ccaa7a9874da49b81d" diff --git a/pyproject.toml b/pyproject.toml index 2971ef21..6f9d17c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ wagtail-markdown = "^0.11.1" unidecode = "^1.3.8" django-storages = {extras = ["s3"], version = "^1.14.2"} boto3 = "^1.34.56" +beautifulsoup4 = "^4.12.3" [tool.poetry.group.dev.dependencies] From a46ed44a142c92c1a53f718f021298fc327b8799 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Mon, 11 Mar 2024 15:42:14 +0100 Subject: [PATCH 7/7] Add a command to set the config --- .gitignore | 1 + Makefile | 1 + config.json.example | 9 +++++++++ .../management/commands/set_config.py | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 config.json.example create mode 100644 content_manager/management/commands/set_config.py diff --git a/.gitignore b/.gitignore index c7436fd8..7f0b44bd 100644 --- a/.gitignore +++ b/.gitignore @@ -146,4 +146,5 @@ dmypy.json .vscode/ # Project-specific stuff +config.json cron.json diff --git a/Makefile b/Makefile index 375474b9..8406a796 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ init: $(EXEC_CMD) poetry run pre-commit install $(EXEC_CMD) poetry run python manage.py migrate make collectstatic + $(EXEC_CMD) poetry run python manage.py set_config $(EXEC_CMD) poetry run python manage.py create_sample_pages .PHONY: runserver diff --git a/config.json.example b/config.json.example new file mode 100644 index 00000000..7ade9b53 --- /dev/null +++ b/config.json.example @@ -0,0 +1,9 @@ +{ + "header_brand": "Intitulé officiel", + "header_brand_html": "Intitulé
    officiel", + "footer_brand": "Intitulé officiel", + "footer_brand_html": "Intitulé
    officiel", + "site_title": "Titre du site", + "site_tagline": "Sous-titre du site", + "footer_description": "Description" +} diff --git a/content_manager/management/commands/set_config.py b/content_manager/management/commands/set_config.py new file mode 100644 index 00000000..1177e632 --- /dev/null +++ b/content_manager/management/commands/set_config.py @@ -0,0 +1,19 @@ +import json +from os.path import isfile + +from django.core.management.base import BaseCommand + +from content_manager.models import CmsDsfrConfig + + +class Command(BaseCommand): + def handle(self, *args, **kwargs): + if isfile("config.json"): + with open("config.json") as config_file: + config_data = json.load(config_file) + + _config, created = CmsDsfrConfig.objects.get_or_create(id=1, defaults=config_data) + if created: + self.stdout.write(self.style.SUCCESS(f"Config imported for {config_data.get('site_title', '')}")) + else: + self.stdout.write(self.style.SUCCESS("Config already existing, data not imported."))