diff --git a/.circleci/config.yml b/.circleci/config.yml index bf5641cd45..38d5451577 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ jobs: steps: - checkout - restore_cache: - key: pipenv-{{ checksum "Pipfile.lock" }}-v1 + key: pipenv-{{ checksum "Pipfile.lock" }}-v2 - run: name: Install pip dependencies command: | @@ -26,7 +26,7 @@ jobs: fi fi - save_cache: - key: pipenv-{{ checksum "Pipfile.lock" }}-v1 + key: pipenv-{{ checksum "Pipfile.lock" }}-v2 paths: - .venv - integreat_cms.egg-info @@ -97,7 +97,7 @@ jobs: - checkout - restore_cache: keys: - - npm-{{ checksum "package-lock.json" }}-v1 + - npm-{{ checksum "package-lock.json" }}-v2 - run: name: Install npm dependencies command: | @@ -112,7 +112,7 @@ jobs: fi fi - save_cache: - key: npm-{{ checksum "package-lock.json" }}-v1 + key: npm-{{ checksum "package-lock.json" }}-v2 paths: - node_modules - run: diff --git a/CHANGELOG.md b/CHANGELOG.md index fffd13b469..5a9efc0a14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ UNRELEASED * [ [#1710](https://github.com/digitalfabrik/integreat-cms/issues/1710) ] Add spacing to sidebar to improve view on small screens * [ [#1526](https://github.com/digitalfabrik/integreat-cms/issues/1526)] Fix sending push notifications in one language * [ [#1630](https://github.com/digitalfabrik/integreat-cms/issues/1630)] Fix not recognized sent status of push notifications +* [ [#1683](https://github.com/digitalfabrik/integreat-cms/issues/1683) ] Improve organization management 2022.10.0 diff --git a/integreat_cms/cms/constants/roles.py b/integreat_cms/cms/constants/roles.py index 54993227c0..da143cfad1 100644 --- a/integreat_cms/cms/constants/roles.py +++ b/integreat_cms/cms/constants/roles.py @@ -73,16 +73,19 @@ MANAGEMENT_PERMISSIONS = EDITOR_PERMISSIONS + [ "change_feedback", "change_imprintpage", + "change_organization", "change_pushnotification", "change_user", "change_chatmessage", "delete_directory", "delete_feedback", "delete_mediafile", + "delete_organization", "grant_page_permissions", "send_push_notification", "view_feedback", "view_imprintpage", + "view_organization", "view_pushnotification", "view_user", ] @@ -142,6 +145,7 @@ "delete_imprintpage", "delete_languagetreenode", "delete_offertemplate", + "delete_organization", "delete_page", "delete_poi", "delete_poicategory", diff --git a/integreat_cms/cms/forms/organizations/organization_form.py b/integreat_cms/cms/forms/organizations/organization_form.py index bd3024a0d7..96e0fb5103 100644 --- a/integreat_cms/cms/forms/organizations/organization_form.py +++ b/integreat_cms/cms/forms/organizations/organization_form.py @@ -1,6 +1,14 @@ +import logging +from django import forms +from django.utils.translation import ugettext_lazy as _ + from ...models import Organization from ..icon_widget import IconWidget from ..custom_model_form import CustomModelForm +from ...utils.slug_utils import generate_unique_slug_helper + + +logger = logging.getLogger(__name__) class OrganizationForm(CustomModelForm): @@ -26,3 +34,46 @@ class Meta: widgets = { "icon": IconWidget(), } + + def __init__(self, **kwargs): + r""" + Initialize organization form + + :param \**kwargs: The supplied keyword arguments + :type \**kwargs: dict + """ + super().__init__(**kwargs) + self.fields["slug"].required = False + + def clean_slug(self): + """ + Validate the slug field (see :ref:`overriding-modelform-clean-method`) + + :return: A unique slug based on the input value + :rtype: str + """ + return generate_unique_slug_helper(self, "organization") + + def clean_name(self): + """ + Validate if form fields name is not already in use for another organization in the same region + (see :ref:`overriding-modelform-clean-method`) + :return: The name which is unique per region + :rtype: str + """ + cleaned_name = self.cleaned_data["name"] + if ( + Organization.objects.exclude(id=self.instance.id) + .filter(region=self.instance.region, name=cleaned_name) + .exists() + ): + self.add_error( + "name", + forms.ValidationError( + _( + "An organization with the same name already exists in this region. Please choose another name." + ), + code="invalid", + ), + ) + return cleaned_name diff --git a/integreat_cms/cms/migrations/0043_update_organization_permissions.py b/integreat_cms/cms/migrations/0043_update_organization_permissions.py new file mode 100644 index 0000000000..c451657910 --- /dev/null +++ b/integreat_cms/cms/migrations/0043_update_organization_permissions.py @@ -0,0 +1,44 @@ +# Generated by Django 3.2.11 on 2022-05-18 00:05 +from django.db import migrations +from ..constants import roles + + +# pylint: disable=unused-argument +def update_roles(apps, schema_editor): + """ + Update the role definitions + + :param apps: The configuration of installed applications + :type apps: ~django.apps.registry.Apps + + :param schema_editor: The database abstraction layer that creates actual SQL code + :type schema_editor: ~django.db.backends.base.schema.BaseDatabaseSchemaEditor + """ + # We can't import the Person model directly as it may be a newer + # version than this migration expects. We use the historical version. + Group = apps.get_model("auth", "Group") + Permission = apps.get_model("auth", "Permission") + + # Assign the correct permissions + for role_name in dict(roles.CHOICES): + group = Group.objects.filter(name=role_name).first() + # Clear permissions + group.permissions.clear() + # Set permissions + group.permissions.add( + *Permission.objects.filter(codename__in=roles.PERMISSIONS[role_name]) + ) + + +class Migration(migrations.Migration): + """ + Migration file to update the role definitions + """ + + dependencies = [ + ("cms", "0042_alter_pushnotificationtranslation_title"), + ] + + operations = [ + migrations.RunPython(update_roles, migrations.RunPython.noop), + ] diff --git a/integreat_cms/cms/models/users/organization.py b/integreat_cms/cms/models/users/organization.py index 9d945b4de0..728b771c20 100644 --- a/integreat_cms/cms/models/users/organization.py +++ b/integreat_cms/cms/models/users/organization.py @@ -61,6 +61,23 @@ def get_repr(self): """ return f"" + @property + def num_pages(self): + """ + + :return: the current number of maintained pages of an organization object + :rtype: int + """ + return self.pages.count() + + @property + def num_members(self): + """ + :return: the current number of members of an organization object + :rtype: int + """ + return self.members.count() + class Meta: #: The verbose name of the model verbose_name = _("organization") diff --git a/integreat_cms/cms/templates/organizations/organization_form.html b/integreat_cms/cms/templates/organizations/organization_form.html index 02f42a6fed..0d1e9cecea 100644 --- a/integreat_cms/cms/templates/organizations/organization_form.html +++ b/integreat_cms/cms/templates/organizations/organization_form.html @@ -1,8 +1,12 @@ {% extends "_base.html" %} {% load i18n %} -{% block content %} {% load static %} {% load widget_tweaks %} +{% load content_filters %} +{% load page_filters %} +{% load tree_filters %} + +{% block content %}
{% csrf_token %}
@@ -15,9 +19,25 @@

{% trans 'Create new organization' %} {% endif %}

- {% if perms.cms.change_organization %} - - {% endif %} +
+ {% if form.instance.id and perms.cms.delete_organization %} +
+ +
+ {% endif %} + {% if perms.cms.change_organization %} +
+ +
+ {% endif %} +
@@ -28,9 +48,6 @@

{% render_field form.name|add_error_class:"border-red-500" %} - -
{{ form.slug.help_text }}
- {% render_field form.slug|add_error_class:"border-red-500" %}

@@ -43,20 +60,136 @@

- {% if form.instance.id and perms.cms.delete_organization %} -
- + {% if form.instance.id %} + + {% get_current_language as LANGUAGE_CODE %} + {% get_language LANGUAGE_CODE as backend_language %} + {% with request.region.default_language as region_default_language %} +
+
+
+

+ + {% trans "Pages" %} +

+
+
+ + + + + + + + + {% if region_default_language != backend_language %} + + {% endif %} + + + + + + {% for page in form.instance.pages.all %} + {% get_translation page region_default_language.slug as page_translation %} + {% get_translation page backend_language.slug as backendlang_page_translation %} + + {% if page_translation %} + + {% else %} + + {% endif %} + + {% if region_default_language != backend_language %} + {% if backendlang_page_translation %} + + {% else %} + + {% endif %} + {% endif%} + + {% empty %} + + + + + {% endfor %} + +
+ {% trans "Name in " %} {{ region_default_language.translated_name }} + + {% trans "Name in "%} {{ backend_language.translated_name }} +
+ + {{ page_translation.title }} + + + {% trans 'Translation not available' %} + + + + {{ backendlang_page_translation.title }} + + + + + {% trans 'Translation not available' %} + +
+ {% trans "This organization currently has no maintained pages." %} +
+
+
+ {% endwith %} +
+
+

+ + {% trans "Members" %} +

+
+
+ + + + + + + + + + {% for member in form.instance.members.all %} + + + + + + {% empty %} + + + + + {% endfor %} + + +
+ {% trans "Username" %} + + {% trans "Role" %} +
+ {{ member.username }} + + {{ member.role }} +
+ {% trans "This organization currently has no members." %} +
+
+
{% endif %} + {% include "generic_confirmation_dialog.html" %} {{ media_config_data|json_script:"media_config_data" }} diff --git a/integreat_cms/cms/templates/organizations/organization_list.html b/integreat_cms/cms/templates/organizations/organization_list.html index 293d8add36..2a83dcae8d 100644 --- a/integreat_cms/cms/templates/organizations/organization_list.html +++ b/integreat_cms/cms/templates/organizations/organization_list.html @@ -18,7 +18,8 @@

{% trans 'Manage Organizations' %}

{% trans 'Name' %} - {% trans 'Slug' %} + {% trans 'Members' %} + {% trans 'Maintained Pages' %} {% trans 'Logo' %} {% trans 'Options' %} diff --git a/integreat_cms/cms/templates/organizations/organization_list_row.html b/integreat_cms/cms/templates/organizations/organization_list_row.html index f5de0265f8..1da09a3ce3 100644 --- a/integreat_cms/cms/templates/organizations/organization_list_row.html +++ b/integreat_cms/cms/templates/organizations/organization_list_row.html @@ -8,7 +8,12 @@ - {{ organization.slug }} + {{ organization.num_members }} + + + + + {{ organization.num_pages }} diff --git a/integreat_cms/locale/de/LC_MESSAGES/django.po b/integreat_cms/locale/de/LC_MESSAGES/django.po index 8c62beb874..1e2640c6a1 100644 --- a/integreat_cms/locale/de/LC_MESSAGES/django.po +++ b/integreat_cms/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-17 14:48+0000\n" +"POT-Creation-Date: 2022-10-17 15:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Integreat \n" "Language-Team: Integreat \n" @@ -1815,6 +1815,14 @@ msgstr "" "Die Datei kann nicht in einem Verzeichnis einer anderen Region hochgeladen " "werden." +#: cms/forms/organizations/organization_form.py:74 +msgid "" +"An organization with the same name already exists in this region. Please " +"choose another name." +msgstr "" +"Eine Organisation mit diesem Namen existiert bereits in dieser Region. Bitte " +"wählen Sie einen anderen Namen." + #: cms/forms/pages/page_filter_form.py:18 msgid "Translation status" msgstr "Übersetzungsstatus" @@ -1955,6 +1963,7 @@ msgid "Email" msgstr "Email" #: cms/forms/users/user_filter_form.py:21 cms/forms/users/user_form.py:27 +#: cms/templates/organizations/organization_form.html:159 #: cms/templates/users/region_user_list.html:25 #: cms/templates/users/user_list.html:34 msgid "Role" @@ -3324,11 +3333,11 @@ msgstr "" msgid "regions" msgstr "Regionen" -#: cms/models/users/organization.py:66 cms/models/users/user.py:65 +#: cms/models/users/organization.py:83 cms/models/users/user.py:65 msgid "organization" msgstr "Organisation" -#: cms/models/users/organization.py:70 +#: cms/models/users/organization.py:87 msgid "organizations" msgstr "Organisationen" @@ -3504,6 +3513,7 @@ msgid "Media Library" msgstr "Medienbibliothek" #: cms/templates/_base.html:153 +#: cms/templates/organizations/organization_form.html:73 msgid "Pages" msgstr "Seiten" @@ -4291,7 +4301,7 @@ msgstr "Wiederholung" #: cms/templates/languages/language_list.html:30 #: cms/templates/linkcheck/links_by_filter.html:64 #: cms/templates/offertemplates/offertemplate_list.html:26 -#: cms/templates/organizations/organization_list.html:23 +#: cms/templates/organizations/organization_list.html:24 #: cms/templates/pages/page_tree.html:90 #: cms/templates/pages/page_tree_archived.html:69 #: cms/templates/pois/poi_list.html:73 @@ -4381,6 +4391,8 @@ msgstr "Veranstaltungen wiederherstellen" #: cms/templates/events/event_list_archived_row.html:31 #: cms/templates/events/event_list_row.html:16 #: cms/templates/events/event_list_row.html:30 +#: cms/templates/organizations/organization_form.html:109 +#: cms/templates/organizations/organization_form.html:125 #: cms/templates/pages/page_tree_archived_node.html:31 #: cms/templates/pages/page_tree_archived_node.html:41 #: cms/templates/pages/page_tree_node.html:38 @@ -4497,6 +4509,7 @@ msgstr "Als ungelesen markieren" #: cms/templates/feedback/admin_feedback_list.html:141 #: cms/templates/feedback/region_feedback_list.html:133 #: cms/templates/languagetreenodes/languagetreenode_list.html:31 +#: cms/templates/organizations/organization_form.html:31 msgid "Delete" msgstr "Löschen" @@ -4744,7 +4757,7 @@ msgstr "Neue Sprache erstellen" #: cms/templates/languages/language_form.html:19 #: cms/templates/linkcheck/link_list_row.html:66 #: cms/templates/offertemplates/offertemplate_form.html:20 -#: cms/templates/organizations/organization_form.html:19 +#: cms/templates/organizations/organization_form.html:37 #: cms/templates/poicategories/poicategory_form.html:19 #: cms/templates/push_notifications/push_notification_form.html:23 #: cms/templates/regions/region_form.html:22 @@ -4781,7 +4794,7 @@ msgstr "Übersetzungen" #: cms/templates/languages/language_form.html:74 #: cms/templates/offertemplates/offertemplate_form.html:57 -#: cms/templates/organizations/organization_form.html:38 +#: cms/templates/organizations/organization_form.html:55 #: cms/templates/regions/region_form.html:78 msgid "Extended Settings" msgstr "Erweiterte Einstellungen" @@ -4823,7 +4836,6 @@ msgstr "Nativer Name" #: cms/templates/languages/language_list.html:23 #: cms/templates/languagetreenodes/languagetreenode_list.html:25 #: cms/templates/offertemplates/offertemplate_list.html:21 -#: cms/templates/organizations/organization_list.html:21 msgid "Slug" msgstr "URL-Parameter" @@ -5017,7 +5029,7 @@ msgid "Create new offer template" msgstr "Neue Angebots-Vorlage erstellen" #: cms/templates/offertemplates/offertemplate_form.html:30 -#: cms/templates/organizations/organization_form.html:25 +#: cms/templates/organizations/organization_form.html:45 #: cms/templates/regions/region_form.html:29 #: cms/templates/users/region_user_form.html:29 #: cms/templates/users/user_form.html:26 @@ -5054,23 +5066,48 @@ msgstr "Noch keine Angebots-Vorlagen vorhanden." msgid "Delete offer templates" msgstr "Angebots-Vorlage löschen" -#: cms/templates/organizations/organization_form.html:12 +#: cms/templates/organizations/organization_form.html:16 #, python-format msgid "Edit organization \"%(organization_name)s\"" msgstr "Organisation \"%(organization_name)s\" bearbeiten" -#: cms/templates/organizations/organization_form.html:15 +#: cms/templates/organizations/organization_form.html:19 msgid "Create new organization" msgstr "Neue Organisation erstellen" -#: cms/templates/organizations/organization_form.html:48 -#: cms/templates/organizations/organization_list_row.html:25 +#: cms/templates/organizations/organization_form.html:25 +#: cms/templates/organizations/organization_list_row.html:30 msgid "Delete organization" msgstr "Organisation löschen" -#: cms/templates/organizations/organization_form.html:54 -msgid "Delete this organization" -msgstr "Diese Organisation löschen" +#: cms/templates/organizations/organization_form.html:84 +#: cms/templates/organizations/organization_form.html:89 +msgid "Name in " +msgstr "Name auf" + +#: cms/templates/organizations/organization_form.html:103 +#: cms/templates/organizations/organization_form.html:116 +msgid "View page" +msgstr "Weitere Seiten-Infos" + +#: cms/templates/organizations/organization_form.html:134 +msgid "This organization currently has no maintained pages." +msgstr "Von dieser Organisation werden zur Zeit keine Seiten verwaltet." + +#: cms/templates/organizations/organization_form.html:148 +#: cms/templates/organizations/organization_list.html:21 +msgid "Members" +msgstr "Mitglieder" + +#: cms/templates/organizations/organization_form.html:156 +#: cms/templates/users/region_user_list.html:21 +#: cms/templates/users/user_list.html:29 +msgid "Username" +msgstr "Benutzername" + +#: cms/templates/organizations/organization_form.html:178 +msgid "This organization currently has no members." +msgstr "Diese Organisation hat zur Zeit keine Mitglieder." #: cms/templates/organizations/organization_list.html:7 msgid "Manage Organizations" @@ -5081,10 +5118,14 @@ msgid "Create organization" msgstr "Organisation erstellen" #: cms/templates/organizations/organization_list.html:22 +msgid "Maintained Pages" +msgstr "Anzahl an verwaltenten Seiten" + +#: cms/templates/organizations/organization_list.html:23 msgid "Logo" msgstr "Logo" -#: cms/templates/organizations/organization_list.html:32 +#: cms/templates/organizations/organization_list.html:33 msgid "No organizations available yet." msgstr "Noch keine Organisationen vorhanden." @@ -6276,11 +6317,6 @@ msgstr "Nutzerverwaltung" msgid "Create user" msgstr "Benutzer erstellen" -#: cms/templates/users/region_user_list.html:21 -#: cms/templates/users/user_list.html:29 -msgid "Username" -msgstr "Benutzername" - #: cms/templates/users/region_user_list.html:22 #: cms/templates/users/user_list.html:30 msgid "First Name" @@ -7640,6 +7676,9 @@ msgstr "" #~ "Wenn Sie diese Felder leer lassen, werden sie automatisch aus der Adresse " #~ "abgeleitet." +#~ msgid "Delete this organization" +#~ msgstr "Diese Organisation löschen" + #~ msgid "No POI categories found with these filters." #~ msgstr "Keine POI kategorie mit diesen Filtern gefunden." @@ -7697,6 +7736,16 @@ msgstr "" #~ msgid "The selected {} were successfully archived" #~ msgstr "Die ausgewählten {} wurden erfolgreich archiviert" +#~ msgid "Number of Members" +#~ msgstr "Mitgliederanzahl" + +#~ msgid "Name in Englisch" +#~ msgstr "Name auf Englisch" + +#~| msgid "Delete this organization" +#~ msgid "Currently no users belong to this organization" +#~ msgstr "Diese Organisation löschen" + #~ msgid "The selected {} were successfully restored" #~ msgstr "Die ausgewählten {} wurden erfolgreich wiederhergestellt" @@ -8920,9 +8969,6 @@ msgstr "" #~ msgid "table of contents caption" #~ msgstr "Überschrift des Inhaltsverzeichnis" -#~ msgid "Name in English" -#~ msgstr "Name auf Englisch" - #~ msgid "Enter the language's name in english here" #~ msgstr "Name der Sprache auf Englisch hier eingeben" diff --git a/tests/cms/views/view_config.py b/tests/cms/views/view_config.py index aaed1e3fa5..169f43c67c 100644 --- a/tests/cms/views/view_config.py +++ b/tests/cms/views/view_config.py @@ -100,8 +100,8 @@ ("region_feedback", STAFF_ROLES + [MANAGEMENT]), ("region_users", STAFF_ROLES + [MANAGEMENT]), ("translation_coverage", ROLES), - ("organizations", STAFF_ROLES), - ("new_organization", STAFF_ROLES), + ("organizations", STAFF_ROLES + [MANAGEMENT]), + ("new_organization", STAFF_ROLES + [MANAGEMENT]), ("user_settings", ROLES), ("authenticate_modify_mfa", ROLES), ],