diff --git a/integreat_cms/cms/fixtures/test_data.json b/integreat_cms/cms/fixtures/test_data.json index 6e430e1b86..9b2483dcf7 100644 --- a/integreat_cms/cms/fixtures/test_data.json +++ b/integreat_cms/cms/fixtures/test_data.json @@ -997,6 +997,21 @@ "created_date": "2024-08-06T13:23:45.256Z" } }, + { + "model": "cms.contact", + "pk": 4, + "fields": { + "title": "", + "name": "", + "location": 6, + "email": "generalcontactinformation@example.com", + "phone_number": "0123456789", + "website": "https://integreat-app.de/", + "archived": false, + "last_updated": "2024-08-06T13:23:45.256Z", + "created_date": "2024-08-06T13:23:45.256Z" + } + }, { "model": "cms.recurrencerule", "pk": 1, diff --git a/integreat_cms/cms/models/contact/contact.py b/integreat_cms/cms/models/contact/contact.py index 680155840a..069ec712f0 100644 --- a/integreat_cms/cms/models/contact/contact.py +++ b/integreat_cms/cms/models/contact/contact.py @@ -2,6 +2,7 @@ from django.db.models import Q from django.utils import timezone from django.utils.functional import cached_property +from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from ..abstract_base_model import AbstractBaseModel @@ -61,7 +62,14 @@ def __str__(self) -> str: :return: A readable string representation of the contact """ - return f"{self.title} {self.name}" + label = ( + f"{self.title} {self.name}" + if self.title and self.name + else gettext("General contact information") + ) + if self.archived: + label += " (⚠ " + gettext("Archived") + ")" + return label def get_repr(self) -> str: """ diff --git a/integreat_cms/cms/templates/pois/poi_form.html b/integreat_cms/cms/templates/pois/poi_form.html index e06df4ab7b..e8d38faf3a 100644 --- a/integreat_cms/cms/templates/pois/poi_form.html +++ b/integreat_cms/cms/templates/pois/poi_form.html @@ -167,6 +167,9 @@ <h3 class="font-bold text-black heading"> <div id="right-sidebar-column" class="flex flex-col flex-wrap 3xl:col-end-3 4xl:col-end-auto md:w-full"> {% include "./poi_form_sidebar/contact_box.html" with box_id="poi-contact" %} + {% if poi_form.instance.id and perms.cms.view_contact %} + {% include "./poi_form_sidebar/related_contacts_box.html" with box_id="poi-related-contacts" %} + {% endif %} {% include "./poi_form_sidebar/opening_hours_box.html" with box_id="poi-opening-hours" no_padding=True %} {% include "./poi_form_sidebar/category_box.html" with box_id="poi-category" %} {% include "./poi_form_sidebar/icon_box.html" with box_id="poi-icon" %} diff --git a/integreat_cms/cms/templates/pois/poi_form_sidebar/action_box.html b/integreat_cms/cms/templates/pois/poi_form_sidebar/action_box.html index fe115284bb..f4b4aec7c2 100644 --- a/integreat_cms/cms/templates/pois/poi_form_sidebar/action_box.html +++ b/integreat_cms/cms/templates/pois/poi_form_sidebar/action_box.html @@ -68,7 +68,7 @@ {% for contact in poi_form.instance.contacts.all %} <a href="{% url 'edit_contact' contact_id=contact.id region_slug=request.region.slug %}" class="block pt-2 hover:underline"> - <i icon-name="pen-square" class="mr-2"></i> {{ contact.title }} {{ contact.name }} + <i icon-name="pen-square" class="mr-2"></i> {{ contact }} </a> {% endfor %} </div> @@ -130,7 +130,7 @@ {% for contact in poi_form.instance.contacts.all %} <a href="{% url 'edit_contact' contact_id=contact.id region_slug=request.region.slug %}" class="block pt-2 hover:underline"> - <i icon-name="pen-square" class="mr-2"></i> {{ contact.title }} {{ contact.name }} + <i icon-name="pen-square" class="mr-2"></i> {{ contact }} </a> {% endfor %} </div> diff --git a/integreat_cms/cms/templates/pois/poi_form_sidebar/related_contacts_box.html b/integreat_cms/cms/templates/pois/poi_form_sidebar/related_contacts_box.html new file mode 100644 index 0000000000..cd8022ef9e --- /dev/null +++ b/integreat_cms/cms/templates/pois/poi_form_sidebar/related_contacts_box.html @@ -0,0 +1,28 @@ +{% extends "../../_collapsible_box.html" %} +{% load i18n %} +{% load widget_tweaks %} +{% block collapsible_box_icon %} + message-square +{% endblock collapsible_box_icon %} +{% block collapsible_box_title %} + {% trans "Related contacts" %} +{% endblock collapsible_box_title %} +{% block collapsible_box_content %} + {% with poi_form.instance.contacts.all as contacts %} + <div> + <div class="help-text"> + {% if contacts %} + {% trans "This location is currently referred to in those contacts." %} + {% else %} + {% trans "This location is not currently referred to in any contact." %} + {% endif %} + </div> + {% for contact in contacts %} + <a href="{% url 'edit_contact' contact_id=contact.id region_slug=request.region.slug %}" + class="block pt-2 hover:underline"> + <i icon-name="pen-square" class="mr-2"></i> {{ contact }} + </a> + {% endfor %} + </div> + {% endwith %} +{% endblock collapsible_box_content %} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 24990687b6..ed27a236a4 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -1640,9 +1640,9 @@ msgstr "Aktiv" msgid "Hidden" msgstr "Versteckt" -#: cms/constants/region_status.py cms/models/pages/page_translation.py -#: cms/models/regions/region.py cms/templates/events/event_form.html -#: cms/templates/pages/page_form.html +#: cms/constants/region_status.py cms/models/contact/contact.py +#: cms/models/pages/page_translation.py cms/models/regions/region.py +#: cms/templates/events/event_form.html cms/templates/pages/page_form.html #: cms/templates/pages/page_tree_archived_node.html #: cms/templates/pois/poi_form.html msgid "Archived" @@ -2742,6 +2742,10 @@ msgstr "archiviert" msgid "Whether or not the location is read-only and hidden in the API." msgstr "Ob der Ort schreibgeschützt und in der API verborgen ist oder nicht." +#: cms/models/contact/contact.py +msgid "General contact information" +msgstr "Allgemeine Kontaktinformationen" + #: cms/models/contact/contact.py msgid "(Copy)" msgstr "(Kopie)" @@ -7669,6 +7673,18 @@ msgstr "Nur Koordinaten übernehmen" msgid "Please choose one option or cancel" msgstr "Bitte eine Option auswählen oder abbrechen" +#: cms/templates/pois/poi_form_sidebar/related_contacts_box.html +msgid "Related contacts" +msgstr "Zugehörige Kontakte" + +#: cms/templates/pois/poi_form_sidebar/related_contacts_box.html +msgid "This location is currently referred to in those contacts." +msgstr "Dieser Ort wird in folgenden Kontakten verwendet." + +#: cms/templates/pois/poi_form_sidebar/related_contacts_box.html +msgid "This location is not currently referred to in any contact." +msgstr "Zur Zeit gibt es keine Kontakte zu diesem Ort." + #: cms/templates/pois/poi_list.html cms/templates/pois/poi_list_archived.html msgid "Archived locations" msgstr "Archivierte Orte" diff --git a/tests/cms/views/contacts/test_contact_actions.py b/tests/cms/views/contacts/test_contact_actions.py index 2cbb3e91e7..2b8810a31e 100644 --- a/tests/cms/views/contacts/test_contact_actions.py +++ b/tests/cms/views/contacts/test_contact_actions.py @@ -9,12 +9,12 @@ def test_copying_contact_works( load_test_data: None, login_role_user: tuple[Client, str], ) -> None: - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 contact = Contact.objects.get(id=1) contact.copy() - assert Contact.objects.all().count() == 4 + assert Contact.objects.all().count() == 5 @pytest.mark.django_db @@ -22,12 +22,12 @@ def test_deleting_contact_works( load_test_data: None, login_role_user: tuple[Client, str], ) -> None: - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 contact = Contact.objects.get(id=1) contact.delete() - assert Contact.objects.all().count() == 2 + assert Contact.objects.all().count() == 3 @pytest.mark.django_db @@ -35,13 +35,13 @@ def test_archiving_contact_works( load_test_data: None, login_role_user: tuple[Client, str], ) -> None: - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 contact = Contact.objects.get(id=1) assert contact.archived is False contact.archive() - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 assert contact.archived is True @@ -50,11 +50,11 @@ def test_restoring_contact_works( load_test_data: None, login_role_user: tuple[Client, str], ) -> None: - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 contact = Contact.objects.get(id=2) assert contact.archived is True contact.restore() - assert Contact.objects.all().count() == 3 + assert Contact.objects.all().count() == 4 assert contact.archived is False diff --git a/tests/cms/views/poi/test_poi_form.py b/tests/cms/views/poi/test_poi_form.py index 41d6411616..717e02ad9f 100644 --- a/tests/cms/views/poi/test_poi_form.py +++ b/tests/cms/views/poi/test_poi_form.py @@ -218,3 +218,106 @@ def test_poi_in_use_not_bulk_archived( # Check the POI is not archived assert not POI.objects.filter(id=poi_id).first().archived + + +@pytest.mark.django_db +def test_poi_form_shows_associated_contacts( + load_test_data: None, + login_role_user: tuple[Client, str], + settings: SettingsWrapper, +) -> None: + """ + POI "Draft location" (id=6) has three related contacts. Test whether they are shown in the POI form. + """ + client, role = login_role_user + + # Choose a POI which has related contacts + POI_ID = 6 + + # Set the language setting to English so assertion does not fail because of corresponding German sentence appearing instead the english one. + settings.LANGUAGE_CODE = "en" + + poi = POI.objects.filter(id=POI_ID).first() + related_contacts = list(poi.contacts.all()) + + assert len(related_contacts) > 0 + + edit_poi = reverse( + "edit_poi", + kwargs={ + "poi_id": poi.id, + "region_slug": poi.region.slug, + "language_slug": poi.region.default_language.slug, + }, + ) + response = client.get(edit_poi) + + if role == ANONYMOUS: + assert response.status_code == 302 + assert ( + response.headers.get("location") == f"{settings.LOGIN_URL}?next={edit_poi}" + ) + # probably needs adjustment after #2958 + elif role in HIGH_PRIV_STAFF_ROLES: + for contact in related_contacts: + if contact.title and contact.name: + assert f"{contact.title} {contact.name}" in response.content.decode( + "utf-8" + ) + else: + assert "General contact information" in response.content.decode("utf-8") + else: + assert ( + "This location is currently referred to in those contacts." + not in response.content.decode("utf-8") + ) + + +@pytest.mark.django_db +def test_poi_form_shows_no_associated_contacts( + load_test_data: None, + login_role_user: tuple[Client, str], + settings: SettingsWrapper, +) -> None: + """ + POI "Test location" (id=4) has no related contacts. Test whether the correct message is shown in the POi form. + """ + client, role = login_role_user + + # Choose a POI which has related contacts + POI_ID = 4 + + # Set the language setting to English so assertion does not fail because of corresponding German sentence appearing instead the english one. + settings.LANGUAGE_CODE = "en" + + poi = POI.objects.filter(id=POI_ID).first() + related_contacts = list(poi.contacts.all()) + + assert len(related_contacts) == 0 + + edit_poi = reverse( + "edit_poi", + kwargs={ + "poi_id": poi.id, + "region_slug": poi.region.slug, + "language_slug": poi.region.default_language.slug, + }, + ) + response = client.get(edit_poi) + + if role == ANONYMOUS: + assert response.status_code == 302 + assert ( + response.headers.get("location") == f"{settings.LOGIN_URL}?next={edit_poi}" + ) + # probably needs adjustment after #2958 + elif role in HIGH_PRIV_STAFF_ROLES: + assert ( + "This location is not currently referred to in any contact." + in response.content.decode("utf-8") + ) + else: + assert ( + "This location is not currently referred to in any contact." + not in response.content.decode("utf-8") + )