From 87716eaa97d33f2c6fe97b8ef9d5f23e85a5c076 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 8 Aug 2023 09:16:00 +0200 Subject: [PATCH 01/10] [#1654] fix jaaropgave pdf styling --- src/open_inwoner/scss/pdf/_jaaropgave.scss | 9 +++++++++ src/open_inwoner/scss/pdf/pdf_portrait.scss | 2 +- src/open_inwoner/ssd/client.py | 6 ------ src/open_inwoner/ssd/templates/jaaropgave.html | 5 +---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/open_inwoner/scss/pdf/_jaaropgave.scss b/src/open_inwoner/scss/pdf/_jaaropgave.scss index 724cb66a22..9fe4779233 100644 --- a/src/open_inwoner/scss/pdf/_jaaropgave.scss +++ b/src/open_inwoner/scss/pdf/_jaaropgave.scss @@ -132,5 +132,14 @@ .section__toelichting .subsection { line-height: 1.6; + font-size: 11px; + + h1, + h2 { + font-size: 12px; + } + h1 { + text-transform: uppercase; + } } } diff --git a/src/open_inwoner/scss/pdf/pdf_portrait.scss b/src/open_inwoner/scss/pdf/pdf_portrait.scss index d00db302da..51eefcb8f8 100644 --- a/src/open_inwoner/scss/pdf/pdf_portrait.scss +++ b/src/open_inwoner/scss/pdf/pdf_portrait.scss @@ -1,4 +1,4 @@ -@import './overwrites'; +// @import './overwrites'; @import './export'; @import './portrait'; @import './maandspecificatie'; diff --git a/src/open_inwoner/ssd/client.py b/src/open_inwoner/ssd/client.py index 47b2c4fcfa..7d63745a55 100644 --- a/src/open_inwoner/ssd/client.py +++ b/src/open_inwoner/ssd/client.py @@ -20,12 +20,6 @@ BASE_DIR = Path(__file__).absolute().parent.parent -# Endpoints for maand/jaarspecificatie -# https://2secure-test.enschede.nl/ENSC/Intern/SSD/UitkeringsSpecificatieClient-v0600 -# https://2secure-test.enschede.nl/ENSC/Intern/SSD/JaarOpgaveClient-v0400 -# Test with 900038937 - - class SSDBaseClient(ABC): """Base class for SSD SOAP client""" diff --git a/src/open_inwoner/ssd/templates/jaaropgave.html b/src/open_inwoner/ssd/templates/jaaropgave.html index 6b5ff00a7d..a6d6083827 100644 --- a/src/open_inwoner/ssd/templates/jaaropgave.html +++ b/src/open_inwoner/ssd/templates/jaaropgave.html @@ -132,11 +132,8 @@

Participatiewet


-
-

Toelichting

-
-

{{ jaaropgave_comments|markdown|safe }}

+

{{ jaaropgave_comments|markdown|safe }}

From 09308ff360b01b7dcc9e155186d5916bd82e6f9b Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 8 Aug 2023 09:58:41 +0200 Subject: [PATCH 02/10] [#1652] translate month names --- src/open_inwoner/ssd/client.py | 2 +- src/open_inwoner/ssd/forms.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/open_inwoner/ssd/client.py b/src/open_inwoner/ssd/client.py index 7d63745a55..44ff43652a 100644 --- a/src/open_inwoner/ssd/client.py +++ b/src/open_inwoner/ssd/client.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from datetime import datetime from pathlib import Path -from typing import Optional, get_type_hints +from typing import Optional from uuid import uuid4 from django.template import loader diff --git a/src/open_inwoner/ssd/forms.py b/src/open_inwoner/ssd/forms.py index 54f7696a88..f1326bce69 100644 --- a/src/open_inwoner/ssd/forms.py +++ b/src/open_inwoner/ssd/forms.py @@ -1,6 +1,7 @@ from datetime import date, datetime from django import forms +from django.template.defaultfilters import date as django_date from dateutil.relativedelta import relativedelta @@ -31,7 +32,7 @@ def get_monthly_report_dates() -> list[tuple[date, str]]: choices = [] for report_date in dates: - formatted = report_date.strftime("%B %Y") + formatted = django_date(report_date, "M Y") choices.append((report_date.date(), formatted)) return choices From b9e1d4fd6b683c3860099e71a6542d338eb43790 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 8 Aug 2023 10:01:05 +0200 Subject: [PATCH 03/10] [#1653] add SSDConfig and SOAP Services to admin-index fixture --- src/open_inwoner/conf/fixtures/django-admin-index.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/open_inwoner/conf/fixtures/django-admin-index.json b/src/open_inwoner/conf/fixtures/django-admin-index.json index 4e8c869a44..a13b5443c4 100644 --- a/src/open_inwoner/conf/fixtures/django-admin-index.json +++ b/src/open_inwoner/conf/fixtures/django-admin-index.json @@ -344,6 +344,14 @@ "openzaak", "zaaktypeinformatieobjecttypeconfig" ], + [ + "soap", + "soapservice" + ], + [ + "ssd", + "ssdconfig" + ], [ "zgw_consumers", "certificate" From d03d9543bd3843c918ee72e930dc492192994b8f Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 8 Aug 2023 10:37:36 +0200 Subject: [PATCH 04/10] [#1654] increase length of endpoint fields --- .../ssd/migrations/0004_auto_20230808_1043.py | 60 +++++++++++++++++++ src/open_inwoner/ssd/models.py | 11 ++-- src/open_inwoner/ssd/tests/test_forms.py | 4 +- 3 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/open_inwoner/ssd/migrations/0004_auto_20230808_1043.py diff --git a/src/open_inwoner/ssd/migrations/0004_auto_20230808_1043.py b/src/open_inwoner/ssd/migrations/0004_auto_20230808_1043.py new file mode 100644 index 0000000000..093f52352a --- /dev/null +++ b/src/open_inwoner/ssd/migrations/0004_auto_20230808_1043.py @@ -0,0 +1,60 @@ +# Generated by Django 3.2.15 on 2023-08-08 08:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("ssd", "0003_auto_20230802_1635"), + ] + + operations = [ + migrations.AlterField( + model_name="ssdconfig", + name="jaaropgave_display_text", + field=models.TextField( + blank=True, + help_text="The text displayed as overview of the 'Jaaropgave' tab", + verbose_name="Display text", + ), + ), + migrations.AlterField( + model_name="ssdconfig", + name="jaaropgave_endpoint", + field=models.CharField( + default="JaarOpgaveClient-v0400", + help_text="Endpoint for the jaaropgave request", + max_length=256, + verbose_name="Jaaropgave endpoint", + ), + ), + migrations.AlterField( + model_name="ssdconfig", + name="maandspecificatie_display_text", + field=models.TextField( + blank=True, + help_text="The text displayed as overview of the 'Maandspecificatie' tab", + verbose_name="Display text", + ), + ), + migrations.AlterField( + model_name="ssdconfig", + name="maandspecificatie_endpoint", + field=models.CharField( + default="UitkeringsSpecificatieClient-v0600", + help_text="Endpoint for the maandspecificatie request", + max_length=256, + verbose_name="Maandspecificatie endpoint", + ), + ), + migrations.AlterField( + model_name="ssdconfig", + name="mijn_uitkeringen_text", + field=models.TextField( + blank=True, + help_text="The text displayed as overview of the 'Mijn Uikeringen' section.", + verbose_name="Overview text", + ), + ), + ] diff --git a/src/open_inwoner/ssd/models.py b/src/open_inwoner/ssd/models.py index 8f91bb46d0..661ee60e6a 100644 --- a/src/open_inwoner/ssd/models.py +++ b/src/open_inwoner/ssd/models.py @@ -24,14 +24,14 @@ class SSDConfig(SingletonModel): ) maandspecificatie_endpoint = models.CharField( _("Maandspecificatie endpoint"), - max_length=32, - blank=True, + max_length=256, + default=("UitkeringsSpecificatieClient-v0600"), help_text=_("Endpoint for the maandspecificatie request"), ) jaaropgave_endpoint = models.CharField( _("Jaaropgave endpoint"), - max_length=32, - blank=True, + max_length=256, + default=("JaarOpgaveClient-v0400"), help_text=_("Endpoint for the jaaropgave request"), ) applicatie_naam = models.CharField( @@ -57,7 +57,6 @@ class SSDConfig(SingletonModel): ) mijn_uitkeringen_text = models.TextField( _("Overview text"), - max_length=704, blank=True, help_text=_("The text displayed as overview of the 'Mijn Uikeringen' section."), ) @@ -80,7 +79,6 @@ class SSDConfig(SingletonModel): ) jaaropgave_display_text = models.TextField( _("Display text"), - max_length=704, blank=True, help_text=_("The text displayed as overview of the 'Jaaropgave' tab"), ) @@ -107,7 +105,6 @@ class SSDConfig(SingletonModel): ) maandspecificatie_display_text = models.TextField( _("Display text"), - max_length=704, blank=True, help_text=_("The text displayed as overview of the 'Maandspecificatie' tab"), ) diff --git a/src/open_inwoner/ssd/tests/test_forms.py b/src/open_inwoner/ssd/tests/test_forms.py index f2fe69c76d..ca6bec4832 100644 --- a/src/open_inwoner/ssd/tests/test_forms.py +++ b/src/open_inwoner/ssd/tests/test_forms.py @@ -29,7 +29,7 @@ def test_get_monthly_choices(self, mock_solo): self.assertEqual(date.month, 1) date_repr = choices[0][1] - self.assertEqual(date_repr, "January 1985") + self.assertEqual(date_repr, "Jan 1985") @freeze_time("1985-01-25") @patch("open_inwoner.ssd.forms.SSDConfig.get_solo") @@ -50,7 +50,7 @@ def test_current_month_not_yet_available(self, m): self.assertEqual(date.month, 12) date_repr = choices[0][1] - self.assertEqual(date_repr, "December 1984") + self.assertEqual(date_repr, "Dec 1984") @patch("open_inwoner.ssd.forms.SSDConfig.get_solo") def test_monthly_reports_not_enabled(self, mock_solo): From e5bb19565026515af08e1d74bd8942c69cece0ea Mon Sep 17 00:00:00 2001 From: Alex de Landgraaf Date: Tue, 15 Aug 2023 20:46:18 +0200 Subject: [PATCH 05/10] Upgrading django-log-outgoing-requests to 0.5.0 --- requirements/base.txt | 2 +- requirements/ci.txt | 2 +- requirements/dev.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index c71ea5ce9b..b3065cbffd 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -195,7 +195,7 @@ django-js-asset==1.2.2 # django-mptt django-localflavor==3.1 # via -r requirements/base.in -django-log-outgoing-requests==0.4.0 +django-log-outgoing-requests==0.5.0 # via -r requirements/base.in django-mptt==0.13.4 # via django-filer diff --git a/requirements/ci.txt b/requirements/ci.txt index 12ff7fb46a..7d400ceb96 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -309,7 +309,7 @@ django-localflavor==3.1 # via # -c requirements/base.txt # -r requirements/base.txt -django-log-outgoing-requests==0.4.0 +django-log-outgoing-requests==0.5.0 # via # -c requirements/base.txt # -r requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index 9652e12f47..e98e665fb2 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -338,7 +338,7 @@ django-localflavor==3.1 # via # -c requirements/ci.txt # -r requirements/ci.txt -django-log-outgoing-requests==0.4.0 +django-log-outgoing-requests==0.5.0 # via # -c requirements/ci.txt # -r requirements/ci.txt From 62c48e24317f0c223f49cba328326502aad55962 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Wed, 9 Aug 2023 16:42:37 +0200 Subject: [PATCH 06/10] clean up implementation of ssd - fix translations of month names - remove unnecessary code for fixed values in xml parsing and template - remove code for testing/development purposes --- src/open_inwoner/ssd/client.py | 13 +----- .../ssd/templates/jaaropgave.html | 12 ++--- .../ssd/tests/test_xml_parsing.py | 11 +---- src/open_inwoner/ssd/xml.py | 46 ++----------------- 4 files changed, 12 insertions(+), 70 deletions(-) diff --git a/src/open_inwoner/ssd/client.py b/src/open_inwoner/ssd/client.py index 44ff43652a..f7f443f6eb 100644 --- a/src/open_inwoner/ssd/client.py +++ b/src/open_inwoner/ssd/client.py @@ -6,6 +6,7 @@ from uuid import uuid4 from django.template import loader +from django.template.defaultfilters import date as django_date from django.utils import timezone import requests @@ -144,11 +145,6 @@ def get_report( jaaropgave = response.text - # TODO: remove when done testing - # xml_response = "src/open_inwoner/ssd/tests/files/jaaropgave_response.xml" - # with open(xml_response, "r") as file: - # jaaropgave = file.read() - if (data := get_jaaropgave_dict(jaaropgave)) is None: return None @@ -185,7 +181,7 @@ def format_report_date(self, report_date_iso: str) -> str: def format_file_name(self, report_date_iso: str) -> str: dt = datetime.strptime(report_date_iso, "%Y-%m-%d") - return f"Maandspecificatie {dt.strftime('%m %Y')}" + return f"Maandspecificatie {django_date(dt, 'M Y')}" def get_report( self, bsn: str, report_date_iso: str, request_base_url: str @@ -199,11 +195,6 @@ def get_report( maandspecificatie = response.text - # TODO: remove when done testing - # xml_response = "src/open_inwoner/ssd/tests/files/uitkering_response.xml" - # with open(xml_response, "r") as file: - # maandspecificatie = file.read() - if (data := get_uitkering_dict(maandspecificatie)) is None: return None diff --git a/src/open_inwoner/ssd/templates/jaaropgave.html b/src/open_inwoner/ssd/templates/jaaropgave.html index a6d6083827..8b576e47ee 100644 --- a/src/open_inwoner/ssd/templates/jaaropgave.html +++ b/src/open_inwoner/ssd/templates/jaaropgave.html @@ -53,15 +53,14 @@

Participatiewet

{{ jaaropgave.fiscaalloon.key }} {{ jaaropgave.loonheffing.key }} - {{ jaaropgave.arbeidskorting.key }} + Verrekende arbeidskorting {{ jaaropgave.loon_heffings_korting.key }} {{ jaaropgave.loon_heffings_korting.code_label }} {{ jaaropgave.fiscaalloon.sign }} {{ jaaropgave.fiscaalloon.value }} {{ jaaropgave.loonheffing.sign }} {{ jaaropgave.loonheffing.value }} - - 0 + 0 {{ jaaropgave.loon_heffings_korting.dates.0.ingangsdatum }} {{ jaaropgave.loon_heffings_korting.dates.0.code }} @@ -90,14 +89,13 @@

Participatiewet

{{ client.bsn_label }} {{ jaaropgave.loon_zorgverzekeringswet.key }} - {{ jaaropgave.ingehouden_bijdrage.key }} + Ingehouden bijdrage Zorgverzekeringswet {{ jaaropgave.periode.key }} {{ client.bsn }} - {{ jaaropgave.loon_zorgverzekeringswet.value }} - {{ jaaropgave.ingehouden_bijdrage.sign }} {{ jaaropgave.ingehouden_bijdrage.value }} + 0 {{ jaaropgave.periode.van }} t/m {{ jaaropgave.periode.tot }} @@ -119,8 +117,6 @@

Participatiewet

{{ jaaropgave.code_loonbelastingtabel.key }} - - 0 0 {{ jaaropgave.werkgevers_heffing_premie.value }} diff --git a/src/open_inwoner/ssd/tests/test_xml_parsing.py b/src/open_inwoner/ssd/tests/test_xml_parsing.py index 20a61a81fe..e7714abbb1 100644 --- a/src/open_inwoner/ssd/tests/test_xml_parsing.py +++ b/src/open_inwoner/ssd/tests/test_xml_parsing.py @@ -51,7 +51,7 @@ def test_uitkering_response_parsing(self): data["uitkeringsspecificatie"]["dossiernummer"]["value"], "61913" ) self.assertEqual(data["uitkeringsspecificatie"]["periode"]["key"], "Periode") - self.assertEqual(data["uitkeringsspecificatie"]["periode"]["value"], "May 1985") + self.assertEqual(data["uitkeringsspecificatie"]["periode"]["value"], "Mei 1985") self.assertEqual(data["uitkeringsspecificatie"]["regeling"]["key"], "Regeling") self.assertEqual( data["uitkeringsspecificatie"]["regeling"]["value"], "Participatiewet" @@ -166,10 +166,6 @@ def test_jaaropgave_response_parsing(self): self.assertEqual(data["inhoudingsplichtige"]["woonplaatsnaam"], "Enschede") # jaaropgave - self.assertEqual( - data["jaaropgave"]["arbeidskorting"]["key"], "Verrekende arbeidskorting" - ) - self.assertEqual(data["jaaropgave"]["arbeidskorting"]["value"], "MYSTERY") self.assertEqual( data["jaaropgave"]["code_loonbelastingtabel"]["key"], "Code loonbelastingtabel", @@ -181,11 +177,6 @@ def test_jaaropgave_response_parsing(self): "Loon loonbelasting / volksverzekeringen", ) self.assertEqual(data["jaaropgave"]["fiscaalloon"]["value"], "7305") - self.assertEqual( - data["jaaropgave"]["ingehouden_bijdrage"]["key"], - "Ingehouden bijdrage Zorgverzekeringswet", - ) - self.assertEqual(data["jaaropgave"]["ingehouden_bijdrage"]["value"], "0") self.assertEqual( data["jaaropgave"]["loon_heffings_korting"]["key"], "Loonheffingskorting Met ingang van", diff --git a/src/open_inwoner/ssd/xml.py b/src/open_inwoner/ssd/xml.py index b055f0849d..ea7e0bfe22 100644 --- a/src/open_inwoner/ssd/xml.py +++ b/src/open_inwoner/ssd/xml.py @@ -1,10 +1,9 @@ -import calendar from datetime import datetime from typing import Optional from xml.parsers.expat import ExpatError +from django.template.defaultfilters import date as django_date from django.utils.text import slugify -from django.utils.translation import gettext_lazy as _ import xmltodict from glom import glom @@ -31,15 +30,12 @@ def format_date(date_str) -> str: def format_date_month_name(date_str) -> str: - """Transform '204805' into 'May 2048'""" + """Transform '204805' into 'Mei 2048'""" patched = date_str + "01" - date = datetime.strptime(patched, "%Y%m%d") - month_name = calendar.month_name[date.month] + dt = datetime.strptime(patched, "%Y%m%d") - formatted_date = _("{month_name} {year}").format( - month_name=month_name, year=date.year - ) + formatted_date = django_date(dt, "M Y") return formatted_date @@ -322,24 +318,14 @@ def get_jaaropgave_dict(xml_data) -> Optional[dict]: ), "value": glom(specificatiejaaropgave_spec, "Loonheffing.WaardeBedrag"), }, - # TODO: figure out if this is needed; see below "arbeidskorting": { "key": "Verrekende arbeidskorting", - "value": "MYSTERY", + "value": "0", }, "code_loonbelastingtabel": { "key": "Code loonbelastingtabel", "value": glom(specificatiejaaropgave_spec, "CdPremieVolksverzekering"), }, - "ingehouden_bijdrage": { - "key": "Ingehouden bijdrage Zorgverzekeringswet", - "sign": get_sign( - specificatiejaaropgave_spec, "IngehoudenPremieZVW.CdPositiefNegatief" - ), - "value": glom( - specificatiejaaropgave_spec, "IngehoudenPremieZVW.WaardeBedrag" - ), - }, "vergoeding_premie_zvw": { "sign": get_sign( specificatiejaaropgave_spec, "VergoedingPremieZVW.CdPositiefNegatief" @@ -366,28 +352,6 @@ def get_jaaropgave_dict(xml_data) -> Optional[dict]: "WerkgeversheffingPremieZVW.WaardeBedrag", ), }, - # TODO: figure out where this is used; relation to `arbeitskorting`? - # "belaste_alimentatie": { - # "sign": get_sign( - # specificatiejaaropgave_spec, "BelasteAlimentatie.CdPositiefNegatief" - # ), - # "bedrag": ( - # float( - # glom( - # specificatiejaaropgave_spec, - # "BelasteAlimentatie.WaardeBedrag", - # default=0, - # ) - # ) - # / 100 - # if glom( - # specificatiejaaropgave_spec, - # "BelasteAlimentatie.WaardeBedrag", - # default=0, - # ) - # else 0 - # ), - # }, } # From 2671d0a33b0b7b61e7b77a0c39e2467713042570 Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Wed, 16 Aug 2023 10:05:34 +0200 Subject: [PATCH 07/10] update settings for new version of django-log-outgoing-requests --- src/open_inwoner/conf/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index de70b00ea7..4d1947b9cf 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -423,7 +423,7 @@ "level": "INFO", "propagate": True, }, - "requests": { + "log_outgoing_requests": { "handlers": ["log_outgoing_requests", "save_outgoing_requests"], "level": "DEBUG", "propagate": True, @@ -436,6 +436,7 @@ # LOG OUTGOING REQUESTS # LOG_OUTGOING_REQUESTS_DB_SAVE = config("LOG_OUTGOING_REQUESTS_DB_SAVE", default=True) +LOG_OUTGOING_REQUESTS_RESET_DB_SAVE_AFTER = None # reset config after $ minutes # From 7a7eb6f20149d28901c1bbb4a8b5d4f49c227afa Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Mon, 14 Aug 2023 16:31:20 +0200 Subject: [PATCH 08/10] [#1657] add option to hide categories from anonymous users - added model field (bool) for hiding categories - added `LoginMaybeRequiredMixin` to categories views in order to conditionally restrict access to authenticated users - refactored rendering of nav bar (removed inclusion tags) --- .../templates/components/Header/Header.html | 93 ++++++++++--------- .../components/Header/PrimaryNavigation.html | 25 +++-- .../components/templatetags/header_tags.py | 46 --------- .../components/tests/test_header.py | 56 +++++++++++ src/open_inwoner/configurations/admin.py | 4 + ...on_hide_categories_from_anonymous_users.py | 22 +++++ src/open_inwoner/configurations/models.py | 7 ++ src/open_inwoner/pdc/tests/test_views.py | 93 +++++++++++++++++++ src/open_inwoner/pdc/views.py | 22 ++++- src/open_inwoner/ssd/tests/test_views.py | 2 +- src/open_inwoner/utils/context_processors.py | 1 + src/open_inwoner/utils/views.py | 16 ++++ 12 files changed, 281 insertions(+), 106 deletions(-) create mode 100644 src/open_inwoner/components/tests/test_header.py create mode 100644 src/open_inwoner/configurations/migrations/0047_siteconfiguration_hide_categories_from_anonymous_users.py create mode 100644 src/open_inwoner/pdc/tests/test_views.py diff --git a/src/open_inwoner/components/templates/components/Header/Header.html b/src/open_inwoner/components/templates/components/Header/Header.html index 5fcadc252f..f1badfa19e 100644 --- a/src/open_inwoner/components/templates/components/Header/Header.html +++ b/src/open_inwoner/components/templates/components/Header/Header.html @@ -3,6 +3,7 @@ {% get_solo "configurations.SiteConfiguration" as config %} {% accessibility_header request=request %} +
@@ -28,13 +29,13 @@
{% if cms_apps.products %} - + {% endif %}
{% if cms_apps.products %} - + {% if request.user.is_authenticated or not config.hide_categories_from_anonymous_users %} + + {% endif %} {% endif %} diff --git a/src/open_inwoner/components/templates/components/Header/PrimaryNavigation.html b/src/open_inwoner/components/templates/components/Header/PrimaryNavigation.html index 53ede41b53..689377090a 100644 --- a/src/open_inwoner/components/templates/components/Header/PrimaryNavigation.html +++ b/src/open_inwoner/components/templates/components/Header/PrimaryNavigation.html @@ -4,22 +4,21 @@ diff --git a/src/open_inwoner/components/templatetags/header_tags.py b/src/open_inwoner/components/templatetags/header_tags.py index f8eb87a5ba..553f32818b 100644 --- a/src/open_inwoner/components/templatetags/header_tags.py +++ b/src/open_inwoner/components/templatetags/header_tags.py @@ -1,7 +1,6 @@ from django import template from open_inwoner.configurations.models import SiteConfiguration -from open_inwoner.questionnaire.models import QuestionnaireStep register = template.Library() @@ -38,51 +37,6 @@ def header(categories, request, **kwargs): + request: Request | the django request object. - has_general_faq_questions: boolean | If the FAQ menu item should be shown. """ - return { - **kwargs, - "categories": categories, - "request": request, - } - - -@register.inclusion_tag("components/Header/PrimaryNavigation.html") -def primary_navigation(categories, request, **kwargs): - """ - Displaying the primary navigation - - Usage: - {% primary_navigation categories=Category.objects.all request=request %} - - Variables: - + categories: Category[] | The categories that should be displayed in the theme dropdown. - + request: Request | The django request object. - + questionnaire: QuestionnaireStep | The default QuestionnaireStep, if any. - - has_general_faq_questions: boolean | If the FAQ menu item should be shown. - - show_plans: boolean | If the Plan item should be shown. - """ - - return { - **kwargs, - "categories": categories, - "request": request, - } - - -@register.inclusion_tag("components/Header/NavigationAuthenticated.html") -def navigation_authenticated(categories, request, **kwargs): - """ - Displaying the desktop navigation when user is authenticated - - Usage: - {% navigation_authenticated categories=Category.objects.all request=request %} - - Variables: - + categories: Category[] | The categories that should be displayed in the theme dropdown. - + request: Request | The django request object. - + questionnaire: QuestionnaireStep | The default QuestionnaireStep, if any. - - has_general_faq_questions: boolean | If the FAQ menu item should be shown. - - show_plans: boolean | If the Plan item should be shown. - """ return { **kwargs, diff --git a/src/open_inwoner/components/tests/test_header.py b/src/open_inwoner/components/tests/test_header.py new file mode 100644 index 0000000000..5bb4f182ca --- /dev/null +++ b/src/open_inwoner/components/tests/test_header.py @@ -0,0 +1,56 @@ +from django.test import TestCase + +from pyquery import PyQuery + +from open_inwoner.accounts.tests.factories import UserFactory +from open_inwoner.cms.products.cms_apps import ProductsApphook +from open_inwoner.cms.tests import cms_tools +from open_inwoner.cms.tests.cms_tools import create_apphook_page +from open_inwoner.configurations.models import SiteConfiguration +from open_inwoner.pdc.tests.factories import CategoryFactory + + +class HeaderTest(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = UserFactory() + cls.user.set_password("12345") + cls.user.email = "test@email.com" + cls.user.save() + + cms_tools.create_homepage() + + # PrimaryNavigation.html requires apphook + categories + create_apphook_page(ProductsApphook) + cls.published1 = CategoryFactory( + path="0001", name="First one", slug="first-one" + ) + cls.published2 = CategoryFactory( + path="0002", name="Second one", slug="second-one" + ) + + def test_categories_hidden_from_anonymous_users(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = True + config.save() + + response = self.client.get("/") + + doc = PyQuery(response.content) + + categories = doc.find("[title='Onderwerpen']") + self.assertEqual(len(categories), 0) + + def test_categories_not_hidden_from_anonymous_users(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = False + config.save() + + response = self.client.get("/") + + doc = PyQuery(response.content) + + categories = doc.find("[title='Onderwerpen']") + self.assertEqual(len(categories), 2) + self.assertEqual(categories[0].tag, "a") + self.assertEqual(categories[1].tag, "button") diff --git a/src/open_inwoner/configurations/admin.py b/src/open_inwoner/configurations/admin.py index 6aa1a0b3ca..ef91eaede7 100644 --- a/src/open_inwoner/configurations/admin.py +++ b/src/open_inwoner/configurations/admin.py @@ -189,6 +189,10 @@ class SiteConfigurarionAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin): ), }, ), + ( + _("Display options for anonymous users"), + {"fields": ("hide_categories_from_anonymous_users",)}, + ), ) inlines = [SiteConfigurationPageInline] form = SiteConfigurarionAdminForm diff --git a/src/open_inwoner/configurations/migrations/0047_siteconfiguration_hide_categories_from_anonymous_users.py b/src/open_inwoner/configurations/migrations/0047_siteconfiguration_hide_categories_from_anonymous_users.py new file mode 100644 index 0000000000..4695922065 --- /dev/null +++ b/src/open_inwoner/configurations/migrations/0047_siteconfiguration_hide_categories_from_anonymous_users.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.15 on 2023-08-15 12:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("configurations", "0046_siteconfiguration_cookie_consent"), + ] + + operations = [ + migrations.AddField( + model_name="siteconfiguration", + name="hide_categories_from_anonymous_users", + field=models.BooleanField( + default=False, + help_text="If checked, categories will be hidden from users who are not logged in.", + verbose_name="Hide categories from anonymouns users", + ), + ), + ] diff --git a/src/open_inwoner/configurations/models.py b/src/open_inwoner/configurations/models.py index 8e44aef0e1..189dd5289a 100644 --- a/src/open_inwoner/configurations/models.py +++ b/src/open_inwoner/configurations/models.py @@ -445,6 +445,13 @@ class SiteConfiguration(SingletonModel): default=True, help_text=_("Whether file sharing via the messages is allowed or not"), ) + hide_categories_from_anonymous_users = models.BooleanField( + verbose_name=_("Hide categories from anonymouns users"), + default=False, + help_text=_( + "If checked, categories will be hidden from users who are not logged in." + ), + ) class Meta: verbose_name = _("Site Configuration") diff --git a/src/open_inwoner/pdc/tests/test_views.py b/src/open_inwoner/pdc/tests/test_views.py new file mode 100644 index 0000000000..27d1565188 --- /dev/null +++ b/src/open_inwoner/pdc/tests/test_views.py @@ -0,0 +1,93 @@ +from django.test import TestCase, override_settings +from django.urls import reverse + +from open_inwoner.accounts.tests.factories import UserFactory +from open_inwoner.configurations.models import SiteConfiguration + +from .factories import CategoryFactory + + +@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") +class TestCategoryListView(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = UserFactory() + cls.user.set_password("12345") + cls.user.email = "test@email.com" + cls.user.save() + + def test_access_restricted(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = True + config.save() + + url = reverse("products:category_list") + + # request with anonymous user + response = self.client.get(url) + + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/accounts/login/?next=/products/") + + # request with user logged in + self.client.login(email=self.user.email, password="12345") + + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + + def test_access_not_restricted(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = False + config.save() + + url = reverse("products:category_list") + + # request with anonymous user + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + + +@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") +class TestCategoryDetailView(TestCase): + @classmethod + def setUpTestData(cls): + cls.user = UserFactory() + cls.user.set_password("12345") + cls.user.email = "test@email.com" + cls.user.save() + + cls.category = CategoryFactory.create(name="test cat") + + def test_access_restricted(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = True + config.save() + + url = reverse("products:category_detail", kwargs={"slug": self.category.slug}) + + # request with anonymous user + response = self.client.get(url) + + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, "/accounts/login/?next=/products/test-cat/") + + # request with user logged in + self.client.login(email=self.user.email, password="12345") + + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + + def test_access_not_restricted(self): + config = SiteConfiguration.get_solo() + config.hide_categories_from_anonymous_users = False + config.save() + + url = reverse("products:category_detail", kwargs={"slug": self.category.slug}) + + # request with anonymous user + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) diff --git a/src/open_inwoner/pdc/views.py b/src/open_inwoner/pdc/views.py index c64897f80a..53f88c0581 100644 --- a/src/open_inwoner/pdc/views.py +++ b/src/open_inwoner/pdc/views.py @@ -9,8 +9,8 @@ from open_inwoner.configurations.models import SiteConfiguration from open_inwoner.pdc.models.product import ProductCondition -from open_inwoner.plans.models import Plan from open_inwoner.questionnaire.models import QuestionnaireStep +from open_inwoner.utils.views import LoginMaybeRequiredMixin from ..utils.views import CommonPageMixin from .choices import YesNo @@ -112,7 +112,9 @@ def get_context_data(self, **kwargs): return super().get_context_data(**kwargs) -class CategoryListView(CommonPageMixin, ListBreadcrumbMixin, ListView): +class CategoryListView( + LoginMaybeRequiredMixin, CommonPageMixin, ListBreadcrumbMixin, ListView +): template_name = "pages/category/list.html" model = Category @@ -124,9 +126,18 @@ def crumbs(self): config = SiteConfiguration.get_solo() return [(config.theme_title, reverse("products:category_list"))] + @property + def display_restricted(self): + config = SiteConfiguration.get_solo() + return config.hide_categories_from_anonymous_users is True + class CategoryDetailView( - CommonPageMixin, BaseBreadcrumbMixin, CategoryBreadcrumbMixin, DetailView + LoginMaybeRequiredMixin, + CommonPageMixin, + BaseBreadcrumbMixin, + CategoryBreadcrumbMixin, + DetailView, ): template_name = "pages/category/detail.html" model = Category @@ -166,6 +177,11 @@ def get_context_data(self, **kwargs): def get_breadcrumb_name(self): return self.object.name + @property + def display_restricted(self): + config = SiteConfiguration.get_solo() + return config.hide_categories_from_anonymous_users is True + class ProductDetailView( CommonPageMixin, BaseBreadcrumbMixin, CategoryBreadcrumbMixin, DetailView diff --git a/src/open_inwoner/ssd/tests/test_views.py b/src/open_inwoner/ssd/tests/test_views.py index d6aaced445..2b7e080b4c 100644 --- a/src/open_inwoner/ssd/tests/test_views.py +++ b/src/open_inwoner/ssd/tests/test_views.py @@ -119,9 +119,9 @@ def setUp(self): self.user.save() def test_get(self): - url = reverse("ssd:yearly_benefits_index") # request with anonymous user + url = reverse("ssd:yearly_benefits_index") response = self.client.get(url) self.assertEqual(response.status_code, HTTPStatus.FOUND) diff --git a/src/open_inwoner/utils/context_processors.py b/src/open_inwoner/utils/context_processors.py index 21e30374a7..51869b9d36 100644 --- a/src/open_inwoner/utils/context_processors.py +++ b/src/open_inwoner/utils/context_processors.py @@ -86,6 +86,7 @@ def settings(request): "settings": dict( [(k, getattr(django_settings, k, None)) for k in public_settings] ), + "hide_categories_from_anonymous_users": config.hide_categories_from_anonymous_users, } if hasattr(django_settings, "SENTRY_CONFIG"): diff --git a/src/open_inwoner/utils/views.py b/src/open_inwoner/utils/views.py index ab78d18b1c..4cebb559bb 100644 --- a/src/open_inwoner/utils/views.py +++ b/src/open_inwoner/utils/views.py @@ -1,4 +1,5 @@ from django import http +from django.contrib.auth.mixins import AccessMixin from django.template import TemplateDoesNotExist, loader from django.views.decorators.csrf import requires_csrf_token from django.views.defaults import ERROR_500_TEMPLATE_NAME @@ -49,6 +50,21 @@ def crumbs(self): ] +class LoginMaybeRequiredMixin(AccessMixin): + """ + Conditional access control on a per-view basis + + Access to the view is restricted to authenticated users if + `self.display_restricted` is `True`, which must be defined on + the view inheriting from this Mixin. + """ + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated and self.display_restricted: + return self.handle_no_permission() + return super().dispatch(request, *args, **kwargs) + + @requires_csrf_token def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): """ From fce4c49ffbf46aab81b6f6358ff065585672dfce Mon Sep 17 00:00:00 2001 From: Paul Schilling Date: Tue, 15 Aug 2023 17:18:07 +0200 Subject: [PATCH 09/10] [#1657] add option to hide search bar and page from anonymous users --- .../templates/components/Header/Header.html | 6 +++-- .../components/tests/test_header.py | 27 +++++++++++++++++++ src/open_inwoner/configurations/admin.py | 7 ++++- ...on_hide_categories_from_anonymous_users.py | 2 +- .../migrations/0048_auto_20230816_1255.py | 25 +++++++++++++++++ src/open_inwoner/configurations/models.py | 9 ++++++- src/open_inwoner/search/tests/test_views.py | 25 +++++++++++++++++ src/open_inwoner/search/views.py | 12 +++++++-- 8 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 src/open_inwoner/configurations/migrations/0048_auto_20230816_1255.py create mode 100644 src/open_inwoner/search/tests/test_views.py diff --git a/src/open_inwoner/components/templates/components/Header/Header.html b/src/open_inwoner/components/templates/components/Header/Header.html index f1badfa19e..f3a5b6135d 100644 --- a/src/open_inwoner/components/templates/components/Header/Header.html +++ b/src/open_inwoner/components/templates/components/Header/Header.html @@ -29,6 +29,7 @@
{% if cms_apps.products %} + {% if request.user.is_authenticated or not config.hide_search_from_anonymous_users %} + {% endif %} {% endif %}
- + {% endfor %} diff --git a/src/open_inwoner/templates/utils/widgets/message_file_input.html b/src/open_inwoner/templates/utils/widgets/message_file_input.html index e2f37c7559..0eec17b12c 100644 --- a/src/open_inwoner/templates/utils/widgets/message_file_input.html +++ b/src/open_inwoner/templates/utils/widgets/message_file_input.html @@ -1,5 +1,5 @@ - + diff --git a/src/open_inwoner/templates/utils/widgets/private_file_input.html b/src/open_inwoner/templates/utils/widgets/private_file_input.html index 48d1b6111d..539154b584 100644 --- a/src/open_inwoner/templates/utils/widgets/private_file_input.html +++ b/src/open_inwoner/templates/utils/widgets/private_file_input.html @@ -1,5 +1,5 @@ {% if widget.is_initial %}{{ widget.initial_text }}: {{ widget.value }}{% if not widget.required %} -{% endif %}
+{{ widget.clear_checkbox_label }}{% endif %}
{{ widget.input_text }}:{% endif %}