From 3952fc2411e865590601581d625afbd1d942dd93 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Fri, 11 Aug 2023 12:41:06 +0200 Subject: [PATCH 01/24] [#1661] Changed footer to use static placeholders --- src/open_inwoner/cms/footer/__init__.py | 0 src/open_inwoner/cms/footer/cms_plugins.py | 23 ++++++ .../templates/components/Footer/Footer.html | 51 ------------ .../templates/components/Footer/Social.html | 10 --- .../components/templatetags/footer_tags.py | 31 ------- src/open_inwoner/conf/base.py | 22 +++++ src/open_inwoner/configurations/admin.py | 13 --- src/open_inwoner/configurations/models.py | 4 + .../scss/components/Footer/Footer.scss | 80 ++++++++----------- .../cms/footer/footer_pages_plugin.html | 15 ++++ src/open_inwoner/templates/master.html | 25 +++++- src/open_inwoner/utils/context_processors.py | 15 ++-- 12 files changed, 125 insertions(+), 164 deletions(-) create mode 100644 src/open_inwoner/cms/footer/__init__.py create mode 100644 src/open_inwoner/cms/footer/cms_plugins.py delete mode 100644 src/open_inwoner/components/templates/components/Footer/Footer.html delete mode 100644 src/open_inwoner/components/templates/components/Footer/Social.html delete mode 100644 src/open_inwoner/components/templatetags/footer_tags.py create mode 100644 src/open_inwoner/templates/cms/footer/footer_pages_plugin.html diff --git a/src/open_inwoner/cms/footer/__init__.py b/src/open_inwoner/cms/footer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/cms/footer/cms_plugins.py b/src/open_inwoner/cms/footer/cms_plugins.py new file mode 100644 index 0000000000..c8f67a7ec9 --- /dev/null +++ b/src/open_inwoner/cms/footer/cms_plugins.py @@ -0,0 +1,23 @@ +from django.utils.translation import gettext_lazy as _ + +from cms.plugin_base import CMSPluginBase +from cms.plugin_pool import plugin_pool + +from open_inwoner.configurations.models import SiteConfiguration +from open_inwoner.openklant.models import OpenKlantConfig + + +@plugin_pool.register_plugin +class FooterPagesPlugin(CMSPluginBase): + name = _("Pages List") + render_template = "cms/footer/footer_pages_plugin.html" + + # cache = False + + def render(self, context, instance, placeholder): + # TODO move options to plugin model + config = SiteConfiguration.get_solo() + ok_config = OpenKlantConfig.get_solo() + context["flatpages"] = config.get_ordered_flatpages + context["has_form_configuration"] = ok_config.has_form_configuration() + return context diff --git a/src/open_inwoner/components/templates/components/Footer/Footer.html b/src/open_inwoner/components/templates/components/Footer/Footer.html deleted file mode 100644 index 947e122f1f..0000000000 --- a/src/open_inwoner/components/templates/components/Footer/Footer.html +++ /dev/null @@ -1,51 +0,0 @@ -{% load i18n link_tags button_tags logo_tags footer_tags solo_tags thumbnail %} - -{% get_solo "configurations.SiteConfiguration" as config %} -{% get_solo "openklant.OpenKlantConfig" as ok_config %} - - diff --git a/src/open_inwoner/components/templates/components/Footer/Social.html b/src/open_inwoner/components/templates/components/Footer/Social.html deleted file mode 100644 index 6ae57fd4e5..0000000000 --- a/src/open_inwoner/components/templates/components/Footer/Social.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n link_tags %} - - diff --git a/src/open_inwoner/components/templatetags/footer_tags.py b/src/open_inwoner/components/templatetags/footer_tags.py deleted file mode 100644 index c81ae297fd..0000000000 --- a/src/open_inwoner/components/templatetags/footer_tags.py +++ /dev/null @@ -1,31 +0,0 @@ -from django import template - -register = template.Library() - - -@register.inclusion_tag("components/Footer/Footer.html") -def footer(footer_texts, **kwargs): - """ - Generating the entire footer. - - Usage: - {% footer footer_texts=settings.footer_texts %} - - Variables: - + footer_texts: dict | the dictionary with the footer texts. - """ - return {**kwargs, "footer_texts": footer_texts} - - -@register.inclusion_tag("components/Footer/Social.html") -def social(**kwargs): - """ - Displaying the social links. - - Usage: - {% social %} - - Variables: - - primary: bool | If the primary styling should be used or not. - """ - return {**kwargs} diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 4d1947b9cf..b03178747b 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -214,6 +214,7 @@ "open_inwoner.cms.collaborate", "open_inwoner.cms.banner", "open_inwoner.cms.extensions", + "open_inwoner.cms.footer", ] MIDDLEWARE = [ @@ -549,6 +550,27 @@ "banner_image": {"plugins": ["BannerImagePlugin"], "name": _("Banner Image")}, "banner_text": {"plugins": ["BannerTextPlugin"], "name": _("Banner Text")}, "login_banner": {"plugins": ["BannerImagePlugin"], "name": _("Login Banner")}, + "footer_left": { + "name": _("Footer, Left"), + "plugins": ["TextPlugin", "LinkPlugin", "FooterPagesPlugin"], + "child_classes": { + "TextPlugin": ["LinkPlugin"], + }, + }, + "footer_center": { + "name": _("Footer, Center"), + "plugins": ["TextPlugin", "LinkPlugin", "FooterPagesPlugin"], + "child_classes": { + "TextPlugin": ["LinkPlugin"], + }, + }, + "footer_right": { + "name": _("Footer, Right"), + "plugins": ["TextPlugin", "LinkPlugin", "FooterPagesPlugin"], + "child_classes": { + "TextPlugin": ["LinkPlugin"], + }, + }, } CMS_TOOLBAR_ANONYMOUS_ON = False diff --git a/src/open_inwoner/configurations/admin.py b/src/open_inwoner/configurations/admin.py index edf4886033..7a2e794693 100644 --- a/src/open_inwoner/configurations/admin.py +++ b/src/open_inwoner/configurations/admin.py @@ -143,19 +143,6 @@ class SiteConfigurarionAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin): ), }, ), - ( - _("Footer addresses"), - { - "fields": ( - "footer_visiting_title", - "footer_visiting_intro", - "footer_visiting_phonenumber", - "footer_visiting_map", - "footer_mailing_title", - "footer_mailing_intro", - ) - }, - ), (_("Emails"), {"fields": ("email_new_message",)}), ( _("Openid Connect"), diff --git a/src/open_inwoner/configurations/models.py b/src/open_inwoner/configurations/models.py index 7b287074e9..81e5d346a8 100644 --- a/src/open_inwoner/configurations/models.py +++ b/src/open_inwoner/configurations/models.py @@ -203,6 +203,8 @@ class SiteConfiguration(SingletonModel): verbose_name=_("Edit goal message"), help_text=_("The message when a user edits a goal."), ) + + # TODO remove these fields footer_visiting_title = models.CharField( max_length=255, default="", @@ -244,6 +246,8 @@ class SiteConfiguration(SingletonModel): blank=True, help_text=_("Mailing intro text on the footer section."), ) + # TODO ^^^^ remove above fields ^^^^ + footer_logo = FilerImageField( verbose_name=_("Footer logo"), null=True, diff --git a/src/open_inwoner/scss/components/Footer/Footer.scss b/src/open_inwoner/scss/components/Footer/Footer.scss index 490ae34258..35a82828c1 100644 --- a/src/open_inwoner/scss/components/Footer/Footer.scss +++ b/src/open_inwoner/scss/components/Footer/Footer.scss @@ -1,83 +1,67 @@ /// /// Footer. /// - .footer { $hm: max(calc((100vw - var(--container-width)) / 2), var(--spacing-large)); $vm: var(--row-height); background-color: var(--color-gray-lightest); box-sizing: border-box; + display: grid; gap: var(--spacing-medium); + grid-template-areas: - 'lg-tx lg-tx lg-tx lg-tx' - 'lg lg lg lg' - 'vstr vstr vstr vstr' - 'ml ml ml ml' - 'scl scl scl scl' - 'nws nws nws nws' - 'lnks lnks lnks lnks'; - grid-template-columns: repeat(4, 1fr); + 'head' + 'left' + 'center' + 'right'; + grid-template-columns: 1fr; padding: $vm $hm; @media (min-width: 768px) { gap: var(--gutter-width); grid-template-areas: - 'lg-tx lg-tx lg-tx lg-tx . . . . . . . . ' - 'lg lg lg lg lg lg lg lg lg lg lg lg' - 'vstr vstr vstr vstr scl scl scl scl scl lnks lnks lnks' - 'ml ml ml ml nws nws nws nws nws lnks lnks lnks'; - grid-template-columns: repeat(12, 1fr); + 'head head head' + 'left center right'; + grid-template-columns: repeat(3, 1fr); } - /// Layout. + &__header { + grid-area: head; + } + &__left { + grid-area: left; + } + &__center { + grid-area: center; + } + &__right { + grid-area: right; + } + &__left, + &__center, + &__right { + :first-child { + padding-top: 0; + } + } &__logo { - display: flex; - grid-area: lg; - img { width: auto; max-height: 60px; } } - &__logo-text { - grid-area: lg-tx; } - &__visitor { - grid-area: vstr; - - &-phonenumber a { - display: block; - color: var(--color-secondary); - text-decoration: none; - } - } - - &__mail { - grid-area: ml; - } - - .social { - grid-area: scl; - } - - &__newsletter { - grid-area: nws; - } - - &__links { - grid-area: lnks; - } - - /// List. - &__list { list-style: none; margin: 0; padding: 0; + :first-child { + margin-top: 0; + } } &__list-item { diff --git a/src/open_inwoner/templates/cms/footer/footer_pages_plugin.html b/src/open_inwoner/templates/cms/footer/footer_pages_plugin.html new file mode 100644 index 0000000000..069a6be6a6 --- /dev/null +++ b/src/open_inwoner/templates/cms/footer/footer_pages_plugin.html @@ -0,0 +1,15 @@ +{% load solo_tags link_tags %} + diff --git a/src/open_inwoner/templates/master.html b/src/open_inwoner/templates/master.html index 511a2acd19..c7caf80e81 100644 --- a/src/open_inwoner/templates/master.html +++ b/src/open_inwoner/templates/master.html @@ -1,4 +1,4 @@ -{% load static i18n header_tags card_tags footer_tags button_tags link_tags notification_tags anchor_menu_tags view_breadcrumbs utils session_tags django_htmx cms_tags menu_tags sekizai_tags %} +{% load static i18n header_tags card_tags button_tags link_tags notification_tags anchor_menu_tags logo_tags view_breadcrumbs utils session_tags django_htmx cms_tags menu_tags sekizai_tags %} @@ -80,7 +80,28 @@ {% endblock main_outer %} - {% footer footer_texts=configurable_text.footer %} + {% if footer %} + + {% endif %} + {% session_timeout %} diff --git a/src/open_inwoner/utils/context_processors.py b/src/open_inwoner/utils/context_processors.py index 51869b9d36..1b91864fac 100644 --- a/src/open_inwoner/utils/context_processors.py +++ b/src/open_inwoner/utils/context_processors.py @@ -52,15 +52,12 @@ def settings(request): "select_questionnaire_title": config.select_questionnaire_title, "select_questionnaire_intro": config.select_questionnaire_intro, }, - "footer": { - "footer_visiting_title": config.footer_visiting_title, - "footer_visiting_intro": config.footer_visiting_intro, - "footer_visiting_phonenumber": config.footer_visiting_phonenumber, - "footer_visiting_map": config.footer_visiting_map, - "footer_mailing_title": config.footer_mailing_title, - "footer_mailing_intro": config.footer_mailing_intro, - "flatpages": config.get_ordered_flatpages, - }, + }, + "footer": { + "logo": config.footer_logo, + "logo_alt": config.name, + "logo_url": config.footer_logo_url, + "logo_title": config.footer_logo_title, }, "hero_image_login": ( config.hero_image_login.file.url if config.hero_image_login else "" From 3557f47824b66613df45146f50c0cb5acaac946c Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 21 Aug 2023 09:22:16 +0200 Subject: [PATCH 02/24] [#1661] Allow arrow-style links --- src/open_inwoner/conf/base.py | 4 ++++ src/open_inwoner/templates/djangocms_link/arrow/link.html | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src/open_inwoner/templates/djangocms_link/arrow/link.html diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index b03178747b..059bce363c 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -575,6 +575,10 @@ CMS_TOOLBAR_ANONYMOUS_ON = False +DJANGOCMS_LINK_TEMPLATES = [ + ("arrow", _("Arrow")), +] + # # Django-Admin-Index # diff --git a/src/open_inwoner/templates/djangocms_link/arrow/link.html b/src/open_inwoner/templates/djangocms_link/arrow/link.html new file mode 100644 index 0000000000..3a3d5ec9b8 --- /dev/null +++ b/src/open_inwoner/templates/djangocms_link/arrow/link.html @@ -0,0 +1,4 @@ +{% load cms_tags button_tags icon_tags i18n %}{% spaceless %} +{# copied and modified from 'default' #} +{# this needs to be in one line for rendering purpose #} +{% endspaceless %}{% icon icon="arrow_forward" %}{% for plugin in instance.child_plugin_instances %}{% render_plugin plugin %}{% empty %}{{ instance.name }}{% endfor %} From 3682d827e28e8d3547a5494975c175c07282d9d2 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 21 Aug 2023 11:02:06 +0200 Subject: [PATCH 03/24] [#1661] Moved contact form link display test to plugin --- src/open_inwoner/cms/footer/tests/__init__.py | 0 .../cms/footer/tests/test_plugin.py | 47 +++++++++++++++++++ .../openklant/tests/test_contactform.py | 34 -------------- 3 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 src/open_inwoner/cms/footer/tests/__init__.py create mode 100644 src/open_inwoner/cms/footer/tests/test_plugin.py diff --git a/src/open_inwoner/cms/footer/tests/__init__.py b/src/open_inwoner/cms/footer/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/cms/footer/tests/test_plugin.py b/src/open_inwoner/cms/footer/tests/test_plugin.py new file mode 100644 index 0000000000..0c74d3cf24 --- /dev/null +++ b/src/open_inwoner/cms/footer/tests/test_plugin.py @@ -0,0 +1,47 @@ +from django.test import TestCase +from django.utils.translation import gettext as _ + +from open_inwoner.cms.footer.cms_plugins import FooterPagesPlugin +from open_inwoner.cms.tests import cms_tools +from open_inwoner.openklant.models import OpenKlantConfig +from open_inwoner.openklant.tests.factories import ContactFormSubjectFactory +from open_inwoner.utils.test import ClearCachesMixin + + +class ContactFormTestCase(ClearCachesMixin, TestCase): + def setUp(self): + super().setUp() + # clear config + config = OpenKlantConfig.get_solo() + config.klanten_service = None + config.contactmomenten_service = None + config.register_email = "" + config.register_contact_moment = False + config.register_bronorganisatie_rsin = "" + config.register_type = "" + config.register_employee_id = "" + config.save() + + def test_no_form_link_shown_in_footer_if_not_has_configuration(self): + # set nothing + config = OpenKlantConfig.get_solo() + self.assertFalse(config.has_form_configuration()) + + html, context = cms_tools.render_plugin(FooterPagesPlugin) + + self.assertNotIn(_("Contact formulier"), html) + + def test_form_link_is_shown_in_footer_when_has_configuration(self): + ok_config = OpenKlantConfig.get_solo() + self.assertFalse(ok_config.has_form_configuration()) + + ContactFormSubjectFactory(config=ok_config) + + ok_config.register_email = "example@example.com" + ok_config.save() + + self.assertTrue(ok_config.has_form_configuration()) + + html, context = cms_tools.render_plugin(FooterPagesPlugin) + + self.assertIn(_("Contact formulier"), html) diff --git a/src/open_inwoner/openklant/tests/test_contactform.py b/src/open_inwoner/openklant/tests/test_contactform.py index 1d54178131..e37ea34c03 100644 --- a/src/open_inwoner/openklant/tests/test_contactform.py +++ b/src/open_inwoner/openklant/tests/test_contactform.py @@ -2,7 +2,6 @@ from django.contrib import messages from django.core import mail -from django.test import override_settings from django.urls import reverse from django.utils.translation import gettext as _ @@ -10,7 +9,6 @@ from django_webtest import WebTest from open_inwoner.accounts.tests.factories import UserFactory -from open_inwoner.cms.tests import cms_tools from open_inwoner.openklant.models import OpenKlantConfig from open_inwoner.openklant.tests.data import MockAPICreateData from open_inwoner.openklant.tests.factories import ContactFormSubjectFactory @@ -79,38 +77,6 @@ def test_no_form_shown_if_not_has_configuration(self, m): self.assertContains(response, _("Contact formulier niet geconfigureerd.")) self.assertEqual(0, len(response.pyquery("#contactmoment-form"))) - @override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") - def test_no_form_link_shown_in_footer_if_not_has_configuration(self, m): - # set nothing - config = OpenKlantConfig.get_solo() - self.assertFalse(config.has_form_configuration()) - - cms_tools.create_homepage() - - response = self.app.get(reverse("pages-root")) - links = response.pyquery(".footer__list-item") - - self.assertEqual(len(links), 0) - - @override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") - def test_form_link_is_shown_in_footer_when_has_configuration(self, m): - ok_config = OpenKlantConfig.get_solo() - self.assertFalse(ok_config.has_form_configuration()) - - ContactFormSubjectFactory(config=ok_config) - - ok_config.register_email = "example@example.com" - ok_config.save() - - self.assertTrue(ok_config.has_form_configuration()) - - cms_tools.create_homepage() - - response = self.app.get(reverse("pages-root")) - links = response.pyquery(".footer__list-item") - - self.assertIn(_("Contact formulier"), links[0].text_content()) - def test_anon_form_requires_either_email_or_phonenumber(self, m): config = OpenKlantConfig.get_solo() config.register_email = "example@example.com" From 9e661bfbed436c0b40ee5538e35454395fac5221 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 21 Aug 2023 15:25:06 +0200 Subject: [PATCH 04/24] [#1661] Removed footer_* address fields from SiteConfiguration model --- .../migrations/0049_auto_20230821_1523.py | 37 ++++++++++++++++ src/open_inwoner/configurations/models.py | 44 ------------------- 2 files changed, 37 insertions(+), 44 deletions(-) create mode 100644 src/open_inwoner/configurations/migrations/0049_auto_20230821_1523.py diff --git a/src/open_inwoner/configurations/migrations/0049_auto_20230821_1523.py b/src/open_inwoner/configurations/migrations/0049_auto_20230821_1523.py new file mode 100644 index 0000000000..883405c32d --- /dev/null +++ b/src/open_inwoner/configurations/migrations/0049_auto_20230821_1523.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.20 on 2023-08-21 13:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("configurations", "0048_auto_20230816_1255"), + ] + + operations = [ + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_mailing_intro", + ), + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_mailing_title", + ), + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_visiting_intro", + ), + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_visiting_map", + ), + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_visiting_phonenumber", + ), + migrations.RemoveField( + model_name="siteconfiguration", + name="footer_visiting_title", + ), + ] diff --git a/src/open_inwoner/configurations/models.py b/src/open_inwoner/configurations/models.py index 81e5d346a8..98ca9e0c71 100644 --- a/src/open_inwoner/configurations/models.py +++ b/src/open_inwoner/configurations/models.py @@ -204,50 +204,6 @@ class SiteConfiguration(SingletonModel): help_text=_("The message when a user edits a goal."), ) - # TODO remove these fields - footer_visiting_title = models.CharField( - max_length=255, - default="", - blank=True, - verbose_name=_("Footer visiting title"), - help_text=_("Visiting title on the footer section."), - ) - footer_visiting_intro = models.TextField( - verbose_name=_("Visiting details"), - default="", - blank=True, - help_text=_("Visiting intro text on the footer section."), - ) - footer_visiting_phonenumber = models.CharField( - max_length=15, - default="", - blank=True, - validators=[DutchPhoneNumberValidator()], - verbose_name=_("Footer visiting phonenumber"), - help_text=_("Visiting phonenumber on the footer section."), - ) - footer_visiting_map = models.CharField( - max_length=255, - verbose_name=_("Footer visiting map"), - default="", - blank=True, - help_text=_("Visiting address in google maps on the footer section."), - ) - footer_mailing_title = models.CharField( - max_length=255, - default="", - blank=True, - verbose_name=_("Footer mailing title"), - help_text=_("Mailing title on the footer section."), - ) - footer_mailing_intro = models.TextField( - verbose_name=_("Mailing details"), - default="", - blank=True, - help_text=_("Mailing intro text on the footer section."), - ) - # TODO ^^^^ remove above fields ^^^^ - footer_logo = FilerImageField( verbose_name=_("Footer logo"), null=True, From f486781e3f669639e85843368ef4af71e8bdb03a Mon Sep 17 00:00:00 2001 From: Jiro Ghianni Date: Mon, 28 Aug 2023 10:35:32 +0200 Subject: [PATCH 05/24] :lipstick: [#1688] Changed primary color into secondary for text-links --- .../templates/components/Header/Header.html | 2 +- .../CookieConsent/CookieBanner.scss | 19 +++++++- .../components/Product/product-detail.scss | 43 +++++++++++++++++-- .../Profile/_personal-information.scss | 12 ++++++ src/open_inwoner/scss/views/App.scss | 5 +++ .../templates/pages/cases/list_inner.html | 2 +- .../pages/cases/submissions_inner.html | 2 +- .../pages/cases/submissions_outer.html | 7 +-- .../templates/pages/contactmoment/list.html | 2 +- .../templates/pages/product/detail.html | 8 ++-- .../templates/pages/profile/me.html | 23 ++++++---- 11 files changed, 99 insertions(+), 26 deletions(-) diff --git a/src/open_inwoner/components/templates/components/Header/Header.html b/src/open_inwoner/components/templates/components/Header/Header.html index 26e481703c..81fb4efee9 100644 --- a/src/open_inwoner/components/templates/components/Header/Header.html +++ b/src/open_inwoner/components/templates/components/Header/Header.html @@ -91,7 +91,7 @@
  • {% trans "Logout" as logout %} - {% link text=logout href=request.user.get_logout_url icon="arrow_forward" icon_position="before" primary=True %} + {% link text=logout href=request.user.get_logout_url icon="arrow_forward" icon_position="before" secondary=True %}
{% elif config.login_show %} diff --git a/src/open_inwoner/scss/components/CookieConsent/CookieBanner.scss b/src/open_inwoner/scss/components/CookieConsent/CookieBanner.scss index 3d8b92d53b..c2a5fe5bfe 100644 --- a/src/open_inwoner/scss/components/CookieConsent/CookieBanner.scss +++ b/src/open_inwoner/scss/components/CookieConsent/CookieBanner.scss @@ -86,7 +86,22 @@ justify-content: center; } - .link--cookie { - font-size: inherit; + .link.link--cookie.link--secondary { + &:hover { + text-decoration: none; + } + + .link__text { + font-size: large; + text-decoration: underline; + + &:hover { + text-decoration: none; + } + + @media (min-width: 500px) { + font-size: x-large; + } + } } } diff --git a/src/open_inwoner/scss/components/Product/product-detail.scss b/src/open_inwoner/scss/components/Product/product-detail.scss index 7c6fa62283..7a562ab1af 100644 --- a/src/open_inwoner/scss/components/Product/product-detail.scss +++ b/src/open_inwoner/scss/components/Product/product-detail.scss @@ -1,8 +1,43 @@ .product-info > .aside, -.product-info > div { - margin: var(--spacing-extra-large) 0; +.product-info { + & > div { + margin: var(--spacing-extra-large) 0; - @media (min-width: 768px) { - margin: var(--spacing-giant) 0; + @media (min-width: 768px) { + margin: var(--spacing-giant) 0; + } + } + + .contact-block { + display: block; + border-radius: var(--border-radius); + box-sizing: border-box; + margin: var(--spacing-medium) 0; + padding: var(--spacing-extra-large); + position: relative; + + @media (min-width: 768px) { + margin: var(--spacing-tiny) 0; + } + + .link.link--secondary .link__text { + color: var(--color-secondary-darker); + // increasing accessibility + filter: grayscale(10%); + } + + &::before { + background-color: var(--color-secondary); + border-radius: var(--border-radius); + bottom: 0px; + content: ''; + height: 100%; + left: 0px; + opacity: 0.1; + pointer-events: none; + position: absolute; + right: 0px; + top: 0px; + } } } diff --git a/src/open_inwoner/scss/components/Profile/_personal-information.scss b/src/open_inwoner/scss/components/Profile/_personal-information.scss index 5d7ecdf25a..0fdf9a95c5 100644 --- a/src/open_inwoner/scss/components/Profile/_personal-information.scss +++ b/src/open_inwoner/scss/components/Profile/_personal-information.scss @@ -35,3 +35,15 @@ } } } + +.form { + &__actions { + .view--profile-detail { + color: var(--color-secondary); + + &:hover { + color: var(--color-gray-dark); + } + } + } +} diff --git a/src/open_inwoner/scss/views/App.scss b/src/open_inwoner/scss/views/App.scss index 8e6dd4d36a..4b99d72ca7 100644 --- a/src/open_inwoner/scss/views/App.scss +++ b/src/open_inwoner/scss/views/App.scss @@ -71,6 +71,11 @@ var(--color-secondary-s), calc(var(--color-secondary-l) - 10%) ); + --color-secondary-lighter: hsl( + var(--color-secondary-h), + calc(var(--color-secondary-s) - 30%), + calc(var(--color-secondary-l) + 30%) + ); --color-accent-h: 50; --color-accent-s: 94%; diff --git a/src/open_inwoner/templates/pages/cases/list_inner.html b/src/open_inwoner/templates/pages/cases/list_inner.html index dabb15f3fe..f4dec88dcf 100644 --- a/src/open_inwoner/templates/pages/cases/list_inner.html +++ b/src/open_inwoner/templates/pages/cases/list_inner.html @@ -22,7 +22,7 @@

{{ page_title }} ({{ paginator.count }})

{% endrender_list %} - + {% trans "Bekijk aanvraag" %} {% icon icon="arrow_forward" icon_position="after" primary=True outlined=True %} diff --git a/src/open_inwoner/templates/pages/cases/submissions_inner.html b/src/open_inwoner/templates/pages/cases/submissions_inner.html index 610d190f68..6ca42d5fa7 100644 --- a/src/open_inwoner/templates/pages/cases/submissions_inner.html +++ b/src/open_inwoner/templates/pages/cases/submissions_inner.html @@ -25,7 +25,7 @@

{{ page_title }} ({{ submissions|length }})

{% link href=submission.vervolg_link text=submission.naam blank=True %} {{submission.datum_laatste_wijziging|date:"d-m-Y"}} {{submission.eind_datum_geldigheid|date:"d-m-Y"}} - {% button text=submission.naam hide_text=True href=submission.vervolg_link icon="arrow_forward" icon_outlined=True transparent=True %} + {% button text=submission.naam hide_text=True href=submission.vervolg_link icon="arrow_forward" secondary=True icon_outlined=True transparent=True %} {% empty %} diff --git a/src/open_inwoner/templates/pages/cases/submissions_outer.html b/src/open_inwoner/templates/pages/cases/submissions_outer.html index e191aea78a..5e1598de74 100644 --- a/src/open_inwoner/templates/pages/cases/submissions_outer.html +++ b/src/open_inwoner/templates/pages/cases/submissions_outer.html @@ -9,12 +9,13 @@ {% block content %}

{% trans "Mijn aanvragen" %}

- +
- +
- {% icon icon="autorenew" extra_classes="spinner-icon rotate" %} + {% icon icon="rotate_right" extra_classes="spinner-icon rotate" %} +
{% trans "Gegevens laden..." %}
{% endblock content %} diff --git a/src/open_inwoner/templates/pages/contactmoment/list.html b/src/open_inwoner/templates/pages/contactmoment/list.html index dbfcdcd6c5..dff910f7d1 100644 --- a/src/open_inwoner/templates/pages/contactmoment/list.html +++ b/src/open_inwoner/templates/pages/contactmoment/list.html @@ -28,7 +28,7 @@

{{ page_title }} ({{ contactmomenten|length {% endrender_list %} - + {% trans "Bekijk vraag" %} {% icon icon="arrow_forward" icon_position="after" primary=True outlined=True %} diff --git a/src/open_inwoner/templates/pages/product/detail.html b/src/open_inwoner/templates/pages/product/detail.html index c264aae885..82ccfeef5e 100644 --- a/src/open_inwoner/templates/pages/product/detail.html +++ b/src/open_inwoner/templates/pages/product/detail.html @@ -129,7 +129,7 @@

{% trans "Zie ook" %}

{% render_column span=9 %} {% if object.contacts.exists %} - {% render_notification type='success' icon=False %} +

{% trans 'Contact' %}

{% for contact in object.contacts.all %} @@ -144,7 +144,7 @@

{{ contact.first_name }} {{ contact.last_name }}

{% endrender_column %} {% render_column start=7 span=6 %} - {% link href='tel:'|add:contact.phonenumber text=contact.phonenumber primary=True %} + {% link href='tel:'|add:contact.phonenumber text=contact.phonenumber secondary=True %} {% endrender_column %} {% endif %} @@ -154,12 +154,12 @@

{{ contact.first_name }} {{ contact.last_name }}

{% endrender_column %} {% render_column start=7 span=6 %} - {% link href='mailto:'|add:contact.email text=contact.email primary=True %} + {% link href='mailto:'|add:contact.email text=contact.email secondary=True %} {% endrender_column %} {% endif %} {% endrender_grid %} {% endfor %} - {% endrender_notification %} +
{% endif %} {% endrender_column %} {% endrender_grid %} diff --git a/src/open_inwoner/templates/pages/profile/me.html b/src/open_inwoner/templates/pages/profile/me.html index 0849923770..eef3d3bac7 100644 --- a/src/open_inwoner/templates/pages/profile/me.html +++ b/src/open_inwoner/templates/pages/profile/me.html @@ -64,7 +64,7 @@

{% trans "Persoonlijk overzicht" %}

{% trans "Interessegebieden" %}
{{ category_text }}
-
{% link href="profile:categories" text="Aanpassen" icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="profile:categories" text="Aanpassen" icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} @@ -75,7 +75,7 @@

{% trans "Persoonlijk overzicht" %}

{{ mentor_contacts|join:", " }}
{% url 'profile:contact_list' as mentor_url %} {% if inbox_page_is_published %} -
{% link href=mentor_url|add:"?type=begeleider" text="Stuur een bericht" icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href=mentor_url|add:"?type=begeleider" text="Stuur een bericht" icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} {% else %}
{% trans "U heeft (nog) geen gemeentelijke begeleider." %}
@@ -88,7 +88,7 @@

{% trans "Persoonlijk overzicht" %}

{% trans "Mijn netwerkcontacten" %}
{{ contact_text }}
-
{% link href="profile:contact_list" text=_("Beheer contacten") icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="profile:contact_list" text=_("Beheer contacten") icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} @@ -96,7 +96,7 @@

{% trans "Persoonlijk overzicht" %}

{% trans "Zelfdiagnose" %}
-
{% link href='products:questionnaire_list' text=_('Start zelfdiagnose') icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href='products:questionnaire_list' text=_('Start zelfdiagnose') icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} @@ -104,7 +104,7 @@

{% trans "Persoonlijk overzicht" %}

{% trans "Acties" %}
{{ action_text }}
-
{% link href="profile:action_list" text="Aanpassen" icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="profile:action_list" text="Aanpassen" icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} @@ -112,21 +112,21 @@

{% trans "Persoonlijk overzicht" %}

{% trans "Communicatievoorkeuren" %}
{{ request.user.get_active_notifications }}
-
{% link href="profile:notifications" text="Aanpassen" icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="profile:notifications" text="Aanpassen" icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} {% if view.config.questions %}
{% trans "Mijn vragen" %}
-
{% link href="cases:contactmoment_list" text=_('Bekijken') icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="cases:contactmoment_list" text=_('Bekijken') icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} {% if view.config.ssd %}
{% trans "Mijn uitkeringen" %}
Jaaropgaven, Maandspecificaties
-
{% link href="ssd:monthly_benefits_index" text=_("Bekijken") icon="arrow_forward" icon_position="after" primary=True %}
+
{% link href="ssd:monthly_benefits_index" text=_("Bekijken") icon="arrow_forward" icon_position="after" secondary=True %}
{% endif %} {% comment %}
@@ -138,7 +138,12 @@

{% trans "Persoonlijk overzicht" %}

{% render_form form=form method="POST" id="delete-form" extra_classes="confirm" spaceless=True data_confirm_title=_("Weet u zeker dat u uw account wilt verwijderen?") data_confirm_text=_("Hiermee worden alleen uw persoonlijke voorkeuren verwijderd. U krijgt dan bijvoorbeeld geen e-mail meer van ons over wijzigingen van uw lopende zaken. Uw persoonsgegevens en uw lopende zaken zelf worden hiermee niet verwijderd.") data_confirm_cancel=_("Nee") data_confirm_default=_("Ja") %} {% csrf_token %} - {% form_actions primary_text=_("Profiel verwijderen") primary_icon="close" transparent=True %} +
+ +
{% endrender_form %} {% if files %} From 01c457e13e6730bdfecec2abd7c844fd301a5621 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Mon, 31 Jul 2023 12:37:14 +0200 Subject: [PATCH 06/24] [#1624] escape html of product content field --- src/open_inwoner/pdc/models/product.py | 5 +++++ src/open_inwoner/pdc/tests/test_product.py | 14 ++++++++++++++ src/open_inwoner/utils/ckeditor.py | 10 ++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/open_inwoner/pdc/models/product.py b/src/open_inwoner/pdc/models/product.py index f1d72acc81..529a86c11e 100644 --- a/src/open_inwoner/pdc/models/product.py +++ b/src/open_inwoner/pdc/models/product.py @@ -1,3 +1,4 @@ +import html import json from typing import Union from uuid import uuid4 @@ -208,6 +209,10 @@ def get_absolute_url(self, category=None): def has_cta_tag(self): return "\[CTABUTTON\]" in self.content + def save(self, *args, **kwargs): + self.content = html.escape(self.content).replace("\\&", "&") + super().save() + class ProductFile(models.Model): product = models.ForeignKey( diff --git a/src/open_inwoner/pdc/tests/test_product.py b/src/open_inwoner/pdc/tests/test_product.py index aaf30ac19d..1cea58cf4c 100644 --- a/src/open_inwoner/pdc/tests/test_product.py +++ b/src/open_inwoner/pdc/tests/test_product.py @@ -277,6 +277,20 @@ def test_sidemenu_button_is_rendered_when_no_cta_inside_product_content(self): self.assertTrue(sidemenu_cta_button) self.assertIn(product.link, sidemenu_cta_button[0].values()) + def test_content_html_escape(self): + product = ProductFactory() + + product.content = "\\hello world\\ **test**" + product.save() + + response = self.app.get( + reverse("products:product_detail", kwargs={"slug": product.slug}) + ) + + self.assertNotContains(response, "hello world") + self.assertContains(response, "<b>hello world</b>") + self.assertContains(response, "test") + @override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") class TestProductDetailView(WebTest): diff --git a/src/open_inwoner/utils/ckeditor.py b/src/open_inwoner/utils/ckeditor.py index ab4b24230a..bf600a644f 100644 --- a/src/open_inwoner/utils/ckeditor.py +++ b/src/open_inwoner/utils/ckeditor.py @@ -1,3 +1,5 @@ +import html + from django.utils.text import slugify from django.utils.translation import gettext as _ @@ -26,8 +28,8 @@ def get_rendered_content(content): Takes object's content as an input and returns the rendered one. """ md = markdown.Markdown(extensions=["tables"]) - html = md.convert(content) - soup = BeautifulSoup(html, "html.parser") + html_string = md.convert(html.escape(content)) + soup = BeautifulSoup(html_string, "html.parser") for tag, class_name in CLASS_ADDERS: for element in soup.find_all(tag): @@ -43,8 +45,8 @@ def get_product_rendered_content(product): Takes product's content as an input and returns the rendered one. """ md = markdown.Markdown(extensions=["tables"]) - html = md.convert(product.content) - soup = BeautifulSoup(html, "html.parser") + html_string = md.convert(product.content) + soup = BeautifulSoup(html_string, "html.parser") for tag, class_name in CLASS_ADDERS: for element in soup.find_all(tag): From c4160d8331be527b497b112bc5066ddfea5fabf0 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 28 Aug 2023 15:00:00 +0200 Subject: [PATCH 07/24] [#1624] Fixed ckeditor escape issue --- src/open_inwoner/pdc/models/product.py | 7 ++++++- src/open_inwoner/pdc/tests/test_product.py | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/open_inwoner/pdc/models/product.py b/src/open_inwoner/pdc/models/product.py index 529a86c11e..e84fd768f7 100644 --- a/src/open_inwoner/pdc/models/product.py +++ b/src/open_inwoner/pdc/models/product.py @@ -6,6 +6,7 @@ from django.contrib.postgres.fields import ArrayField from django.db import models from django.urls import reverse +from django.utils.html import strip_tags from django.utils.translation import ugettext_lazy as _ from filer.fields.file import FilerFileField @@ -210,7 +211,11 @@ def has_cta_tag(self): return "\[CTABUTTON\]" in self.content def save(self, *args, **kwargs): - self.content = html.escape(self.content).replace("\\&", "&") + # - remove weird undocumented \\< escape/prefix generated by CKeditor + # \\ -> + # - strip tags to kill html + # - save markdown and render to html later + self.content = strip_tags(self.content.replace("\\<", "<")) super().save() diff --git a/src/open_inwoner/pdc/tests/test_product.py b/src/open_inwoner/pdc/tests/test_product.py index 1cea58cf4c..404ba6f0fa 100644 --- a/src/open_inwoner/pdc/tests/test_product.py +++ b/src/open_inwoner/pdc/tests/test_product.py @@ -280,15 +280,16 @@ def test_sidemenu_button_is_rendered_when_no_cta_inside_product_content(self): def test_content_html_escape(self): product = ProductFactory() - product.content = "\\hello world\\ **test**" + product.content = "hello \\world\\ **test**" product.save() response = self.app.get( reverse("products:product_detail", kwargs={"slug": product.slug}) ) - self.assertNotContains(response, "hello world") - self.assertContains(response, "<b>hello world</b>") + self.assertNotContains(response, "world") + self.assertNotContains(response, "<b>world</b>") + self.assertContains(response, "hello world") self.assertContains(response, "test") From 81939e966c28859e7a2e9c178c29258027a1da52 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 4 Sep 2023 09:50:53 +0200 Subject: [PATCH 08/24] [#1624] Allowed html in CKEditor, remove-slashes at render time --- src/open_inwoner/pdc/models/product.py | 8 -------- src/open_inwoner/pdc/tests/test_product.py | 8 +++++--- src/open_inwoner/utils/ckeditor.py | 14 ++++++++------ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/open_inwoner/pdc/models/product.py b/src/open_inwoner/pdc/models/product.py index e84fd768f7..c18c2ffb5d 100644 --- a/src/open_inwoner/pdc/models/product.py +++ b/src/open_inwoner/pdc/models/product.py @@ -210,14 +210,6 @@ def get_absolute_url(self, category=None): def has_cta_tag(self): return "\[CTABUTTON\]" in self.content - def save(self, *args, **kwargs): - # - remove weird undocumented \\< escape/prefix generated by CKeditor - # \\ -> - # - strip tags to kill html - # - save markdown and render to html later - self.content = strip_tags(self.content.replace("\\<", "<")) - super().save() - class ProductFile(models.Model): product = models.ForeignKey( diff --git a/src/open_inwoner/pdc/tests/test_product.py b/src/open_inwoner/pdc/tests/test_product.py index 404ba6f0fa..c894c69e94 100644 --- a/src/open_inwoner/pdc/tests/test_product.py +++ b/src/open_inwoner/pdc/tests/test_product.py @@ -1,3 +1,5 @@ +from html import escape + from django.test import override_settings from django.urls import reverse @@ -287,9 +289,9 @@ def test_content_html_escape(self): reverse("products:product_detail", kwargs={"slug": product.slug}) ) - self.assertNotContains(response, "world") - self.assertNotContains(response, "<b>world</b>") - self.assertContains(response, "hello world") + self.assertNotContains(response, "hello world") + self.assertNotContains(response, escape("world")) + self.assertContains(response, "hello world") self.assertContains(response, "test") diff --git a/src/open_inwoner/utils/ckeditor.py b/src/open_inwoner/utils/ckeditor.py index bf600a644f..a8a473d90d 100644 --- a/src/open_inwoner/utils/ckeditor.py +++ b/src/open_inwoner/utils/ckeditor.py @@ -1,5 +1,3 @@ -import html - from django.utils.text import slugify from django.utils.translation import gettext as _ @@ -28,8 +26,10 @@ def get_rendered_content(content): Takes object's content as an input and returns the rendered one. """ md = markdown.Markdown(extensions=["tables"]) - html_string = md.convert(html.escape(content)) - soup = BeautifulSoup(html_string, "html.parser") + # remove weird undocumented \\< escape/prefix generated by CKeditor + content = content.replace("\\<", "<") + html = md.convert(content) + soup = BeautifulSoup(html, "html.parser") for tag, class_name in CLASS_ADDERS: for element in soup.find_all(tag): @@ -45,8 +45,10 @@ def get_product_rendered_content(product): Takes product's content as an input and returns the rendered one. """ md = markdown.Markdown(extensions=["tables"]) - html_string = md.convert(product.content) - soup = BeautifulSoup(html_string, "html.parser") + # remove weird undocumented \\< escape/prefix generated by CKeditor + content = product.content.replace("\\<", "<") + html = md.convert(content) + soup = BeautifulSoup(html, "html.parser") for tag, class_name in CLASS_ADDERS: for element in soup.find_all(tag): From 3c335c8053bb3dcee08f409d783ff4733e283be0 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Thu, 24 Aug 2023 15:21:48 +0200 Subject: [PATCH 09/24] [#1427] Dropped status_list template tag --- .../components/status/status_list.html | 13 --------- .../components/templatetags/status_tags.py | 29 ------------------- .../templates/pages/cases/status_inner.html | 20 ++++++++++--- 3 files changed, 16 insertions(+), 46 deletions(-) delete mode 100644 src/open_inwoner/components/templates/components/status/status_list.html delete mode 100644 src/open_inwoner/components/templatetags/status_tags.py diff --git a/src/open_inwoner/components/templates/components/status/status_list.html b/src/open_inwoner/components/templates/components/status/status_list.html deleted file mode 100644 index 726ff1c209..0000000000 --- a/src/open_inwoner/components/templates/components/status/status_list.html +++ /dev/null @@ -1,13 +0,0 @@ -{% load icon_tags i18n %} - - diff --git a/src/open_inwoner/components/templatetags/status_tags.py b/src/open_inwoner/components/templatetags/status_tags.py deleted file mode 100644 index d5cf3d9b11..0000000000 --- a/src/open_inwoner/components/templatetags/status_tags.py +++ /dev/null @@ -1,29 +0,0 @@ -from datetime import datetime -from typing import TypedDict - -from django import template - -register = template.Library() - - -class Status(TypedDict): - icon: str - label: str - date: datetime - - -@register.inclusion_tag("components/status/status_list.html") -def status_list(statuses: list[Status], **kwargs) -> dict: - """ - Shows multiple statuses in an (historic) list. - - Usage: - {% status_list statuses %} - - Variables: - + statuses: list[Status] | List of Status objects. - """ - return { - **kwargs, - "statuses": statuses, - } diff --git a/src/open_inwoner/templates/pages/cases/status_inner.html b/src/open_inwoner/templates/pages/cases/status_inner.html index 8c1b4adec9..546873cf64 100644 --- a/src/open_inwoner/templates/pages/cases/status_inner.html +++ b/src/open_inwoner/templates/pages/cases/status_inner.html @@ -1,4 +1,4 @@ -{% load i18n anchor_menu_tags card_tags dashboard_tags file_tags grid_tags status_tags table_tags solo_tags button_tags icon_tags notification_tags %} +{% load i18n anchor_menu_tags card_tags dashboard_tags file_tags grid_tags table_tags solo_tags button_tags icon_tags notification_tags %} {# Anchor menu-mobile #}
@@ -29,7 +29,19 @@

{{ case.description }}

{# Status history. #} {% if case.statuses %}

{% trans 'Status' %}

- {% status_list case.statuses %} + + + {% endif %} {# Documents. #} @@ -49,7 +61,7 @@

{% trans "Document uploaden" %}

{% endblocktranslate %}

{% endif %} - + {# Upload document form. #}
{% include 'pages/cases/document_form.html' %} @@ -65,7 +77,7 @@

{% trans "Document toevoegen" %}

{% button_row %} {% button href=case.external_upload_url text=_("Document uploaden") title=_("Opens new window") primary=True icon="open_in_new" icon_position="after" %} {% endbutton_row %} - {% endif %} + {% endif %} {# Contact moment form #} {% if case.contact_form_enabled %} From 45b283c16a390b420c78ccce519d9834c5052687 Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Fri, 25 Aug 2023 12:21:46 +0200 Subject: [PATCH 10/24] [#1427] Implemented OpenZaak status translation --- src/open_inwoner/cms/cases/views/mixins.py | 5 +- src/open_inwoner/cms/cases/views/status.py | 22 +++++- src/open_inwoner/openzaak/admin.py | 32 ++++++++- src/open_inwoner/openzaak/managers.py | 8 ++- .../migrations/0021_statustranslation.py | 44 ++++++++++++ src/open_inwoner/openzaak/models.py | 19 +++++ .../openzaak/resources/__init__.py | 0 .../openzaak/resources/import_resource.py | 33 +++++++++ src/open_inwoner/openzaak/tests/factories.py | 9 +++ .../openzaak/tests/test_case_detail.py | 32 ++++++++- src/open_inwoner/openzaak/tests/test_cases.py | 26 ++++++- .../openzaak/tests/test_status_translation.py | 26 +++++++ .../templates/pages/cases/status_inner.html | 4 +- .../templates/pages/contactmoment/detail.html | 2 +- .../utils/tests/test_translate.py | 69 +++++++++++++++++++ src/open_inwoner/utils/translate.py | 38 ++++++++++ 16 files changed, 357 insertions(+), 12 deletions(-) create mode 100644 src/open_inwoner/openzaak/migrations/0021_statustranslation.py create mode 100644 src/open_inwoner/openzaak/resources/__init__.py create mode 100644 src/open_inwoner/openzaak/resources/import_resource.py create mode 100644 src/open_inwoner/openzaak/tests/test_status_translation.py create mode 100644 src/open_inwoner/utils/tests/test_translate.py create mode 100644 src/open_inwoner/utils/translate.py diff --git a/src/open_inwoner/cms/cases/views/mixins.py b/src/open_inwoner/cms/cases/views/mixins.py index 8b6dd10132..3a4a37daa3 100644 --- a/src/open_inwoner/cms/cases/views/mixins.py +++ b/src/open_inwoner/cms/cases/views/mixins.py @@ -19,7 +19,7 @@ fetch_single_case_type, fetch_single_status_type, ) -from open_inwoner.openzaak.models import OpenZaakConfig +from open_inwoner.openzaak.models import OpenZaakConfig, StatusTranslation from open_inwoner.openzaak.utils import format_zaak_identificatie, is_zaak_visible from open_inwoner.utils.mixins import PaginationMixin from open_inwoner.utils.views import LogMixin @@ -141,6 +141,7 @@ def get_cases(self) -> List[Zaak]: def process_cases(self, cases: List[Zaak]) -> List[dict]: # Prepare data for frontend config = OpenZaakConfig.get_solo() + status_translate = StatusTranslation.objects.get_lookup() updated_cases = [] for case in cases: @@ -150,7 +151,7 @@ def process_cases(self, cases: List[Zaak]) -> List[dict]: "start_date": case.startdatum, "end_date": getattr(case, "einddatum", None), "description": case.zaaktype.omschrijving, - "current_status": glom( + "current_status": status_translate.from_glom( case, "status.statustype.omschrijving", default="" ), } diff --git a/src/open_inwoner/cms/cases/views/status.py b/src/open_inwoner/cms/cases/views/status.py index 0be35c65cf..1dfcbfadd4 100644 --- a/src/open_inwoner/cms/cases/views/status.py +++ b/src/open_inwoner/cms/cases/views/status.py @@ -23,7 +23,7 @@ create_klant, fetch_klant_for_bsn, ) -from open_inwoner.openzaak.api_models import Zaak +from open_inwoner.openzaak.api_models import Status, Zaak from open_inwoner.openzaak.cases import ( connect_case_with_document, fetch_case_information_objects, @@ -41,6 +41,7 @@ ) from open_inwoner.openzaak.models import ( OpenZaakConfig, + StatusTranslation, ZaakTypeConfig, ZaakTypeInformatieObjectTypeConfig, ) @@ -49,6 +50,7 @@ get_role_name_display, is_info_object_visible, ) +from open_inwoner.utils.translate import TranslationLookup from open_inwoner.utils.views import CommonPageMixin, LogMixin from ..forms import CaseContactForm, CaseUploadForm @@ -110,6 +112,7 @@ def get_context_data(self, **kwargs): if self.case: self.log_case_access(self.case.identificatie) config = OpenZaakConfig.get_solo() + status_translate = StatusTranslation.objects.get_lookup() documents = self.get_case_document_files(self.case) @@ -147,12 +150,12 @@ def get_context_data(self, **kwargs): self.case, "uiterlijke_einddatum_afdoening", None ), "description": self.case.zaaktype.omschrijving, - "current_status": glom( + "current_status": status_translate.from_glom( self.case, "status.statustype.omschrijving", default=_("No data available"), ), - "statuses": statuses, + "statuses": self.get_statuses_data(statuses, status_translate), "documents": documents, "allowed_file_extensions": sorted(config.allowed_file_extensions), } @@ -221,6 +224,19 @@ def get_initiator_display(self, case: Zaak) -> str: return "" return ", ".join([get_role_name_display(r) for r in roles]) + def get_statuses_data( + self, statuses: List[Status], lookup: TranslationLookup + ) -> List[dict]: + return [ + { + "date": s.datum_status_gezet, + "label": lookup.from_glom( + s, "statustype.omschrijving", default=_("No data available") + ), + } + for s in statuses + ] + def get_case_document_files(self, case: Zaak) -> List[SimpleFile]: case_info_objects = fetch_case_information_objects(case.url) diff --git a/src/open_inwoner/openzaak/admin.py b/src/open_inwoner/openzaak/admin.py index 62a9886797..65e79eec52 100644 --- a/src/open_inwoner/openzaak/admin.py +++ b/src/open_inwoner/openzaak/admin.py @@ -1,19 +1,23 @@ from django.contrib import admin, messages from django.core.exceptions import ValidationError -from django.db.models import BooleanField, Count, Exists, ExpressionWrapper, Q +from django.db.models import BooleanField, Count, ExpressionWrapper, Q from django.forms.models import BaseInlineFormSet from django.utils.translation import gettext_lazy as _, ngettext +from import_export.admin import ImportExportMixin +from import_export.formats import base_formats from solo.admin import SingletonModelAdmin from .models import ( CatalogusConfig, OpenZaakConfig, + StatusTranslation, UserCaseInfoObjectNotification, UserCaseStatusNotification, ZaakTypeConfig, ZaakTypeInformatieObjectTypeConfig, ) +from .resources.import_resource import StatusTranslationImportResource @admin.register(OpenZaakConfig) @@ -324,3 +328,29 @@ class UserCaseInfoObjectNotificationAdmin(admin.ModelAdmin): def has_change_permission(self, request, obj=None): return False + + +@admin.register(StatusTranslation) +class StatusTranslationAdmin(ImportExportMixin, admin.ModelAdmin): + fields = [ + "status", + "translation", + ] + search_fields = [ + "status", + "translation", + ] + list_display = [ + "id", + "status", + "translation", + ] + list_editable = [ + "status", + "translation", + ] + ordering = ("status",) + + # import-export + resource_class = StatusTranslationImportResource + formats = [base_formats.XLSX, base_formats.CSV] diff --git a/src/open_inwoner/openzaak/managers.py b/src/open_inwoner/openzaak/managers.py index c469e7c5f8..edef3098ca 100644 --- a/src/open_inwoner/openzaak/managers.py +++ b/src/open_inwoner/openzaak/managers.py @@ -6,7 +6,8 @@ from django.utils import timezone from open_inwoner.accounts.models import User -from open_inwoner.openzaak.api_models import Zaak, ZaakType +from open_inwoner.openzaak.api_models import ZaakType +from open_inwoner.utils.translate import TranslationLookup if TYPE_CHECKING: from open_inwoner.openzaak.models import ( @@ -162,3 +163,8 @@ def filter_questions_enabled_for_case_type(self, case_type: ZaakType): return self.none() return self.filter_case_type(case_type).filter(questions_enabled=True) + + +class StatusTranslationQuerySet(models.QuerySet): + def get_lookup(self): + return TranslationLookup(self.values_list("status", "translation")) diff --git a/src/open_inwoner/openzaak/migrations/0021_statustranslation.py b/src/open_inwoner/openzaak/migrations/0021_statustranslation.py new file mode 100644 index 0000000000..892e37aff3 --- /dev/null +++ b/src/open_inwoner/openzaak/migrations/0021_statustranslation.py @@ -0,0 +1,44 @@ +# Generated by Django 3.2.20 on 2023-08-25 08:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "openzaak", + "0020_rename_contact_moments_enabled_zaaktypeconfig_contact_form_enabled", + ), + ] + + operations = [ + migrations.CreateModel( + name="StatusTranslation", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField( + max_length=255, unique=True, verbose_name="Status tekst" + ), + ), + ( + "translation", + models.CharField(max_length=255, verbose_name="Vertaling"), + ), + ], + options={ + "verbose_name": "Status vertaling", + "verbose_name_plural": "Status vertalingen", + }, + ), + ] diff --git a/src/open_inwoner/openzaak/models.py b/src/open_inwoner/openzaak/models.py index 2ac7f64576..76add7400f 100644 --- a/src/open_inwoner/openzaak/models.py +++ b/src/open_inwoner/openzaak/models.py @@ -12,6 +12,7 @@ from zgw_consumers.constants import APITypes from open_inwoner.openzaak.managers import ( + StatusTranslationQuerySet, UserCaseInfoObjectNotificationManager, UserCaseStatusNotificationManager, ZaakTypeConfigQueryset, @@ -361,3 +362,21 @@ def has_received_similar_notes_within(self, period: timedelta) -> bool: ) or UserCaseStatusNotification.objects.has_received_similar_notes_within( self.user, self.case_uuid, period ) + + +class StatusTranslation(models.Model): + status = models.CharField( + verbose_name=_("Status tekst"), + max_length=255, + unique=True, + ) + translation = models.CharField( + verbose_name=_("Vertaling"), + max_length=255, + ) + + objects = StatusTranslationQuerySet.as_manager() + + class Meta: + verbose_name = _("Status vertaling") + verbose_name_plural = _("Status vertalingen") diff --git a/src/open_inwoner/openzaak/resources/__init__.py b/src/open_inwoner/openzaak/resources/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/openzaak/resources/import_resource.py b/src/open_inwoner/openzaak/resources/import_resource.py new file mode 100644 index 0000000000..8dba9908c8 --- /dev/null +++ b/src/open_inwoner/openzaak/resources/import_resource.py @@ -0,0 +1,33 @@ +from django.utils.translation import ugettext_lazy as _ + +from import_export import fields, resources +from import_export.exceptions import ImportExportError + +from open_inwoner.openzaak.models import StatusTranslation + + +class StatusTranslationImportResource(resources.ModelResource): + def before_import(self, dataset, using_transactions, dry_run, **kwargs): + # Validate that file contains all the headers + missing_headers = set(self.get_diff_headers()) - set(dataset.headers) + if missing_headers: + missing_headers = ",\n".join(missing_headers) + raise ImportExportError(_(f"Missing required headers: {missing_headers}")) + + return super().before_import(dataset, using_transactions, dry_run, **kwargs) + + def get_or_init_instance(self, instance_loader, row): + # Replace newlines from excel + for key, value in row.items(): + if isinstance(value, str): + row[key] = value.replace("_x000D_", "\n") + + return super().get_or_init_instance(instance_loader, row) + + status = fields.Field(column_name="status", attribute="status") + translation = fields.Field(column_name="translation", attribute="translation") + + class Meta: + model = StatusTranslation + import_id_fields = ("status",) + fields = ("status", "translation") diff --git a/src/open_inwoner/openzaak/tests/factories.py b/src/open_inwoner/openzaak/tests/factories.py index 69299e619d..afa8c0579f 100644 --- a/src/open_inwoner/openzaak/tests/factories.py +++ b/src/open_inwoner/openzaak/tests/factories.py @@ -12,6 +12,7 @@ from open_inwoner.openzaak.api_models import Notification, Rol, ZaakType from open_inwoner.openzaak.models import ( CatalogusConfig, + StatusTranslation, UserCaseInfoObjectNotification, UserCaseStatusNotification, ZaakTypeConfig, @@ -141,6 +142,14 @@ class Meta: model = Notification +class StatusTranslationFactory(factory.django.DjangoModelFactory): + status = factory.Faker("pystr", max_chars=50) + translation = factory.Faker("pystr", max_chars=80) + + class Meta: + model = StatusTranslation + + def generate_rol( type_: str, identification: dict, diff --git a/src/open_inwoner/openzaak/tests/test_case_detail.py b/src/open_inwoner/openzaak/tests/test_case_detail.py index 17fc917164..274b6317af 100644 --- a/src/open_inwoner/openzaak/tests/test_case_detail.py +++ b/src/open_inwoner/openzaak/tests/test_case_detail.py @@ -24,6 +24,7 @@ from open_inwoner.accounts.tests.factories import UserFactory from open_inwoner.cms.cases.views.status import SimpleFile from open_inwoner.openzaak.tests.factories import ( + StatusTranslationFactory, ZaakTypeConfigFactory, ZaakTypeInformatieObjectTypeConfigFactory, ) @@ -350,6 +351,8 @@ def _setUpMocks(self, m): ) def test_status_is_retrieved_when_user_logged_in_via_digid(self, m): + self.maxDiff = None + self._setUpMocks(m) status_new_obj, status_finish_obj = factory( Status, [self.status_new, self.status_finish] @@ -370,7 +373,16 @@ def test_status_is_retrieved_when_user_logged_in_via_digid(self, m): "end_date_legal": datetime.date(2022, 1, 5), "description": "Coffee zaaktype", "current_status": "Finish", - "statuses": [status_new_obj, status_finish_obj], + "statuses": [ + { + "date": datetime.datetime(2021, 1, 12), + "label": "Initial request", + }, + { + "date": datetime.datetime(2021, 3, 12), + "label": "Finish", + }, + ], # only one visible information object "documents": [self.informatie_object_file], "initiator": "Foo Bar van der Bazz", @@ -407,6 +419,24 @@ def test_page_reformats_zaak_identificatie(self, m): spy_format.assert_called_once() + def test_page_translates_statuses(self, m): + st1 = StatusTranslationFactory( + status=self.status_type_new["omschrijving"], + translation="Translated First Status Type", + ) + st2 = StatusTranslationFactory( + status=self.status_type_finish["omschrijving"], + translation="Translated Second Status Type", + ) + self._setUpMocks(m) + response = self.app.get( + self.case_detail_url, user=self.user, headers={"HX-Request": "true"} + ) + self.assertNotContains(response, st1.status) + self.assertNotContains(response, st2.status) + self.assertContains(response, st1.translation) + self.assertContains(response, st2.translation) + def test_when_accessing_case_detail_a_timelinelog_is_created(self, m): self._setUpMocks(m) diff --git a/src/open_inwoner/openzaak/tests/test_cases.py b/src/open_inwoner/openzaak/tests/test_cases.py index 212e287d25..fa744883f0 100644 --- a/src/open_inwoner/openzaak/tests/test_cases.py +++ b/src/open_inwoner/openzaak/tests/test_cases.py @@ -21,7 +21,7 @@ from ...utils.tests.helpers import AssertRedirectsMixin from ..models import OpenZaakConfig from ..utils import format_zaak_identificatie -from .factories import ServiceFactory +from .factories import ServiceFactory, StatusTranslationFactory from .shared import CATALOGI_ROOT, ZAKEN_ROOT @@ -381,6 +381,18 @@ def test_list_open_cases_reformats_zaak_identificatie(self, m): spy_format.assert_called() self.assertEqual(spy_format.call_count, 2) + def test_list_open_cases_translates_status(self, m): + st1 = StatusTranslationFactory( + status=self.status_type1["omschrijving"], + translation="Translated Status Type", + ) + self._setUpMocks(m) + response = self.app.get( + self.inner_url_open, user=self.user, headers={"HX-Request": "true"} + ) + self.assertNotContains(response, st1.status) + self.assertContains(response, st1.translation) + def test_list_open_cases_logs_displayed_case_ids(self, m): self._setUpMocks(m) @@ -467,6 +479,18 @@ def test_list_closed_cases_reformats_zaak_identificatie(self, m): spy_format.assert_called() self.assertEqual(spy_format.call_count, 1) + def test_list_closed_cases_translates_status(self, m): + st1 = StatusTranslationFactory( + status=self.status_type2["omschrijving"], + translation="Translated Status Type", + ) + self._setUpMocks(m) + response = self.app.get( + self.inner_url_closed, user=self.user, headers={"HX-Request": "true"} + ) + self.assertNotContains(response, st1.status) + self.assertContains(response, st1.translation) + def test_list_closed_cases_logs_displayed_case_ids(self, m): self._setUpMocks(m) diff --git a/src/open_inwoner/openzaak/tests/test_status_translation.py b/src/open_inwoner/openzaak/tests/test_status_translation.py new file mode 100644 index 0000000000..bfd0ca27a8 --- /dev/null +++ b/src/open_inwoner/openzaak/tests/test_status_translation.py @@ -0,0 +1,26 @@ +from django.test import TestCase + +from open_inwoner.openzaak.models import StatusTranslation +from open_inwoner.openzaak.tests.factories import StatusTranslationFactory + + +class StatusTranslationModelTest(TestCase): + def test_lookup(self): + StatusTranslationFactory(status="foo", translation="FOO") + StatusTranslationFactory(status="bar", translation="BAR") + + lookup = StatusTranslation.objects.get_lookup() + + tests = [ + # input, expected + ("foo", "FOO"), + ("bar", "BAR"), + ("bazz", "bazz"), + ("", ""), + ] + for value, expected in tests: + with self.subTest(value=value, expected=expected): + actual = lookup(value) + self.assertEqual(expected, actual) + + # NOTE the TranslationLookup helper is further tested in its own file diff --git a/src/open_inwoner/templates/pages/cases/status_inner.html b/src/open_inwoner/templates/pages/cases/status_inner.html index 546873cf64..27c2cdab26 100644 --- a/src/open_inwoner/templates/pages/cases/status_inner.html +++ b/src/open_inwoner/templates/pages/cases/status_inner.html @@ -35,8 +35,8 @@

{% trans 'Status' %}

{% for status in case.statuses %}
  • {% icon icon="task_alt" %} -

    {{ status.datum_status_gezet|date }}

    -

    {{ status.statustype.omschrijving }}

    +

    {{ status.date|date }}

    +

    {{ status.label }}

  • {% endfor %} diff --git a/src/open_inwoner/templates/pages/contactmoment/detail.html b/src/open_inwoner/templates/pages/contactmoment/detail.html index ea0cc5e1df..9055fe43fc 100644 --- a/src/open_inwoner/templates/pages/contactmoment/detail.html +++ b/src/open_inwoner/templates/pages/contactmoment/detail.html @@ -1,5 +1,5 @@ {% extends 'master.html' %} -{% load i18n anchor_menu_tags card_tags dashboard_tags file_tags grid_tags status_tags table_tags solo_tags form_tags button_tags %} +{% load i18n anchor_menu_tags card_tags dashboard_tags file_tags grid_tags table_tags solo_tags form_tags button_tags %} {% block sidebar_content %} {% if contactmoment %} diff --git a/src/open_inwoner/utils/tests/test_translate.py b/src/open_inwoner/utils/tests/test_translate.py new file mode 100644 index 0000000000..ceb5871b3b --- /dev/null +++ b/src/open_inwoner/utils/tests/test_translate.py @@ -0,0 +1,69 @@ +from django.test import TestCase + +from open_inwoner.utils.translate import TranslationLookup + + +class TranslationLookupTest(TestCase): + def test_lookup(self): + values_list = [ + ("foo", "FOO"), + ("bar", "BAR"), + ] + lookup = TranslationLookup(values_list) + + tests = [ + # input, expected + ("foo", "FOO"), + ("bar", "BAR"), + ("bazz", "bazz"), + ("", ""), + ] + for value, expected in tests: + with self.subTest(value=value, expected=expected): + actual = lookup(value) + self.assertEqual(expected, actual) + + # extra + with self.subTest("normal key with default returns key"): + actual = lookup("bazz", default="buzz") + self.assertEqual("bazz", actual) + + with self.subTest("empty key with default return default"): + actual = lookup("", default="buzz") + self.assertEqual("buzz", actual) + + def test_lookup_from_glom(self): + values_list = [ + ("foo", "FOO"), + ("bar", "BAR"), + ] + lookup = TranslationLookup(values_list) + + data = { + "aaa": { + "fff": "foo", + "bbb": "bar", + "zzz": "bazz", + }, + } + + tests = [ + # input, expected + ("aaa.fff", "FOO"), + ("aaa.bbb", "BAR"), + ("aaa.zzz", "bazz"), + ("aaa.xxx", ""), + ("", ""), + ] + for value, expected in tests: + with self.subTest(value=value, expected=expected): + actual = lookup.from_glom(data, value) + self.assertEqual(expected, actual) + + with self.subTest("with default"): + actual = lookup.from_glom(data, "aaa.xxx", default="buzz") + self.assertEqual("buzz", actual) + + with self.subTest("with empty default"): + actual = lookup.from_glom(data, "aaa.xxx", default="") + self.assertEqual("", actual) diff --git a/src/open_inwoner/utils/translate.py b/src/open_inwoner/utils/translate.py new file mode 100644 index 0000000000..5755ac07f5 --- /dev/null +++ b/src/open_inwoner/utils/translate.py @@ -0,0 +1,38 @@ +from typing import Any, Iterable, Optional, Tuple + +from glom import glom + + +class TranslationLookup: + """ + simple key value lookup seeded from queryset.values_list(key, value) + """ + + def __init__(self, key_value_iterable: Iterable[Tuple[str, str]]): + self.mapping = dict(key_value_iterable) + + def __call__(self, key: str, *, default: str = "") -> str: + """ + lookup translation of `key` + """ + # no mapping is found return either default or original string + if not key: + return default + return self.mapping.get(key, key) + + def from_glom(self, obj: Any, path: str, *, default: str = "") -> str: + """ + convenience lookup translation of a value glommed by `path` from object `obj` + + usage: + + str = lookup.from_glom(zaak, "status.statustype.omschrijving", default=_("No data")) + """ + return self( + glom( + obj, + path, + default="", + ), + default=default, + ) From ede1259a605d029d0102d885d48df2049f02cfaa Mon Sep 17 00:00:00 2001 From: Bart van der Schoor Date: Mon, 4 Sep 2023 12:34:18 +0200 Subject: [PATCH 11/24] [#1624] Added merge migration --- ...e_0021_statustranslation_0022_mark_as_is_sent.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/open_inwoner/openzaak/migrations/0023_merge_0021_statustranslation_0022_mark_as_is_sent.py diff --git a/src/open_inwoner/openzaak/migrations/0023_merge_0021_statustranslation_0022_mark_as_is_sent.py b/src/open_inwoner/openzaak/migrations/0023_merge_0021_statustranslation_0022_mark_as_is_sent.py new file mode 100644 index 0000000000..1f4b6c241e --- /dev/null +++ b/src/open_inwoner/openzaak/migrations/0023_merge_0021_statustranslation_0022_mark_as_is_sent.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.20 on 2023-09-04 10:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("openzaak", "0021_statustranslation"), + ("openzaak", "0022_mark_as_is_sent"), + ] + + operations = [] From 4e3a004ca490f3768e96ada004397a81ebe601e5 Mon Sep 17 00:00:00 2001 From: Jiro Ghianni Date: Mon, 4 Sep 2023 17:39:17 +0200 Subject: [PATCH 12/24] [#1688] Removed unused render_notification template-tag --- .../templatetags/notification_tags.py | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/open_inwoner/components/templatetags/notification_tags.py b/src/open_inwoner/components/templatetags/notification_tags.py index 8bc72a6f44..799147c5e4 100644 --- a/src/open_inwoner/components/templatetags/notification_tags.py +++ b/src/open_inwoner/components/templatetags/notification_tags.py @@ -61,30 +61,3 @@ def notification(type, message, **kwargs): "message": message, **kwargs, } - - -@register.tag() -def render_notification(parser, token): - """ - Add a notification to the screen. These will be places inline. - - Usage: - {% render_notification type="success" %} - this is the message - {% endrender_notification %} - - Variables: - + type: string | the type of notification. This will change the coloring. - + message: string | The message for the notification. - - title: string | The title that should be displayed. - - action: string | The href of the button. - - action_text: string | The text of the button. - - closable: bool | If a close button should be shown. - """ - bits = token.split_contents() - context_kwargs = parse_component_with_args(parser, bits, "render_notification") - nodelist = parser.parse(("endrender_notification",)) # End tag - parser.delete_first_token() - return ContentsNode( - nodelist, "components/Notification/Notification.html", **context_kwargs - ) # Template From 23eecb80345288e22f849ed6282ec47ac7a11a79 Mon Sep 17 00:00:00 2001 From: Jiro Ghianni Date: Mon, 4 Sep 2023 17:29:28 +0200 Subject: [PATCH 13/24] [#1717] Restructured mobile menu so warningbanner does not overlap --- .../templates/components/Header/Header.html | 26 +++++++++---------- .../scss/components/Header/Header.scss | 14 +++++----- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/open_inwoner/components/templates/components/Header/Header.html b/src/open_inwoner/components/templates/components/Header/Header.html index 9b425c3bb8..d1183432d4 100644 --- a/src/open_inwoner/components/templates/components/Header/Header.html +++ b/src/open_inwoner/components/templates/components/Header/Header.html @@ -27,18 +27,19 @@ {% button text="" title="Inloggen" href=login_url icon="person" icon_position="before" primary=True icon_outlined=True transparent=True %} {% endif %} +
    + {# end of mobile header-menu with logo #} -
    - +
    {% if cms_apps.products %} {% if request.user.is_authenticated or not config.hide_search_from_anonymous_users %} - + {% endif %} {% endif %} @@ -102,10 +103,8 @@
    -
    -
    -{#end of mobile menu#} + {# end of submenu items #} {% firstof config.logo.default_alt_text config.name as logo_alt_text %}
    {% logo src=config.logo.file.url alt="Homepage "|add:logo_alt_text svg_height=75 %}
    @@ -126,11 +125,12 @@ {% include "components/Header/NavigationAuthenticated.html" %} + {# end of header container #} {% if cms_apps.products %} {% if request.user.is_authenticated or not config.hide_search_from_anonymous_users %} -