From 1852c627ef07c8db4dd45c097dd05d513d93e928 Mon Sep 17 00:00:00 2001 From: Charlotte <47758554+charludo@users.noreply.github.com> Date: Fri, 17 Jan 2025 08:32:39 +0100 Subject: [PATCH] Prevent orphaned contact cards (#3282) --- integreat_cms/cms/fixtures/test_data.json | 89 +++++++++++++++++- integreat_cms/cms/models/contact/contact.py | 8 +- .../cms/templates/contacts/contact_card.html | 6 +- .../cms/templates/contacts/contact_form.html | 6 +- .../cms/views/contacts/contact_actions.py | 54 +++++++---- .../views/contacts/contact_bulk_actions.py | 45 ++++++++- integreat_cms/locale/de/LC_MESSAGES/django.po | 40 ++++++++ integreat_cms/static/src/css/contact_card.css | 4 + tests/api/api_config.py | 4 +- .../augsburg_de_children.json | 21 +++++ ...burg_de_children_archived_descendants.json | 4 +- .../augsburg_de_children_depth_2.json | 21 +++++ .../expected-outputs/augsburg_de_pages.json | 30 +++++- tests/cms/models/contacts/test_contacts.py | 16 ++-- tests/cms/test_duplicate_regions.py | 10 +- .../views/contacts/test_contact_actions.py | 65 ++++++------- tests/cms/views/poi/test_poi_form.py | 2 +- .../Integreat - Deutsch - Augsburg.pdf | Bin 100586 -> 101694 bytes tests/pdf/test_pdf_export.py | 4 +- .../expected-sitemaps/sitemap-augsburg-de.xml | 2 +- tests/sitemap/sitemap_config.py | 2 +- 21 files changed, 336 insertions(+), 97 deletions(-) rename tests/pdf/files/{429b3fbce4 => 89219cf973}/Integreat - Deutsch - Augsburg.pdf (81%) diff --git a/integreat_cms/cms/fixtures/test_data.json b/integreat_cms/cms/fixtures/test_data.json index 134fcf6288..50c144af44 100644 --- a/integreat_cms/cms/fixtures/test_data.json +++ b/integreat_cms/cms/fixtures/test_data.json @@ -1034,6 +1034,21 @@ "created_date": "2024-08-06T13:23:45.256Z" } }, + { + "model": "cms.contact", + "pk": 5, + "fields": { + "point_of_contact_for": "Testkontakt", + "name": "referred to in a page", + "location": 6, + "email": "", + "phone_number": "", + "website": "", + "archived": false, + "last_updated": "2025-01-08T13:53:31.813Z", + "created_date": "2025-01-08T13:53:31.806Z" + } + }, { "model": "cms.recurrencerule", "pk": 1, @@ -2304,6 +2319,22 @@ "redirect_to": "" } }, + { + "model": "linkcheck.url", + "pk": 21, + "fields": { + "url": "/augsburg/contact/5/", + "last_checked": "2025-01-08T13:54:42.851Z", + "anchor_status": null, + "ssl_status": null, + "status": true, + "status_code": 200, + "redirect_status_code": null, + "message": "", + "error_message": "", + "redirect_to": "" + } + }, { "model": "linkcheck.link", "pk": 1, @@ -2760,6 +2791,18 @@ "ignore": false } }, + { + "model": "linkcheck.link", + "pk": 40, + "fields": { + "content_type": ["cms", "pagetranslation"], + "object_id": 101, + "field": "content", + "url": 21, + "text": "Contact", + "ignore": false + } + }, { "model": "cms.page", "pk": 1, @@ -3450,6 +3493,29 @@ "embedded_offers": [] } }, + { + "model": "cms.page", + "pk": 31, + "fields": { + "created_date": "2025-01-11T07:07:46.585Z", + "lft": 1, + "rgt": 2, + "tree_id": 11, + "depth": 1, + "parent": null, + "region": 1, + "explicitly_archived": false, + "icon": null, + "mirrored_page": null, + "mirrored_page_first": true, + "organization": null, + "api_token": "", + "hix_ignore": false, + "authors": [], + "editors": [], + "embedded_offers": [] + } + }, { "model": "cms.regionfeedback", "pk": 1, "fields": { "feedback_ptr": 1 } }, { "model": "cms.regionfeedback", "pk": 4, "fields": { "feedback_ptr": 4 } }, { "model": "cms.regionfeedback", "pk": 7, "fields": { "feedback_ptr": 7 } }, @@ -4262,7 +4328,7 @@ "title": "Gesundheitsamt", "slug": "gesundheitsamt", "status": "PUBLIC", - "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", + "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", "language": 1, "currently_in_translation": false, "machine_translated": false, @@ -5557,6 +5623,27 @@ "hix_feedback": null } }, + { + "model": "cms.pagetranslation", + "pk": 101, + "fields": { + "title": "Wichtige Kontakte", + "slug": "wichtige-kontakte", + "status": "PUBLIC", + "content": "
Contact\r\n

referred to in a page | Testkontakt

\r\n

\"Address:Viktoriastraße 1, 86150 Augsburg

\r\n
", + "language": 1, + "currently_in_translation": false, + "machine_translated": false, + "version": 1, + "minor_edit": false, + "last_updated": "2025-01-11T07:07:46.642Z", + "creator": ["root"], + "automatic_translation": false, + "page": 31, + "hix_score": null, + "hix_feedback": null + } + }, { "model": "cms.imprintpagetranslation", "pk": 1, diff --git a/integreat_cms/cms/models/contact/contact.py b/integreat_cms/cms/models/contact/contact.py index 6053d529c6..d21450c22d 100644 --- a/integreat_cms/cms/models/contact/contact.py +++ b/integreat_cms/cms/models/contact/contact.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Generator, TYPE_CHECKING +from typing import List, TYPE_CHECKING from django.conf import settings from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector @@ -225,16 +225,16 @@ def referring_event_translations(self) -> QuerySet[EventTranslation]: ) @cached_property - def referring_objects(self) -> Generator[AbstractContentTranslation]: + def referring_objects(self) -> List[AbstractContentTranslation]: """ Returns a list of all objects linking to this contact. :return: all objects referring to this contact """ - return ( + return [ link.content_object for link in Link.objects.filter(url__url=self.absolute_url) - ) + ] def archive(self) -> None: """ diff --git a/integreat_cms/cms/templates/contacts/contact_card.html b/integreat_cms/cms/templates/contacts/contact_card.html index 191bce298f..afe0de11f0 100644 --- a/integreat_cms/cms/templates/contacts/contact_card.html +++ b/integreat_cms/cms/templates/contacts/contact_card.html @@ -26,12 +26,11 @@

{% endif %}

Address: -   {{ contact.location.short_address }} + {{ contact.location.short_address }}

{% if contact.email %}

Email: -   {{ contact.email }}

{% endif %} @@ -41,13 +40,12 @@

alt="Phone Number: " width="15" height="15" /> -   {{ contact.phone_number }} + {{ contact.phone_number }}

{% endif %} {% if contact.website %}

Website: -   {{ contact.website }}

{% endif %} diff --git a/integreat_cms/cms/templates/contacts/contact_form.html b/integreat_cms/cms/templates/contacts/contact_form.html index 7c72d77aa4..c00fb3f6ce 100644 --- a/integreat_cms/cms/templates/contacts/contact_form.html +++ b/integreat_cms/cms/templates/contacts/contact_form.html @@ -100,7 +100,8 @@

data-confirmation-title="{{ archive_dialog_title }}" data-confirmation-text="{{ archive_dialog_text }}" data-confirmation-subject="{{ contact_form.instance.name }}" - data-action="{% url 'archive_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}"> + data-action="{% url 'archive_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}" + {% if contact_form.instance.referring_objects %}disabled{% endif %}> {% translate "Archive this contact" %} @@ -122,7 +123,8 @@

data-confirmation-title="{{ delete_dialog_title }}" data-confirmation-text="{{ delete_dialog_text }}" data-confirmation-subject="{{ contact_form.instance.name }}" - data-action="{% url 'delete_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}"> + data-action="{% url 'delete_contact' contact_id=contact_form.instance.id region_slug=request.region.slug %}" + {% if contact_form.instance.referring_objects %}disabled{% endif %}> {% translate "Delete contact" %} diff --git a/integreat_cms/cms/views/contacts/contact_actions.py b/integreat_cms/cms/views/contacts/contact_actions.py index 65f63cd4c1..0d4469c74a 100644 --- a/integreat_cms/cms/views/contacts/contact_actions.py +++ b/integreat_cms/cms/views/contacts/contact_actions.py @@ -28,18 +28,25 @@ def archive_contact( to_be_archived_contact = get_object_or_404( Contact, id=contact_id, location__region=request.region ) - to_be_archived_contact.archive() - - messages.success( + if not to_be_archived_contact.referring_objects: + to_be_archived_contact.archive() + messages.success( + request, + _("Contact {0} was successfully archived").format(to_be_archived_contact), + ) + return redirect( + "contacts", + **{ + "region_slug": region_slug, + }, + ) + messages.error( request, - _("Contact {0} was successfully archived").format(to_be_archived_contact), - ) - return redirect( - "contacts", - **{ - "region_slug": region_slug, - }, + _('Cannot archive contact "{0}" while content objects refer to it.').format( + to_be_archived_contact, + ), ) + return redirect("edit_contact", region_slug=region_slug, contact_id=contact_id) @permission_required("cms.delete_contact") @@ -57,16 +64,25 @@ def delete_contact( to_be_deleted_contact = get_object_or_404( Contact, id=contact_id, location__region=request.region ) - to_be_deleted_contact.delete() - messages.success( - request, _("Contact {0} was successfully deleted").format(to_be_deleted_contact) - ) - return redirect( - "contacts", - **{ - "region_slug": region_slug, - }, + if not to_be_deleted_contact.referring_objects: + to_be_deleted_contact.delete() + messages.success( + request, + _("Contact {0} was successfully deleted").format(to_be_deleted_contact), + ) + return redirect( + "contacts", + **{ + "region_slug": region_slug, + }, + ) + messages.error( + request, + _('Cannot delete contact "{0}" while content objects refer to it.').format( + to_be_deleted_contact, + ), ) + return redirect("edit_contact", region_slug=region_slug, contact_id=contact_id) @permission_required("cms.change_contact") diff --git a/integreat_cms/cms/views/contacts/contact_bulk_actions.py b/integreat_cms/cms/views/contacts/contact_bulk_actions.py index 76d6b6364a..da0822c5c3 100644 --- a/integreat_cms/cms/views/contacts/contact_bulk_actions.py +++ b/integreat_cms/cms/views/contacts/contact_bulk_actions.py @@ -50,10 +50,14 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: """ archive_successful = [] + archive_failure = [] for content_object in self.get_queryset(): - content_object.archive() - archive_successful.append(content_object) + if not content_object.referring_objects: + content_object.archive() + archive_successful.append(content_object) + else: + archive_failure.append(content_object) if archive_successful: messages.success( @@ -69,6 +73,20 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) + if archive_failure: + messages.error( + request, + ngettext_lazy( + "{model_name} {object_names} cannot be archived while content objects refer to it.", + "The following {model_name_plural} could be archived: {object_names}", + ).format( + len(archive_failure), + model_name=self.model._meta.verbose_name.title(), + model_name_plural=self.model._meta.verbose_name_plural, + object_names=iter_to_string(archive_failure), + ), + ) + return super().post(request, *args, **kwargs) @@ -131,9 +149,14 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: :return: The redirect """ delete_sucessful = [] + delete_failure = [] + for content_object in self.get_queryset(): - content_object.delete() - delete_sucessful.append(content_object) + if not content_object.referring_objects: + content_object.delete() + delete_sucessful.append(content_object) + else: + delete_failure.append(content_object) if delete_sucessful: messages.success( @@ -149,4 +172,18 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ), ) + if delete_failure: + messages.error( + request, + ngettext_lazy( + "{model_name} {object_names} cannot be deleted while content objects refer to it.", + "The following {model_name_plural} could be deleted: {object_names}", + len(delete_failure), + ).format( + model_name=self.model._meta.verbose_name.title(), + model_name_plural=self.model._meta.verbose_name_plural, + object_names=iter_to_string(delete_failure), + ), + ) + return super().post(request, *args, **kwargs) diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index a48f125b6b..88ad2f3981 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -9170,11 +9170,23 @@ msgstr[1] "" msgid "Contact {0} was successfully archived" msgstr "Kontakt {0} wurde erfolgreich archiviert" +#: cms/views/contacts/contact_actions.py +#, python-brace-format +msgid "Cannot archive contact \"{0}\" while content objects refer to it." +msgstr "" +"Kontakt {0} kann nicht archiviert werden solange Inhalte auf ihn verweisen." + #: cms/views/contacts/contact_actions.py #, python-brace-format msgid "Contact {0} was successfully deleted" msgstr "Kontakt {0} wurde erfolgreich gelöscht." +#: cms/views/contacts/contact_actions.py +#, python-brace-format +msgid "Cannot delete contact \"{0}\" while content objects refer to it." +msgstr "" +"Kontakt {0} kann nicht gelöscht werden solange Inhalte auf ihn verweisen." + #: cms/views/contacts/contact_actions.py #, python-brace-format msgid "Contact {0} was successfully restored" @@ -9185,6 +9197,20 @@ msgstr "Kontakt {0} wurde erfolgreich wiederhergestellt" msgid "Contact {0} was successfully copied" msgstr "Kontakt {0} wurde erfolgreich kopiert" +#: cms/views/contacts/contact_bulk_actions.py +#, python-brace-format +msgid "" +"{model_name} {object_names} cannot be archived while content objects refer " +"to it." +msgid_plural "" +"The following {model_name_plural} could be archived: {object_names}" +msgstr[0] "" +"{model_name} {object_names} kann nicht archiviert werden solange Inhalte auf " +"ihn verweisen." +msgstr[1] "" +"Die folgenden {model_name_plural} konnten nicht archiviert werden: " +"{object_names}" + #: cms/views/contacts/contact_bulk_actions.py #: cms/views/organizations/organization_bulk_actions.py #, python-brace-format @@ -9195,6 +9221,20 @@ msgstr[0] "{model_name} {object_names} wurde erfolgreich gelöscht." msgstr[1] "" "Die folgenden {model_name_plural} wurden erfolgreich gelöscht: {object_names}" +#: cms/views/contacts/contact_bulk_actions.py +#, python-brace-format +msgid "" +"{model_name} {object_names} cannot be deleted while content objects refer to " +"it." +msgid_plural "" +"The following {model_name_plural} could be deleted: {object_names}" +msgstr[0] "" +"{model_name} {object_names} kann nicht gelöscht werden solange Inhalte auf " +"ihn verweisen." +msgstr[1] "" +"Die folgenden {model_name_plural} konnten nicht gelöscht werden: " +"{object_names}" + #: cms/views/contacts/contact_context_mixin.py msgid "Please confirm that you really want to archive this contact" msgstr "Bitte bestätigen Sie, dass dieser Kontakt archiviert werden soll." diff --git a/integreat_cms/static/src/css/contact_card.css b/integreat_cms/static/src/css/contact_card.css index 3cc240d3a3..2895818895 100644 --- a/integreat_cms/static/src/css/contact_card.css +++ b/integreat_cms/static/src/css/contact_card.css @@ -22,4 +22,8 @@ h4 { margin: 0 0 12px 0; } + + img { + margin: 0 8px 0 0; + } } diff --git a/tests/api/api_config.py b/tests/api/api_config.py index 6ed030bbd5..65ccb9da8e 100644 --- a/tests/api/api_config.py +++ b/tests/api/api_config.py @@ -121,7 +121,7 @@ "/augsburg/de/wp-json/extensions/v3/children/", "tests/api/expected-outputs/augsburg_de_children.json", 200, - 14, + 15, ), ( "/api/v3/augsburg/de/children/?depth=3&url=/augsburg/de/behörden-und-beratung/behörden/", @@ -142,7 +142,7 @@ "/augsburg/de/wp-json/extensions/v3/children/?depth=2", "tests/api/expected-outputs/augsburg_de_children_depth_2.json", 200, - 14, + 15, ), ( "/api/v3/augsburg/de/events/", diff --git a/tests/api/expected-outputs/augsburg_de_children.json b/tests/api/expected-outputs/augsburg_de_children.json index 993b27c487..1d98b869cd 100644 --- a/tests/api/expected-outputs/augsburg_de_children.json +++ b/tests/api/expected-outputs/augsburg_de_children.json @@ -229,5 +229,26 @@ "organization": null, "embedded_offers": [], "hash": null + }, + { + "id": 101, + "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/", + "path": "/augsburg/de/wichtige-kontakte/", + "title": "Wichtige Kontakte", + "modified_gmt": "2025-01-11T07:07:46.642Z", + "last_updated": "2025-01-11T08:07:46.642+01:00", + "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n", + "content": "
Contact\r\n

referred to in a page | Testkontakt

\r\n

\"Address:Viktoriastraße 1, 86150 Augsburg

\r\n
", + "parent": { + "id": 0, + "url": null, + "path": null + }, + "order": 11, + "available_languages": {}, + "thumbnail": null, + "organization": null, + "hash": null, + "embedded_offers": [] } ] diff --git a/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json b/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json index 342d58df90..700ac077c0 100644 --- a/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json +++ b/tests/api/expected-outputs/augsburg_de_children_archived_descendants.json @@ -88,8 +88,8 @@ "title": "Gesundheitsamt", "modified_gmt": "2022-01-16T16:41:33.158Z", "last_updated": "2022-01-16T17:41:33.158+01:00", - "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n", - "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", + "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n", + "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", "parent": { "id": 15, "url": "http://localhost:8000/augsburg/de/behörden-und-beratung/behörden/", diff --git a/tests/api/expected-outputs/augsburg_de_children_depth_2.json b/tests/api/expected-outputs/augsburg_de_children_depth_2.json index 0b4f3763e9..ad49854a56 100644 --- a/tests/api/expected-outputs/augsburg_de_children_depth_2.json +++ b/tests/api/expected-outputs/augsburg_de_children_depth_2.json @@ -565,5 +565,26 @@ "organization": null, "embedded_offers": [], "hash": null + }, + { + "id": 101, + "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/", + "path": "/augsburg/de/wichtige-kontakte/", + "title": "Wichtige Kontakte", + "modified_gmt": "2025-01-11T07:07:46.642Z", + "last_updated": "2025-01-11T08:07:46.642+01:00", + "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n", + "content": "
Contact\r\n

referred to in a page | Testkontakt

\r\n

\"Address:Viktoriastraße 1, 86150 Augsburg

\r\n
", + "parent": { + "id": 0, + "url": null, + "path": null + }, + "order": 11, + "available_languages": {}, + "thumbnail": null, + "organization": null, + "hash": null, + "embedded_offers": [] } ] diff --git a/tests/api/expected-outputs/augsburg_de_pages.json b/tests/api/expected-outputs/augsburg_de_pages.json index f2e9a0f2f9..0fb614890e 100644 --- a/tests/api/expected-outputs/augsburg_de_pages.json +++ b/tests/api/expected-outputs/augsburg_de_pages.json @@ -311,7 +311,10 @@ "alias": "zammad-formular", "url": "", "thumbnail": "", - "post": { "form-id": "example", "zammad-url": "https://zammad.example.com" } + "post": { + "form-id": "example", + "zammad-url": "https://zammad.example.com" + } } ], "hash": null @@ -323,8 +326,8 @@ "title": "Gesundheitsamt", "modified_gmt": "2022-01-16T16:41:33.158Z", "last_updated": "2022-01-16T17:41:33.158+01:00", - "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n", - "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", + "excerpt": "Leistungen des Gesundheitsamtes:\r\n\r\nBeratung zur Gesundheitsfragen\r\nBeratung für Menschen, die an einer psychischen Krankheit leiden\r\nKontaktstelle für Selbsthilfegruppen Infos auch unter:www.augsburg.de/selbsthilfe-schwaben\r\nBeratung für Schwangere\r\nImpfberatung\r\nBelehrung nach Infektionsschutzgesetz § 43 (Lebensmittelbelehrung, Gesundheitszeugnis)\r\nUntersuchung für Kinder, bevor sie in die Schule gehen können (Schuleingangsuntersuchung)\r\nErstuntersuchung von unbegleiteten minderjährigen Ausländerinnen und Ausländern auf Veranlassung des AKJF\r\nErstuntersuchung nach amtlicher Vorladung im Bereich Migration und Asyl\r\nMeldepflichtige Krankheiten (Tuberkulose, Gelbsucht etc.)\r\nTuberkulosefürsorge\r\nGesundheitsberatung und Untersuchung für Prostituierte\r\nanonyme AIDS-Beratung / HIV-Test\r\n\r\n", + "content": "

Leistungen des Gesundheitsamtes:

\r\n\r\n
", "parent": { "id": 15, "url": "http://localhost:8000/augsburg/de/behörden-und-beratung/behörden/", @@ -647,5 +650,26 @@ "organization": null, "embedded_offers": [], "hash": null + }, + { + "id": 101, + "url": "http://localhost:8000/augsburg/de/wichtige-kontakte/", + "path": "/augsburg/de/wichtige-kontakte/", + "title": "Wichtige Kontakte", + "modified_gmt": "2025-01-11T07:07:46.642Z", + "last_updated": "2025-01-11T08:07:46.642+01:00", + "excerpt": "Contact\r\nreferred to in a page | Testkontakt\r\nViktoriastraße 1, 86150 Augsburg\r\n", + "content": "
Contact\r\n

referred to in a page | Testkontakt

\r\n

\"Address:Viktoriastraße 1, 86150 Augsburg

\r\n
", + "parent": { + "id": 0, + "url": null, + "path": null + }, + "order": 11, + "available_languages": {}, + "thumbnail": null, + "organization": null, + "hash": null, + "embedded_offers": [] } ] diff --git a/tests/cms/models/contacts/test_contacts.py b/tests/cms/models/contacts/test_contacts.py index 1793d314d6..d4e26cf784 100644 --- a/tests/cms/models/contacts/test_contacts.py +++ b/tests/cms/models/contacts/test_contacts.py @@ -40,37 +40,37 @@ def test_contact_string( def test_copying_contact_works( load_test_data: None, ) -> None: - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 contact = Contact.objects.get(id=1) contact.copy() - assert Contact.objects.all().count() == 5 + assert Contact.objects.all().count() == 6 @pytest.mark.django_db def test_deleting_contact_works( load_test_data: None, ) -> None: - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 contact = Contact.objects.get(id=1) contact.delete() - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 @pytest.mark.django_db def test_archiving_contact_works( load_test_data: None, ) -> None: - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 contact = Contact.objects.get(id=1) assert contact.archived is False contact.archive() - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 assert contact.archived is True @@ -78,11 +78,11 @@ def test_archiving_contact_works( def test_restoring_contact_works( load_test_data: None, ) -> None: - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 contact = Contact.objects.get(id=2) assert contact.archived is True contact.restore() - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 assert contact.archived is False diff --git a/tests/cms/test_duplicate_regions.py b/tests/cms/test_duplicate_regions.py index d9707b929e..77133266ee 100644 --- a/tests/cms/test_duplicate_regions.py +++ b/tests/cms/test_duplicate_regions.py @@ -118,23 +118,23 @@ def test_duplicate_regions( # Check if links exist assert get_url_count(source_region.slug) == { - "number_all_urls": 17, + "number_all_urls": 18, "number_email_urls": 0, "number_ignored_urls": 1, "number_invalid_urls": 4, "number_phone_urls": 0, "number_unchecked_urls": 0, - "number_valid_urls": 12, + "number_valid_urls": 13, }, "Links should exist in the source region" # Check if links have been cloned (except ignored ones) assert get_url_count(target_region.slug) == { - "number_all_urls": 17, + "number_all_urls": 20, "number_email_urls": 0, "number_ignored_urls": 0, - "number_invalid_urls": 9, + "number_invalid_urls": 10, "number_phone_urls": 0, "number_unchecked_urls": 0, - "number_valid_urls": 8, + "number_valid_urls": 10, }, "Links should be cloned into the new region" # Check if internal links have been cloned diff --git a/tests/cms/views/contacts/test_contact_actions.py b/tests/cms/views/contacts/test_contact_actions.py index 8197201678..c77bddc8bf 100644 --- a/tests/cms/views/contacts/test_contact_actions.py +++ b/tests/cms/views/contacts/test_contact_actions.py @@ -18,11 +18,12 @@ # Use the region Augsburg, as it has some contacts in the test data REGION_SLUG = "augsburg" +USED_CONTACT_ID = 5 NOT_USED_CONTACT_ID = 3 ARCHIVED_CONTACT_ID = 2 -test_archive_parameters = [(NOT_USED_CONTACT_ID, True)] +test_archive_parameters = [(NOT_USED_CONTACT_ID, True), (USED_CONTACT_ID, False)] @pytest.mark.django_db @@ -68,14 +69,14 @@ def test_archive_contact( ).content.decode("utf-8") assert Contact.objects.filter(id=contact_id).first().archived else: - # To be adjusted after #3282 assert_message_in_log( - "ERROR Contact couldn't be archived as it's used in a content", + f'ERROR Cannot archive contact "{contact_string}" while content objects refer to it.', caplog, ) - assert ( - "Contact couldn't be archived as it's used in a content" - in client.get(redirect_url).content.decode("utf-8") + assert f"Cannot archive contact "{contact_string}" while content objects refer to it." in client.get( + redirect_url + ).content.decode( + "utf-8" ) assert not Contact.objects.filter(id=contact_id).first().archived elif role == ANONYMOUS: @@ -88,7 +89,7 @@ def test_archive_contact( assert response.status_code == 403 -test_delete_parameters = [(NOT_USED_CONTACT_ID, True)] +test_delete_parameters = [(NOT_USED_CONTACT_ID, True), (USED_CONTACT_ID, False)] @pytest.mark.django_db @@ -134,14 +135,14 @@ def test_delete_contact( ).content.decode("utf-8") assert not Contact.objects.filter(id=contact_id).first() else: - # To be adjusted after #3282 assert_message_in_log( - "ERROR Contact couldn't be archived as it's used in a content", + f'ERROR Cannot delete contact "{contact_string}" while content objects refer to it.', caplog, ) - assert ( - "Contact couldn't be archived as it's used in a content" - in client.get(redirect_url).content.decode("utf-8") + assert f"Cannot delete contact "{contact_string}" while content objects refer to it." in client.get( + redirect_url + ).content.decode( + "utf-8" ) assert Contact.objects.filter(id=contact_id).first() elif role == ANONYMOUS: @@ -184,7 +185,7 @@ def test_restore_contact( response = client.post(restore_contact) if role in HIGH_PRIV_STAFF_ROLES: - response.status_code == 302 + assert response.status_code == 302 redirect_url = response.headers.get("location") assert_message_in_log( f"SUCCESS Contact {contact_string} was successfully restored", @@ -205,7 +206,7 @@ def test_restore_contact( assert response.status_code == 403 -BULK_ARCHIVE_SELECTED_IDS = [NOT_USED_CONTACT_ID] +BULK_ARCHIVE_SELECTED_IDS = [NOT_USED_CONTACT_ID, USED_CONTACT_ID] @pytest.mark.django_db @@ -226,6 +227,7 @@ def test_bulk_archive_contacts( not_used_contact_string = str( Contact.objects.filter(id=NOT_USED_CONTACT_ID).first() ) + used_contact_string = str(Contact.objects.filter(id=USED_CONTACT_ID).first()) bulk_archive_contacts = reverse( "bulk_archive_contacts", @@ -239,25 +241,18 @@ def test_bulk_archive_contacts( ) if role in HIGH_PRIV_STAFF_ROLES: - response.status_code == 302 + assert response.status_code == 302 redirect_url = response.headers.get("location") redirect_page = client.get(redirect_url).content.decode("utf-8") - # To be adjusted after #3282 - """ assert_message_in_log( - "ERROR could not be archived", + f'ERROR Contact "{used_contact_string}" cannot be archived while content objects refer to it.', caplog, ) assert ( - " could not be archived" + f"Contact "{used_contact_string}" cannot be archived while content objects refer to it." in redirect_page ) - assert ( - not Contact.objects.filter(id=USED_CONTACT_ID) - .first() - .archived - ) - """ + assert not Contact.objects.filter(id=USED_CONTACT_ID).first().archived assert_message_in_log( f'SUCCESS Contact "{not_used_contact_string}" was successfully archived.', caplog, @@ -277,7 +272,7 @@ def test_bulk_archive_contacts( assert response.status_code == 403 -BULK_DELETE_SELECTED_IDS = [NOT_USED_CONTACT_ID] +BULK_DELETE_SELECTED_IDS = [NOT_USED_CONTACT_ID, USED_CONTACT_ID] @pytest.mark.django_db @@ -298,6 +293,7 @@ def test_bulk_delete_contacts( not_used_contact_string = str( Contact.objects.filter(id=NOT_USED_CONTACT_ID).first() ) + used_contact_string = str(Contact.objects.filter(id=USED_CONTACT_ID).first()) bulk_delete_contacts = reverse( "bulk_delete_contacts", @@ -311,25 +307,18 @@ def test_bulk_delete_contacts( ) if role in HIGH_PRIV_STAFF_ROLES: - response.status_code == 302 + assert response.status_code == 302 redirect_url = response.headers.get("location") redirect_page = client.get(redirect_url).content.decode("utf-8") - # To be adjusted after #3282 - """ assert_message_in_log( - "ERROR could not be archived", + f'ERROR Contact "{used_contact_string}" cannot be deleted while content objects refer to it.', caplog, ) assert ( - " could not be archived" + f"Contact "{used_contact_string}" cannot be deleted while content objects refer to it." in redirect_page ) - assert ( - not Contact.objects.filter(id=USED_CONTACT_ID) - .first() - .archived - ) - """ + assert Contact.objects.filter(id=USED_CONTACT_ID).first() assert_message_in_log( f'SUCCESS Contact "{not_used_contact_string}" was successfully deleted.', caplog, @@ -381,7 +370,7 @@ def test_bulk_restore_contacts( ) if role in HIGH_PRIV_STAFF_ROLES: - response.status_code == 302 + assert response.status_code == 302 redirect_url = response.headers.get("location") redirect_page = client.get(redirect_url).content.decode("utf-8") diff --git a/tests/cms/views/poi/test_poi_form.py b/tests/cms/views/poi/test_poi_form.py index c0e7bf6e4e..8b28995b95 100644 --- a/tests/cms/views/poi/test_poi_form.py +++ b/tests/cms/views/poi/test_poi_form.py @@ -227,7 +227,7 @@ def test_poi_form_shows_associated_contacts( settings: SettingsWrapper, ) -> None: """ - POI "Draft location" (id=6) has three related contacts. Test whether they are shown in the POI form. + POI "Draft location" (id=6) has four related contacts. Test whether they are shown in the POI form. """ client, role = login_role_user diff --git a/tests/pdf/files/429b3fbce4/Integreat - Deutsch - Augsburg.pdf b/tests/pdf/files/89219cf973/Integreat - Deutsch - Augsburg.pdf similarity index 81% rename from tests/pdf/files/429b3fbce4/Integreat - Deutsch - Augsburg.pdf rename to tests/pdf/files/89219cf973/Integreat - Deutsch - Augsburg.pdf index e0f1afde2c6ce386bebee7f045da1060422f6a16..62974e6db919a53c8ac41da3a4c0ff3784f74cc8 100644 GIT binary patch delta 16544 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 diff --git a/tests/pdf/test_pdf_export.py b/tests/pdf/test_pdf_export.py index 080e923207..faeb5d8e55 100644 --- a/tests/pdf/test_pdf_export.py +++ b/tests/pdf/test_pdf_export.py @@ -23,9 +23,9 @@ ), ( "de", - [1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 26, 27, 28], + [1, 2, 3, 4, 5, 6, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 26, 27, 28, 31], "", - "429b3fbce4/Integreat - Deutsch - Augsburg.pdf", + "89219cf973/Integreat - Deutsch - Augsburg.pdf", ), ( "en", diff --git a/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml b/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml index 99ec896016..cc8405e22c 100644 --- a/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml +++ b/tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml @@ -1,4 +1,4 @@ -https://integreat.app/augsburg/de/willkommen/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/willkommen-in-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/uber-die-app-integreat-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/wissenswertes-uber-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/stadtplan/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/kontakt-zu-app-team-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/ausländerbehörde/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/gesundheitsamt/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/deutsch-selber-lernen/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sprachkurse/2022-01-16monthly1.0https://integreat.app/augsburg/de/alltag/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sonstige-sprachlernangebote/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/dolmetschen-und-übersetzen/2022-01-16monthly1.0https://integreat.app/augsburg/de/test-hidden-language/2022-11-30monthly1.0https://integreat.app/augsburg/de/test-links/2023-08-18monthly1.0https://integreat.app/augsburg/de/jetzt-google-translate-ist-da/2022-12-30monthly1.0https://integreat.app/augsburg/de/locations/test-ort/2020-01-22monthly0.5https://integreat.app/augsburg/de/offers/ihk-lehrstellenboerse2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/lehrstellen-radar2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/sprungbrett2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/zammad-formular2023-10-09monthly1.0https://integreat.app/augsburg/de/events/test-veranstaltung/2020-01-22daily0.5 +https://integreat.app/augsburg/de/willkommen/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/willkommen-in-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/uber-die-app-integreat-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/wissenswertes-uber-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/stadtplan/2019-08-12monthly1.0https://integreat.app/augsburg/de/willkommen/kontakt-zu-app-team-augsburg/2019-08-12monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/ausländerbehörde/2022-01-16monthly1.0https://integreat.app/augsburg/de/behörden-und-beratung/behörden/gesundheitsamt/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/deutsch-selber-lernen/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sprachkurse/2022-01-16monthly1.0https://integreat.app/augsburg/de/alltag/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/sonstige-sprachlernangebote/2022-01-16monthly1.0https://integreat.app/augsburg/de/deutsche-sprache/dolmetschen-und-übersetzen/2022-01-16monthly1.0https://integreat.app/augsburg/de/test-hidden-language/2022-11-30monthly1.0https://integreat.app/augsburg/de/test-links/2023-08-18monthly1.0https://integreat.app/augsburg/de/jetzt-google-translate-ist-da/2022-12-30monthly1.0https://integreat.app/augsburg/de/wichtige-kontakte/2025-01-11monthly1.0https://integreat.app/augsburg/de/locations/test-ort/2020-01-22monthly0.5https://integreat.app/augsburg/de/offers/ihk-lehrstellenboerse2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/lehrstellen-radar2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/sprungbrett2019-10-09monthly1.0https://integreat.app/augsburg/de/offers/zammad-formular2023-10-09monthly1.0https://integreat.app/augsburg/de/events/test-veranstaltung/2020-01-22daily0.5 diff --git a/tests/sitemap/sitemap_config.py b/tests/sitemap/sitemap_config.py index 1be0d263fc..7ee1433a94 100644 --- a/tests/sitemap/sitemap_config.py +++ b/tests/sitemap/sitemap_config.py @@ -19,7 +19,7 @@ ( "/augsburg/de/sitemap.xml", "tests/sitemap/expected-sitemaps/sitemap-augsburg-de.xml", - 147, + 152, ), ( "/augsburg/en/sitemap.xml",