From ab5429a66410966cebbce2616e216abf02f50b32 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:07:49 -0300 Subject: Allowed creation of site-wide banners via the admin interface. Fixes #1550. A banner requires a title, and optionally HTML body and CTA label/URL. A banner can be active or inactive, and only one can be active at a time. Banners can be previewed from the admin via the "View on site" options on a Banner's detail page. This initial implementation is intentionally minimal to provide a robust but flexible MVP, with the goal to gather feedback for future iterations. Future improvements could include: - Active since and until dates. - Flexible CTA URL handling, such as URL names or local URLs. Thanks to Sarahs for the reviews. --- aggregator/tests.py | 4 +- djangoproject/scss/_style.scss | 6 + djangoproject/templates/base.html | 4 +- djangoproject/templates/foundation/banner.html | 9 ++ .../templates/foundation/banner_preview.html | 9 ++ djangoproject/templates/fundraising/index.html | 2 + djangoproject/urls/www.py | 7 +- foundation/admin.py | 29 +++++ foundation/migrations/0008_banner.py | 92 ++++++++++++++ foundation/models.py | 82 +++++++++++- foundation/templatetags/banner.py | 10 ++ foundation/tests.py | 138 ++++++++++++++++++++- foundation/views.py | 8 ++ 13 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 djangoproject/templates/foundation/banner.html create mode 100644 djangoproject/templates/foundation/banner_preview.html create mode 100644 foundation/migrations/0008_banner.py create mode 100644 foundation/templatetags/banner.py diff --git a/aggregator/tests.py b/aggregator/tests.py index afe67b0c..f5716c58 100644 --- a/aggregator/tests.py +++ b/aggregator/tests.py @@ -92,7 +92,7 @@ class AggregatorTests(TestCase): def test_community_index_number_of_queries(self): """Intended to prevent an n+1 issue on the community index view""" url = reverse("community-index") - with self.assertNumQueries(8): + with self.assertNumQueries(9): self.client.get(url) def test_empty_feed_type_not_rendered(self): @@ -119,7 +119,7 @@ class AggregatorTests(TestCase): url = reverse( "community-feed-list", kwargs={"feed_type_slug": self.feed_type.slug} ) - with self.assertNumQueries(8): + with self.assertNumQueries(9): self.client.get(url) def test_management_command_sends_no_email_with_no_pending_feeds(self): diff --git a/djangoproject/scss/_style.scss b/djangoproject/scss/_style.scss index 6a8565d9..be2c29de 100644 --- a/djangoproject/scss/_style.scss +++ b/djangoproject/scss/_style.scss @@ -711,6 +711,12 @@ header { } } + .banner-preview-notice { + background-color: $warning-bg; + text-align: center; + margin: 0; + padding: 0; + } a, h1 a { color: $black; diff --git a/djangoproject/templates/base.html b/djangoproject/templates/base.html index ec6d3399..d6860ff8 100644 --- a/djangoproject/templates/base.html +++ b/djangoproject/templates/base.html @@ -1,4 +1,4 @@ -{% load static %} +{% load static banner %}
@@ -59,7 +59,7 @@