diff --git a/config/settings.py b/config/settings.py
index ccc1f175d..e4a7666da 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -131,6 +131,8 @@
TIME_ZONE = "UTC"
+USE_L10N = True
+
USE_I18N = True
USE_TZ = True
diff --git a/dsfr/admin.py b/dsfr/admin.py
index 07e827915..36942f667 100644
--- a/dsfr/admin.py
+++ b/dsfr/admin.py
@@ -15,18 +15,34 @@ class DsfrConfigAdmin(admin.ModelAdmin):
fieldsets = (
("", {"fields": ("language",)}),
(
- "Site",
+ _("Website"),
{
"fields": (
("site_title", "beta_tag"),
"site_tagline",
- "notice",
"mourning",
)
},
),
(
- "En-tête",
+ _("Notice"),
+ {
+ "fields": (
+ "notice_title",
+ "notice_description",
+ "notice_type",
+ "notice_link",
+ "notice_icon_class",
+ "notice_is_collapsible",
+ ),
+ "description": _(
+ "The important notice banner should only be used for essential and temporary information. \
+ (Excessive or continuous use risks “drowning” the message.)"
+ ),
+ },
+ ),
+ (
+ _("Header"),
{
"fields": (
"header_brand",
@@ -35,7 +51,7 @@ class DsfrConfigAdmin(admin.ModelAdmin):
},
),
(
- "Pied de page",
+ _("Footer"),
{
"fields": (
"footer_brand",
@@ -46,7 +62,7 @@ class DsfrConfigAdmin(admin.ModelAdmin):
},
),
(
- "Logo opérateur",
+ _("Operator logo"),
{
"fields": (
"operator_logo_file",
diff --git a/dsfr/constants.py b/dsfr/constants.py
index 378db3f06..d550ccf89 100644
--- a/dsfr/constants.py
+++ b/dsfr/constants.py
@@ -57,6 +57,34 @@
(_("Illustration colors"), COLOR_CHOICES_ILLUSTRATION),
]
+# Types allowed for the site Notice
+NOTICE_TYPE_CHOICES = [
+ (
+ _("Generic notices"),
+ [
+ ("info", _("Information")),
+ ("warning", _("Warning")),
+ ("alert", _("Alert")),
+ ],
+ ),
+ (
+ _("Weather alert notices"),
+ [
+ ("weather-orange", _("Orange weather alert")),
+ ("weather-red", _("Red weather alert")),
+ ("weather-purple", _("Purple weather alert")),
+ ],
+ ),
+ (
+ _("Alert notices"),
+ [
+ ("attack", _("Attack alert")),
+ ("witness", _("Call for witnesses")),
+ ("cyberattack", _("Cyberattack")),
+ ],
+ ),
+]
+
# Ratio classes used for medias and cards
IMAGE_RATIOS = [
("fr-ratio-32x9", "32x9"),
diff --git a/dsfr/locale/fr/LC_MESSAGES/django.mo b/dsfr/locale/fr/LC_MESSAGES/django.mo
index 3e72743e8..1d02895c7 100644
Binary files a/dsfr/locale/fr/LC_MESSAGES/django.mo and b/dsfr/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/dsfr/locale/fr/LC_MESSAGES/django.po b/dsfr/locale/fr/LC_MESSAGES/django.po
index 2e0bef32d..f70d4f31d 100644
--- a/dsfr/locale/fr/LC_MESSAGES/django.po
+++ b/dsfr/locale/fr/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-08-19 12:48+0200\n"
-"PO-Revision-Date: 2024-08-19 12:48+0200\n"
+"POT-Creation-Date: 2024-08-23 09:51+0200\n"
+"PO-Revision-Date: 2024-08-23 10:09+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
@@ -18,7 +18,37 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.4.2\n"
-#: dsfr/admin.py:59
+#: dsfr/admin.py:18
+msgid "Website"
+msgstr "Site"
+
+#: dsfr/admin.py:28
+msgid "Notice"
+msgstr "Bandeau d’information importante"
+
+#: dsfr/admin.py:39
+msgid ""
+"The important notice banner should only be used for essential and temporary "
+"information. (Excessive or continuous use risks "
+"“drowning” the message.)"
+msgstr ""
+"Le bandeau d’information importante doit être utilisé uniquement pour une "
+"information primordiale et temporaire. (Une utilisation excessive ou "
+"continue risque de “noyer” le message)"
+
+#: dsfr/admin.py:45
+msgid "Header"
+msgstr "En-tête"
+
+#: dsfr/admin.py:54
+msgid "Footer"
+msgstr "Pied de page"
+
+#: dsfr/admin.py:65 dsfr/models.py:129
+msgid "Operator logo"
+msgstr "Logo opérateur"
+
+#: dsfr/admin.py:75
msgid "Newsletter"
msgstr "Lettre d’information"
@@ -50,7 +80,7 @@ msgstr "Info"
msgid "Success"
msgstr "Succès"
-#: dsfr/constants.py:23
+#: dsfr/constants.py:23 dsfr/constants.py:66
msgid "Warning"
msgstr "Avertissement"
@@ -74,6 +104,50 @@ msgstr "Couleurs illustratives"
msgid "System colors"
msgstr "Couleurs système"
+#: dsfr/constants.py:63
+msgid "Generic notices"
+msgstr "Bandeaux génériques"
+
+#: dsfr/constants.py:65
+msgid "Information"
+msgstr "Information"
+
+#: dsfr/constants.py:67
+msgid "Alert"
+msgstr "Alerte"
+
+#: dsfr/constants.py:71
+msgid "Weather alert notices"
+msgstr "Bandeaux de vigilance météo"
+
+#: dsfr/constants.py:73
+msgid "Orange weather alert"
+msgstr "Vigilance météo orange"
+
+#: dsfr/constants.py:74
+msgid "Red weather alert"
+msgstr "Vigilance météo rouge"
+
+#: dsfr/constants.py:75
+msgid "Purple weather alert"
+msgstr "Vigilance météo violette"
+
+#: dsfr/constants.py:79
+msgid "Alert notices"
+msgstr "Bandeaux d’alerte"
+
+#: dsfr/constants.py:81
+msgid "Attack alert"
+msgstr "Alerte attentat"
+
+#: dsfr/constants.py:82
+msgid "Call for witnesses"
+msgstr "Appel à témoins"
+
+#: dsfr/constants.py:83
+msgid "Cyberattack"
+msgstr "Cyberattaque"
+
#: dsfr/models.py:20
msgid "Language"
msgstr "Langue"
@@ -102,109 +176,137 @@ msgstr "Titre du site"
msgid "Site tagline"
msgstr "Sous-titre du site"
-#: dsfr/models.py:42
-msgid "Important notice"
-msgstr "Bandeau d’information importante"
+#: dsfr/models.py:44
+msgid "Notice title"
+msgstr "Titre du bandeau"
+
+#: dsfr/models.py:48 dsfr/models.py:55
+msgid "Can include HTML"
+msgstr "Peut inclure du HTML"
+
+#: dsfr/models.py:52
+msgid "Notice description"
+msgstr "Description du bandeau"
-#: dsfr/models.py:46
+#: dsfr/models.py:59
+msgid "Notice type"
+msgstr "Type de bandeau"
+
+#: dsfr/models.py:65
msgid ""
-"The important notice banner should only be used for essential and temporary "
-"information. (Excessive or continuous use risks “drowning” the "
-"message.)"
+"Use is strictly regulated, see documentation."
msgstr ""
-"Le bandeau d’information importante doit être utilisé uniquement pour une "
-"information primordiale et temporaire. (Une utilisation excessive ou "
-"continue risque de “noyer” le message)"
+"L’usage de ce bandeau est strictement encadré, voir la documentation."
-#: dsfr/models.py:53
+#: dsfr/models.py:71
+msgid "Notice link"
+msgstr "Lien du bandeau"
+
+#: dsfr/models.py:74
+msgid "Standardized consultation link at the end of the notice."
+msgstr "Lien de consultation standardisé à la fin du bandeau."
+
+#: dsfr/models.py:78
+msgid "Notice icon class"
+msgstr "Classe d’icône du bandeau"
+
+#: dsfr/models.py:82
+msgid "For weather alerts only"
+msgstr "Pour les bandeaux de vigilance météo uniquement"
+
+#: dsfr/models.py:85
+msgid "Collapsible?"
+msgstr "Repliable ?"
+
+#: dsfr/models.py:89
msgid "Institution (header)"
msgstr "Institution (en-tête)"
-#: dsfr/models.py:59
+#: dsfr/models.py:95
msgid "Institution with line break (header)"
msgstr "Institution avec césure (en-tête)"
-#: dsfr/models.py:64
+#: dsfr/models.py:100
msgid "Show the BETA tag next to the title"
msgstr "Afficher la mention BETA à côté du titre"
-#: dsfr/models.py:68
+#: dsfr/models.py:104
msgid "Institution (footer)"
-msgstr "Institution (pied"
+msgstr "Institution (pied)"
-#: dsfr/models.py:74
+#: dsfr/models.py:110
msgid "Institution with line break (footer)"
msgstr "Institution avec césure (pied)"
-#: dsfr/models.py:79
+#: dsfr/models.py:115
msgid "Description"
msgstr "Description"
-#: dsfr/models.py:82
+#: dsfr/models.py:118
msgid "Newsletter description"
msgstr "Description de la lettre d’information"
-#: dsfr/models.py:86
+#: dsfr/models.py:122
msgid "Newsletter registration URL"
msgstr "URL d‘inscription à la lettre d’information"
-#: dsfr/models.py:93
-msgid "Operator logo"
-msgstr "Logo opérateur"
-
-#: dsfr/models.py:100
+#: dsfr/models.py:136
msgid "Logo alt text"
msgstr "Alternative textuelle du logo"
-#: dsfr/models.py:103
+#: dsfr/models.py:139
msgid "Must contain the text present in the image."
msgstr "Doit impérativement contenir le texte présent dans l’image."
-#: dsfr/models.py:106
+#: dsfr/models.py:142
msgid "Width (em)"
msgstr "Largeur (em)"
-#: dsfr/models.py:112
+#: dsfr/models.py:148
msgid ""
"To be adjusted according to the width of the logo. Example for a "
"vertical logo: 3.5, Example for a horizontal logo: 8."
msgstr ""
-"À ajuster en fonction de la largeur du logo. Exemple pour un logo vertical: "
-"3.5, Exemple pour un logo horizontal: 8."
+"À ajuster en fonction de la largeur du logo. Exemple pour un logo "
+"vertical: 3.5, Exemple pour un logo horizontal: 8."
-#: dsfr/models.py:118
+#: dsfr/models.py:154
msgid "Mourning"
msgstr "Mise en berne"
-#: dsfr/models.py:121
+#: dsfr/models.py:157
msgid "Accessibility compliance status"
msgstr "Statut de conformité de l’accessibilité"
-#: dsfr/models.py:128
+#: dsfr/models.py:164
msgid "Configuration"
msgstr "Configuration"
-#: dsfr/models.py:131
+#: dsfr/models.py:167
msgid "Site config:"
msgstr "Configuration du site :"
-#: dsfr/models.py:139
+#: dsfr/models.py:175
msgid "Title"
msgstr "Titre"
-#: dsfr/models.py:142
+#: dsfr/models.py:178
msgid "URL"
msgstr "URL"
-#: dsfr/models.py:147
+#: dsfr/models.py:183
msgid "Icon class"
msgstr "Classe d’icône"
-#: dsfr/models.py:154
+#: dsfr/models.py:190
msgid "Social media"
msgstr "Réseau social"
-#: dsfr/models.py:155
+#: dsfr/models.py:191
msgid "Social medias"
msgstr "Réseaux sociaux"
@@ -263,7 +365,7 @@ msgstr "Abonnez-vous à notre lettre d’information"
#: dsfr/templates/dsfr/follow.html:18
msgctxt "Button title"
msgid "Subscribe to our newsletter"
-msgstr "S'abonner à notre lettre d’information"
+msgstr "S’abonner à notre lettre d’information"
#: dsfr/templates/dsfr/follow.html:24
msgid "Subscribe"
@@ -271,7 +373,7 @@ msgstr "S’abonner"
#: dsfr/templates/dsfr/follow.html:34
msgid "Follow us
on social media"
-msgstr "Suivez-nous
sur les réseaux sociaux"
+msgstr "Suivez-nous
sur les réseaux sociaux"
#: dsfr/templates/dsfr/footer.html:9
msgid "Back to home page"
diff --git a/dsfr/migrations/0009_dsfrconfig_notice_type_alter_dsfrconfig_notice.py b/dsfr/migrations/0009_dsfrconfig_notice_type_alter_dsfrconfig_notice.py
new file mode 100644
index 000000000..4da002c53
--- /dev/null
+++ b/dsfr/migrations/0009_dsfrconfig_notice_type_alter_dsfrconfig_notice.py
@@ -0,0 +1,44 @@
+# Generated by Django 4.2.15 on 2024-08-23 06:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("dsfr", "0008_alter_dsfrsocialmedia_options_dsfrconfig_language_and_more"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="dsfrconfig",
+ name="notice_type",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("info", "Information"),
+ ("warning", "Warning"),
+ ("alert", "Alert"),
+ ("weather-orange", "Orange weather alert"),
+ ("weather-red", "Red weather alert"),
+ ("weather-purple", "Purple weather alert"),
+ ("attack", "Attack alert"),
+ ("witness", "Call for witnesses"),
+ ("cyberattack", "Cyberattack"),
+ ],
+ help_text='Use is strictly regulated, see documentation.',
+ max_length=20,
+ verbose_name="Notice type",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="dsfrconfig",
+ name="notice",
+ field=models.TextField(
+ blank=True,
+ default="",
+ help_text="The important notice banner should only be used for essential and temporary information. (Excessive or continuous use risks “drowning” the message.)",
+ verbose_name="Important notice content",
+ ),
+ ),
+ ]
diff --git a/dsfr/migrations/0010_dsfrconfig_notice_icon_class_alter_dsfrconfig_notice_and_more.py b/dsfr/migrations/0010_dsfrconfig_notice_icon_class_alter_dsfrconfig_notice_and_more.py
new file mode 100644
index 000000000..730818ce8
--- /dev/null
+++ b/dsfr/migrations/0010_dsfrconfig_notice_icon_class_alter_dsfrconfig_notice_and_more.py
@@ -0,0 +1,71 @@
+# Generated by Django 4.2.15 on 2024-08-23 07:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("dsfr", "0009_dsfrconfig_notice_type_alter_dsfrconfig_notice"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="dsfrconfig",
+ name="notice_icon_class",
+ field=models.CharField(
+ blank=True,
+ default="",
+ help_text="For weather alerts only",
+ max_length=200,
+ verbose_name="Notice icon class",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="dsfrconfig",
+ name="notice",
+ field=models.TextField(
+ blank=True,
+ default="",
+ help_text="The important notice banner should only be used for essential and temporary information. (Excessive or continuous use risks “drowning” the message.)",
+ verbose_name="Notice content",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="dsfrconfig",
+ name="notice_type",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ (
+ "Generic notices",
+ [
+ ("info", "Information"),
+ ("warning", "Warning"),
+ ("alert", "Alert"),
+ ],
+ ),
+ (
+ "Weather alert notices",
+ [
+ ("weather-orange", "Orange weather alert"),
+ ("weather-red", "Red weather alert"),
+ ("weather-purple", "Purple weather alert"),
+ ],
+ ),
+ (
+ "Alert notices",
+ [
+ ("attack", "Attack alert"),
+ ("witness", "Call for witnesses"),
+ ("cyberattack", "Cyberattack"),
+ ],
+ ),
+ ],
+ default="info",
+ help_text='Use is strictly regulated, see documentation.',
+ max_length=20,
+ verbose_name="Notice type",
+ ),
+ ),
+ ]
diff --git a/dsfr/migrations/0011_rename_notice_dsfrconfig_notice_title.py b/dsfr/migrations/0011_rename_notice_dsfrconfig_notice_title.py
new file mode 100644
index 000000000..a1326ff90
--- /dev/null
+++ b/dsfr/migrations/0011_rename_notice_dsfrconfig_notice_title.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.15 on 2024-08-23 07:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("dsfr", "0010_dsfrconfig_notice_icon_class_alter_dsfrconfig_notice_and_more"),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name="dsfrconfig",
+ old_name="notice",
+ new_name="notice_title",
+ ),
+ ]
diff --git a/dsfr/migrations/0012_dsfrconfig_notice_description_and_more.py b/dsfr/migrations/0012_dsfrconfig_notice_description_and_more.py
new file mode 100644
index 000000000..f84623d82
--- /dev/null
+++ b/dsfr/migrations/0012_dsfrconfig_notice_description_and_more.py
@@ -0,0 +1,86 @@
+# Generated by Django 4.2.15 on 2024-08-23 07:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("dsfr", "0011_rename_notice_dsfrconfig_notice_title"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="dsfrconfig",
+ name="notice_description",
+ field=models.TextField(
+ blank=True,
+ default="",
+ help_text="Can include HTML",
+ verbose_name="Notice description",
+ ),
+ ),
+ migrations.AddField(
+ model_name="dsfrconfig",
+ name="notice_is_collapsible",
+ field=models.BooleanField(default=False, verbose_name="Collapsible?"),
+ ),
+ migrations.AddField(
+ model_name="dsfrconfig",
+ name="notice_link",
+ field=models.URLField(
+ blank=True,
+ default="",
+ help_text="Standardized consultation link at the end of the notice.",
+ verbose_name="Notice link",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="dsfrconfig",
+ name="notice_title",
+ field=models.CharField(
+ blank=True,
+ default="",
+ help_text="Can include HTML",
+ max_length=250,
+ verbose_name="Notice title",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="dsfrconfig",
+ name="notice_type",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ (
+ "Generic notices",
+ [
+ ("info", "Information"),
+ ("warning", "Warning"),
+ ("alert", "Alert"),
+ ],
+ ),
+ (
+ "Weather alert notices",
+ [
+ ("weather-orange", "Orange weather alert"),
+ ("weather-red", "Red weather alert"),
+ ("weather-purple", "Purple weather alert"),
+ ],
+ ),
+ (
+ "Alert notices",
+ [
+ ("attack", "Attack alert"),
+ ("witness", "Call for witnesses"),
+ ("cyberattack", "Cyberattack"),
+ ],
+ ),
+ ],
+ default="info",
+ help_text='Use is strictly regulated, see documentation.',
+ max_length=20,
+ verbose_name="Notice type",
+ ),
+ ),
+ ]
diff --git a/dsfr/models.py b/dsfr/models.py
index 87fad2388..4399a6f4c 100644
--- a/dsfr/models.py
+++ b/dsfr/models.py
@@ -5,7 +5,7 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
-from dsfr.constants import DJANGO_DSFR_LANGUAGES
+from dsfr.constants import DJANGO_DSFR_LANGUAGES, NOTICE_TYPE_CHOICES
def validate_image_extension(value):
@@ -38,16 +38,52 @@ class DsfrConfig(models.Model):
site_tagline = models.CharField(
_("Site tagline"), max_length=200, default=_("Site tagline"), blank=True
)
- notice = models.TextField(
- _("Important notice"),
+
+ # Notice
+ notice_title = models.CharField(
+ _("Notice title"),
+ max_length=250,
+ default="",
+ blank=True,
+ help_text=_("Can include HTML"),
+ )
+
+ notice_description = models.TextField(
+ _("Notice description"),
default="",
blank=True,
+ help_text=_("Can include HTML"),
+ )
+
+ notice_type = models.CharField(
+ _("Notice type"),
+ choices=NOTICE_TYPE_CHOICES,
+ default="info",
+ blank=True,
+ max_length=20,
help_text=_(
- "The important notice banner should only be used for essential and temporary information. \
- (Excessive or continuous use risks “drowning” the message.)"
+ 'Use is strictly regulated, see \
+ documentation.'
),
)
+ notice_link = models.URLField(
+ _("Notice link"),
+ default="",
+ blank=True,
+ help_text=_("Standardized consultation link at the end of the notice."),
+ )
+
+ notice_icon_class = models.CharField(
+ _("Notice icon class"),
+ max_length=200,
+ default="",
+ blank=True,
+ help_text=_("For weather alerts only"),
+ )
+
+ notice_is_collapsible = models.BooleanField(_("Collapsible?"), default=False) # type: ignore
+
# Header
header_brand = models.CharField(
_("Institution (header)"),
diff --git a/dsfr/templates/dsfr/base.html b/dsfr/templates/dsfr/base.html
index 84e59704e..452924ad0 100644
--- a/dsfr/templates/dsfr/base.html
+++ b/dsfr/templates/dsfr/base.html
@@ -38,8 +38,8 @@
{% dsfr_theme_modale %}
- {% if SITE_CONFIG.notice %}
- {% dsfr_notice title=SITE_CONFIG.notice %}
+ {% if SITE_CONFIG.notice_title or SITE_CONFIG.notice_description %}
+ {% dsfr_notice title=SITE_CONFIG.notice_title description=SITE_CONFIG.notice_description link=SITE_CONFIG.notice_link type=SITE_CONFIG.notice_type icon=SITE_CONFIG.notice_icon_class is_collapsible=SITE_CONFIG.notice_is_collapsible %}
{% endif %}