From 2af91aa1780ac218949ebee2286867f18e90e912 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 8 Jan 2025 14:27:23 +0100 Subject: [PATCH 1/3] # 450 Optimization recalculate_parcels command * optimizes recalculate_parcels.py command so that only non-empty geometries will be processed * drops test_identifier_generating.py command due to missing usage --- .../commands/recalculate_parcels.py | 4 +- .../commands/test_identifier_generating.py | 51 ------------------- 2 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 konova/management/commands/test_identifier_generating.py diff --git a/konova/management/commands/recalculate_parcels.py b/konova/management/commands/recalculate_parcels.py index 42165c5f..021e3d68 100644 --- a/konova/management/commands/recalculate_parcels.py +++ b/konova/management/commands/recalculate_parcels.py @@ -34,7 +34,9 @@ class Command(BaseKonovaCommand): def recalculate_parcels(self, options: dict): force_all = options.get("force_all", False) - geometry_objects = Geometry.objects.all().exclude( + geometry_objects = Geometry.objects.filter( + geom__isempty=False, + ).exclude( geom=None ) diff --git a/konova/management/commands/test_identifier_generating.py b/konova/management/commands/test_identifier_generating.py deleted file mode 100644 index 09c35731..00000000 --- a/konova/management/commands/test_identifier_generating.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Author: Michel Peltriaux -Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany -Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 19.08.21 - -""" -from django.core.management import BaseCommand - -from intervention.models import Intervention - - -class Command(BaseCommand): - help = "Performs test on collisions using the identifier generation" - - def handle(self, *args, **options): - identifiers = {} - max_iterations = 100000 - try: - collisions = 0 - len_ids = len(identifiers) - while len_ids < max_iterations: - tmp_intervention = Intervention() - _id = tmp_intervention.generate_new_identifier() - len_ids = len(identifiers) - if _id not in identifiers: - if len_ids % (max_iterations/5) == 0: - print(len_ids) - identifiers[_id] = None - else: - collisions += 1 - print("+++ Collision after {} identifiers +++".format(len_ids)) - - except KeyboardInterrupt: - self._break_line() - exit(-1) - print( - "\n{} collisions in {} identifiers; Collision rate {}%".format( - collisions, - len_ids, - (collisions / len_ids)*100, - ) - ) - - def _break_line(self): - """ Simply prints a line break - - Returns: - - """ - self.stdout.write("\n") \ No newline at end of file From 5a0c5285e79ee3ced144c9263427dec692bbf9fe Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 8 Jan 2025 14:35:35 +0100 Subject: [PATCH 2/3] # 457 User autocomplete fix * fixes bug where empty query parameter would show users in autocomplete share view * fixes same behaviour on autocomplete share team view --- user/autocomplete/share.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/user/autocomplete/share.py b/user/autocomplete/share.py index 215e32c6..4e450286 100644 --- a/user/autocomplete/share.py +++ b/user/autocomplete/share.py @@ -17,10 +17,11 @@ class ShareUserAutocomplete(Select2QuerySetView): """ def get_queryset(self): + qs = User.objects.none() if self.request.user.is_anonymous: - return User.objects.none() - qs = User.objects.all() + return qs if self.q: + qs = User.objects.all() # Due to privacy concerns only a full username match will return the proper user entry qs = qs.filter( Q(username=self.q) | @@ -41,13 +42,13 @@ class ShareTeamAutocomplete(Select2QuerySetView): """ def get_queryset(self): + qs = Team.objects.none() if self.request.user.is_anonymous: - return Team.objects.none() - qs = Team.objects.filter( - deleted__isnull=True - ) + return qs if self.q: - # Due to privacy concerns only a full username match will return the proper user entry + qs = Team.objects.filter( + deleted__isnull=True + ) q_parts = self.q.split(" ") q = Q() for part in q_parts: From 9b63307f01fa998e9a844028ede377d5c6f0c6c1 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 8 Jan 2025 16:03:26 +0100 Subject: [PATCH 3/3] # 456 Rework API key creation * removes frontend input field holding generated API key * replaces with modal form * reworks tests on API token form --- konova/utils/message_templates.py | 3 + konova/views/geometry.py | 1 - locale/de/LC_MESSAGES/django.mo | Bin 45741 -> 46027 bytes locale/de/LC_MESSAGES/django.po | 325 ++++++------------------------ user/forms/modals/api_token.py | 49 +++++ user/forms/user.py | 45 ----- user/templates/user/token.html | 19 +- user/tests/unit/test_forms.py | 58 +++--- user/urls.py | 6 +- user/views/api_token.py | 57 ++++++ user/views/views.py | 40 +--- 11 files changed, 214 insertions(+), 389 deletions(-) create mode 100644 user/forms/modals/api_token.py create mode 100644 user/views/api_token.py diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index b36e2a0a..e5e71557 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -91,3 +91,6 @@ INTERVENTION_HAS_REVOCATIONS_TEMPLATE = _("This intervention has {} revocations" DATA_CHECKED_ON_TEMPLATE = _("Checked on {} by {}") DATA_CHECKED_PREVIOUSLY_TEMPLATE = _("Data has changed since last check on {} by {}") DATA_IS_UNCHECKED = _("Current data not checked yet") + +# API TOKEN SETTINGS +NEW_API_TOKEN_GENERATED = _("New token generated. Administrators need to validate.") \ No newline at end of file diff --git a/konova/views/geometry.py b/konova/views/geometry.py index c9eed0d5..30aa8cd9 100644 --- a/konova/views/geometry.py +++ b/konova/views/geometry.py @@ -5,7 +5,6 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.gis.geos import MultiPolygon from django.http import HttpResponse, HttpRequest from django.shortcuts import get_object_or_404 diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 176949462bf5e1a407e824d18768d38fe5b0d69f..2a3ee10f5c815fa231ec41330a146d20c3c3ba46 100644 GIT binary patch delta 11641 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 diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 9bc92c82..eb35247c 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -38,13 +38,14 @@ #: konova/forms/modals/remove_form.py:23 #: konova/forms/modals/resubmission_form.py:22 #: konova/forms/modals/resubmission_form.py:38 konova/forms/remove_form.py:25 -#: konova/tests/unit/test_forms.py:59 user/forms/user.py:39 +#: konova/tests/unit/test_forms.py:59 user/forms/modals/api_token.py:17 +#: user/forms/user.py:39 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-19 10:32+0200\n" +"POT-Creation-Date: 2025-01-08 15:26+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -373,7 +374,6 @@ msgid "Identifier" msgstr "Kennung" #: compensation/forms/compensation.py:33 intervention/forms/intervention.py:33 -#: user/forms/user.py:77 msgid "Generated automatically - not editable" msgstr "Automatisch generiert - nicht bearbeitbar" @@ -1795,8 +1795,7 @@ msgstr "Bearbeitender Nutzer" msgid "" "Search for entries where this person has been participated according to log " "history" -msgstr "" -"Sucht nach Einträgen, an denen diese Person gearbeitet hat" +msgstr "Sucht nach Einträgen, an denen diese Person gearbeitet hat" #: konova/forms/base_form.py:23 templates/form/collapsable/form.html:62 msgid "Save" @@ -1858,6 +1857,7 @@ msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." #: konova/forms/modals/remove_form.py:22 konova/forms/remove_form.py:24 +#: user/forms/modals/api_token.py:16 msgid "Confirm" msgstr "Bestätige" @@ -2283,6 +2283,10 @@ msgstr "" msgid "Current data not checked yet" msgstr "Momentane Daten noch nicht geprüft" +#: konova/utils/message_templates.py:96 +msgid "New token generated. Administrators need to validate." +msgstr "Neuer Token generiert. Administratoren sind informiert." + #: konova/utils/messenger.py:70 msgid "{} checked" msgstr "{} geprüft" @@ -2321,7 +2325,7 @@ msgstr "Home" msgid "Log" msgstr "Log" -#: konova/views/map_proxy.py:70 +#: konova/views/map_proxy.py:84 msgid "" "The external service is currently unavailable.
Please try again in a few " "moments..." @@ -2831,10 +2835,8 @@ msgid "Reports" msgstr "Berichte" #: templates/navbars/navbar.html:57 -#, fuzzy -#| msgid "Admins" msgid "Admin" -msgstr "Administratoren" +msgstr "" #: templates/navbars/navbar.html:59 user/templates/user/index.html:31 msgid "Settings" @@ -2873,6 +2875,21 @@ msgstr "" "Falls die Geometrie nicht leer ist, werden die Flurstücke aktuell berechnet. " "Bitte laden Sie diese Seite in ein paar Augenblicken erneut..." +#: user/forms/modals/api_token.py:25 +msgid "Generate API Token" +msgstr "API Token generieren" + +#: user/forms/modals/api_token.py:29 +msgid "" +"You are about to create a new API token. The existing one will not be usable " +"afterwards." +msgstr "" +"Wenn Sie fortfahren, generieren Sie einen neuen API Token. Ihren existierenden werden Sie dann nicht länger nutzen können." + +#: user/forms/modals/api_token.py:31 +msgid "A new token needs to be validated by an administrator!" +msgstr "Neue Tokens müssen durch Administratoren freigeschaltet werden!" + #: user/forms/modals/team.py:20 user/forms/modals/team.py:24 #: user/forms/team.py:17 user/forms/team.py:22 msgid "Team name" @@ -2895,11 +2912,11 @@ msgstr "" "Mehrfachauswahl möglich - Sie können nur Nutzer wählen, die noch nicht " "Mitglieder dieses Teams sind. Geben Sie den ganzen Nutzernamen an." -#: user/forms/modals/team.py:56 user/tests/unit/test_forms.py:31 +#: user/forms/modals/team.py:56 user/tests/unit/test_forms.py:29 msgid "Create new team" msgstr "Neues Team anlegen" -#: user/forms/modals/team.py:57 user/tests/unit/test_forms.py:32 +#: user/forms/modals/team.py:57 user/tests/unit/test_forms.py:30 msgid "" "You will become the administrator for this group by default. You do not need " "to add yourself to the list of members." @@ -2928,11 +2945,11 @@ msgid "There must be at least one admin on this team." msgstr "Es muss mindestens einen Administrator für das Team geben." #: user/forms/modals/team.py:160 user/templates/user/team/index.html:60 -#: user/tests/unit/test_forms.py:88 +#: user/tests/unit/test_forms.py:86 msgid "Edit team" msgstr "Team bearbeiten" -#: user/forms/modals/team.py:187 user/tests/unit/test_forms.py:165 +#: user/forms/modals/team.py:187 user/tests/unit/test_forms.py:163 msgid "" "ATTENTION!\n" "\n" @@ -2949,7 +2966,7 @@ msgstr "" "Sind Sie sicher, dass Sie dieses Team löschen möchten?" #: user/forms/modals/team.py:197 user/templates/user/team/index.html:56 -#: user/tests/unit/test_forms.py:198 +#: user/tests/unit/test_forms.py:196 msgid "Leave team" msgstr "Team verlassen" @@ -2981,22 +2998,10 @@ msgstr "Benachrichtigungen" msgid "Select the situations when you want to receive a notification" msgstr "Wann wollen Sie per E-Mail benachrichtigt werden?" -#: user/forms/user.py:38 user/tests/unit/test_forms.py:234 +#: user/forms/user.py:38 user/tests/unit/test_forms.py:232 msgid "Edit notifications" msgstr "Benachrichtigungen bearbeiten" -#: user/forms/user.py:73 -msgid "Token" -msgstr "" - -#: user/forms/user.py:88 user/tests/unit/test_forms.py:260 -msgid "Create new token" -msgstr "Neuen Token generieren" - -#: user/forms/user.py:89 user/tests/unit/test_forms.py:261 -msgid "A new token needs to be validated by an administrator!" -msgstr "Neue Tokens müssen durch Administratoren freigeschaltet werden!" - #: user/models/user_action.py:23 msgid "Unrecorded" msgstr "Entzeichnet" @@ -3051,7 +3056,7 @@ msgid "Manage teams" msgstr "" #: user/templates/user/index.html:53 user/templates/user/team/index.html:19 -#: user/views/views.py:171 +#: user/views/views.py:135 msgid "Teams" msgstr "" @@ -3087,270 +3092,58 @@ msgstr "API Einstellungen" msgid "Current token" msgstr "Aktueller Token" -#: user/templates/user/token.html:14 +#: user/templates/user/token.html:15 +msgid "Create new token" +msgstr "Neuen Token generieren" + +#: user/templates/user/token.html:23 msgid "Authenticated by admins" msgstr "Von Admin freigeschaltet" -#: user/templates/user/token.html:18 +#: user/templates/user/token.html:27 msgid "Token has been verified and can be used" msgstr "Token wurde freigeschaltet und kann verwendet werden" -#: user/templates/user/token.html:20 +#: user/templates/user/token.html:29 msgid "Token waiting for verification" msgstr "Token noch nicht freigeschaltet" -#: user/templates/user/token.html:24 +#: user/templates/user/token.html:33 msgid "Valid until" msgstr "Läuft ab am" -#: user/views/views.py:35 -msgid "User settings" -msgstr "Einstellungen" - -#: user/views/views.py:61 -msgid "Notifications edited" -msgstr "Benachrichtigungen bearbeitet" - -#: user/views/views.py:73 -msgid "User notifications" -msgstr "Benachrichtigungen" - -#: user/views/views.py:96 -msgid "New token generated. Administrators need to validate." -msgstr "Neuer Token generiert. Administratoren sind informiert." - -#: user/views/views.py:107 +#: user/views/api_token.py:33 msgid "User API token" msgstr "API Nutzer Token" -#: user/views/views.py:183 +#: user/views/views.py:33 +msgid "User settings" +msgstr "Einstellungen" + +#: user/views/views.py:59 +msgid "Notifications edited" +msgstr "Benachrichtigungen bearbeitet" + +#: user/views/views.py:71 +msgid "User notifications" +msgstr "Benachrichtigungen" + +#: user/views/views.py:147 msgid "New team added" msgstr "Neues Team hinzugefügt" -#: user/views/views.py:198 +#: user/views/views.py:162 msgid "Team edited" msgstr "Team bearbeitet" -#: user/views/views.py:213 +#: user/views/views.py:177 msgid "Team removed" msgstr "Team gelöscht" -#: user/views/views.py:228 +#: user/views/views.py:192 msgid "You are not a member of this team" msgstr "Sie sind kein Mitglied dieses Teams" -#: user/views/views.py:235 +#: user/views/views.py:199 msgid "Left Team" msgstr "Team verlassen" - -#~ msgid "close" -#~ msgstr "Schließen" - -#~ msgid "Options" -#~ msgstr "Optionen" - -#~ msgid "Commands" -#~ msgstr "Befehle" - -#~ msgid "Missing command." -#~ msgstr "Befehl fehlt" - -#~ msgid "Missing argument" -#~ msgstr "Argument fehlt" - -#~ msgid "Missing option" -#~ msgstr "Option fehlt" - -#~ msgid "Missing parameter" -#~ msgstr "Parameter fehlt" - -#~ msgid "Messages" -#~ msgstr "Nachrichten" - -#~ msgid "This field is required." -#~ msgstr "Pflichtfeld" - -#~ msgid "Monday" -#~ msgstr "Montag" - -#~ msgid "Tuesday" -#~ msgstr "Dienstag" - -#~ msgid "Wednesday" -#~ msgstr "Mittwoch" - -#~ msgid "Thursday" -#~ msgstr "Donnerstag" - -#~ msgid "Friday" -#~ msgstr "Freitag" - -#~ msgid "Saturday" -#~ msgstr "Samstag" - -#~ msgid "Sunday" -#~ msgstr "Sonntag" - -#~ msgid "Mon" -#~ msgstr "Mo" - -#~ msgid "Tue" -#~ msgstr "Di" - -#~ msgid "Wed" -#~ msgstr "Mi" - -#~ msgid "Thu" -#~ msgstr "Do" - -#~ msgid "Fri" -#~ msgstr "Fr" - -#~ msgid "Sat" -#~ msgstr "Sa" - -#~ msgid "Sun" -#~ msgstr "So" - -#~ msgid "January" -#~ msgstr "Januar" - -#~ msgid "February" -#~ msgstr "Februar" - -#~ msgid "March" -#~ msgstr "März" - -#~ msgid "May" -#~ msgstr "Mai" - -#~ msgid "June" -#~ msgstr "Juni" - -#~ msgid "July" -#~ msgstr "Juli" - -#~ msgid "October" -#~ msgstr "Oktober" - -#~ msgid "December" -#~ msgstr "Dezember" - -#~ msgid "mar" -#~ msgstr "mär" - -#~ msgid "may" -#~ msgstr "mai" - -#~ msgid "oct" -#~ msgstr "okt" - -#~ msgid "dec" -#~ msgstr "dez" - -#~ msgctxt "abbrev. month" -#~ msgid "March" -#~ msgstr "Mär" - -#~ msgctxt "abbrev. month" -#~ msgid "May" -#~ msgstr "Mai" - -#~ msgctxt "abbrev. month" -#~ msgid "June" -#~ msgstr "Juni" - -#~ msgctxt "abbrev. month" -#~ msgid "July" -#~ msgstr "Juli" - -#~ msgctxt "abbrev. month" -#~ msgid "Oct." -#~ msgstr "Okt." - -#~ msgctxt "abbrev. month" -#~ msgid "Dec." -#~ msgstr "Dez." - -#~ msgctxt "alt. month" -#~ msgid "January" -#~ msgstr "Januar" - -#~ msgctxt "alt. month" -#~ msgid "February" -#~ msgstr "Februar" - -#~ msgctxt "alt. month" -#~ msgid "March" -#~ msgstr "März" - -#~ msgctxt "alt. month" -#~ msgid "May" -#~ msgstr "Mai" - -#~ msgctxt "alt. month" -#~ msgid "June" -#~ msgstr "Juni" - -#~ msgctxt "alt. month" -#~ msgid "July" -#~ msgstr "Juli" - -#~ msgctxt "alt. month" -#~ msgid "October" -#~ msgstr "Oktober" - -#~ msgctxt "alt. month" -#~ msgid "December" -#~ msgstr "Dezember" - -#~ msgid "or" -#~ msgstr "oder" - -#~ msgid "" -#~ "Deductable surface can not be larger than existing surfaces in after " -#~ "states" -#~ msgstr "" -#~ "Die abbuchbare Fläche darf die Gesamtfläche der Zielzustände nicht " -#~ "überschreiten" - -#~ msgid "" -#~ "Deductable surface can not be smaller than the sum of already existing " -#~ "deductions. Please contact the responsible users for the deductions!" -#~ msgstr "" -#~ "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar " -#~ "einstellen wollen. Kontaktieren Sie die für die Abbuchungen " -#~ "verantwortlichen Nutzer!" - -#~ msgid "Added deadline" -#~ msgstr "Frist/Termin hinzugefügt" - -#~ msgid "Change default configuration for your KSP map" -#~ msgstr "Karteneinstellungen ändern" - -#~ msgid "Map settings" -#~ msgstr "Karte" - -#~ msgid "There are errors on this intervention:" -#~ msgstr "Es liegen Fehler in diesem Eingriff vor:" - -#~ msgid "Before" -#~ msgstr "Vor" - -#~ msgid "Groups" -#~ msgstr "Gruppen" - -#~ msgid "Show more..." -#~ msgstr "Mehr anzeigen..." - -#~ msgid "Kreis" -#~ msgstr "Kreis" - -#~ msgid "Gemarkung" -#~ msgstr "Gemarkung" - -#~ msgid "Loading..." -#~ msgstr "Lade..." - -#~ msgid "Who handles the eco-account" -#~ msgstr "Wer für die Herrichtung des Ökokontos verantwortlich ist" diff --git a/user/forms/modals/api_token.py b/user/forms/modals/api_token.py new file mode 100644 index 00000000..d5ead620 --- /dev/null +++ b/user/forms/modals/api_token.py @@ -0,0 +1,49 @@ +""" +Author: Michel Peltriaux +Created on: 08.01.25 + +""" +from django import forms +from django.utils.translation import gettext_lazy as _ + +from api.models import APIUserToken +from konova.forms.modals import BaseModalForm +from konova.utils.mailer import Mailer + + +class NewAPITokenModalForm(BaseModalForm): + confirm = forms.BooleanField( + label=_("Confirm"), + label_suffix=_(""), + widget=forms.CheckboxInput(), + required=True, + ) + + def __init__(self, *args, **kwargs): + self.template = "modal/modal_form.html" + super().__init__(*args, **kwargs) + self.form_title = _("Generate API Token") + + self.form_caption = "" + if self.__user_has_api_token(): + self.form_caption = _("You are about to create a new API token. The existing one will not be usable afterwards.") + self.form_caption += "\n" + self.form_caption += _("A new token needs to be validated by an administrator!") + # Disable automatic w-100 setting for this type of modal form. Looks kinda strange + self.fields["confirm"].widget.attrs["class"] = "" + + def __user_has_api_token(self): + return self.instance.api_token is not None + + def save(self): + user = self.instance + if user.api_token is not None: + user.api_token.delete() + user.api_token = APIUserToken.objects.create() + user.save() + + mailer = Mailer() + mailer.send_mail_verify_api_token(user) + + return user.api_token + diff --git a/user/forms/user.py b/user/forms/user.py index e27dba9b..190ce126 100644 --- a/user/forms/user.py +++ b/user/forms/user.py @@ -66,48 +66,3 @@ class UserNotificationForm(BaseForm): id__in=selected_notification_ids, ) self.user.notifications.set(notifications) - - -class UserAPITokenForm(BaseForm): - token = forms.CharField( - label=_("Token"), - label_suffix="", - max_length=255, - required=True, - help_text=_("Generated automatically - not editable"), - widget=GenerateInput( - attrs={ - "class": "form-control", - "url": reverse_lazy("api:generate-new-token"), - } - ) - ) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.form_title = _("Create new token") - self.form_caption = _("A new token needs to be validated by an administrator!") - - self.action_url = reverse("user:api-token") - self.cancel_redirect = reverse("user:index") - - # Make direct token editing by user impossible. Instead set the proper url for generating a new token - self.initialize_form_field("token", None) - self.fields["token"].widget.attrs["readonly"] = True - - def save(self): - """ Saves the form data - - Returns: - api_token (APIUserToken) - """ - user = self.instance - new_token = self.cleaned_data["token"] - if user.api_token is not None: - user.api_token.delete() - new_token = APIUserToken.objects.create( - token=new_token - ) - user.api_token = new_token - user.save() - return new_token \ No newline at end of file diff --git a/user/templates/user/token.html b/user/templates/user/token.html index 47b873d7..ab0f8272 100644 --- a/user/templates/user/token.html +++ b/user/templates/user/token.html @@ -8,7 +8,16 @@ - + @@ -27,7 +36,9 @@
{% trans 'Current token' %}{{ user.api_token.token }} +
+
{{ user.api_token.token }}
+
+ +
+
+
{% trans 'Authenticated by admins' %}
-
- {% include 'form/table/generic_table_form.html' %} - + +{% with 'btn-modal' as btn_class %} + {% include 'modal/modal_form_script.html' %} +{% endwith %} + {% endblock %} \ No newline at end of file diff --git a/user/tests/unit/test_forms.py b/user/tests/unit/test_forms.py index 48ecd5d9..20e4333c 100644 --- a/user/tests/unit/test_forms.py +++ b/user/tests/unit/test_forms.py @@ -5,15 +5,14 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 12.09.23 """ -from django.core.exceptions import ObjectDoesNotExist from django.test import RequestFactory from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from api.models import APIUserToken from konova.tests.test_views import BaseTestCase +from user.forms.modals.api_token import NewAPITokenModalForm from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm -from user.forms.user import UserNotificationForm, UserAPITokenForm +from user.forms.user import UserNotificationForm from user.models import Team, UserAction, UserNotification @@ -252,35 +251,28 @@ class UserNotificationFormTestCase(BaseTestCase): self.assertIn(selected_notification, self.user.notifications.all()) -class UserAPITokenFormTestCase(BaseTestCase): - def test_init(self): - form = UserAPITokenForm( - instance=self.user - ) - self.assertEqual(form.form_title, str(_("Create new token"))) - self.assertEqual(form.form_caption, str(_("A new token needs to be validated by an administrator!"))) - self.assertEqual(form.action_url, reverse("user:api-token")) - self.assertEqual(form.cancel_redirect, reverse("user:index")) - - self.assertIsNone(form.fields["token"].initial) - self.assertTrue(form.fields["token"].widget.attrs["readonly"]) - - def test_save(self): - data = { - "token": APIUserToken().token +class ApiTokenFormTestCase(BaseTestCase): + def test_new_token_and_recreating_token(self): + request = RequestFactory().request() + request.user = self.user + request.POST = { + "confirm": True } - form = UserAPITokenForm( - data, - instance=self.user - ) - self.assertTrue(form.is_valid(), msg=form.errors) + self.assertIsNone(self.user.api_token) - token = form.save() - self.assertEqual(self.user.api_token, token) - new_token = form.save() - self.assertEqual(self.user.api_token, new_token) - try: - token.refresh_from_db() - self.fail("Token should be deleted and not be fetchable anymore") - except ObjectDoesNotExist: - pass + form = NewAPITokenModalForm(request.POST, instance=self.user) + form.save() + self.user.refresh_from_db() + token = self.user.api_token + self.assertFalse(token.is_active) + self.assertIsNone(token.valid_until) + self.assertIsNotNone(token.token) + + old_token = token.token + form.save() + self.user.refresh_from_db() + new_token = self.user.api_token + self.assertNotEqual(new_token.token, old_token) + self.assertFalse(new_token.is_active) + self.assertIsNone(new_token.valid_until) + diff --git a/user/urls.py b/user/urls.py index cb6e20b0..c3127a1e 100644 --- a/user/urls.py +++ b/user/urls.py @@ -9,6 +9,7 @@ from django.urls import path from user.autocomplete.share import ShareUserAutocomplete, ShareTeamAutocomplete from user.autocomplete.team import TeamAdminAutocomplete +from user.views.api_token import APITokenView, new_api_token_view from user.views.propagate import PropagateUserView from user.views.views import * @@ -17,7 +18,8 @@ urlpatterns = [ path("", index_view, name="index"), path("propagate/", PropagateUserView.as_view(), name="propagate"), path("notifications/", notifications_view, name="notifications"), - path("token/api", api_token_view, name="api-token"), + path("token/api", APITokenView.as_view(), name="api-token"), + path("token/api/new", new_api_token_view, name="api-token-new"), path("contact/", contact_view, name="contact"), path("team/", index_team_view, name="team-index"), path("team/new", new_team_view, name="team-new"), @@ -30,4 +32,4 @@ urlpatterns = [ path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"), path("atcmplt/share/t", ShareTeamAutocomplete.as_view(), name="share-team-autocomplete"), path("atcmplt/team/admin", TeamAdminAutocomplete.as_view(), name="team-admin-autocomplete"), -] \ No newline at end of file +] diff --git a/user/views/api_token.py b/user/views/api_token.py new file mode 100644 index 00000000..3b653c28 --- /dev/null +++ b/user/views/api_token.py @@ -0,0 +1,57 @@ +""" +Author: Michel Peltriaux +Created on: 08.01.25 + +""" +from django.contrib.auth.decorators import login_required +from django.http import HttpRequest +from django.shortcuts import render +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.views import View +from django.utils.translation import gettext_lazy as _ + +from konova.contexts import BaseContext +from konova.decorators import default_group_required +from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER +from konova.utils.message_templates import NEW_API_TOKEN_GENERATED +from user.forms.modals.api_token import NewAPITokenModalForm + + +class APITokenView(View): + + @method_decorator(login_required) + @method_decorator(default_group_required) + def dispatch(self, request, *args, **kwargs): + return super().dispatch(request, *args, **kwargs) + + def get(self, request: HttpRequest): + template = "user/token.html" + user = request.user + + context = { + "user": user, + TAB_TITLE_IDENTIFIER: _("User API token"), + } + context = BaseContext(request, context).context + return render(request, template, context) + + +def new_api_token_view(request: HttpRequest): + """ Function based view for processing ModalForm + (Currently ModalForms only work properly with function based views) + + Args: + request (): + + Returns: + + """ + user = request.user + + form = NewAPITokenModalForm(request.POST or None, instance=user, request=request) + return form.process_request( + request=request, + msg_success=NEW_API_TOKEN_GENERATED, + redirect_url=reverse("user:api-token"), + ) \ No newline at end of file diff --git a/user/views/views.py b/user/views/views.py index 30ce627e..5d773926 100644 --- a/user/views/views.py +++ b/user/views/views.py @@ -3,19 +3,17 @@ from django.contrib.auth.decorators import login_required from django.urls import reverse from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER -from konova.utils.mailer import Mailer -from konova.utils.message_templates import FORM_INVALID from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm from user.forms.modals.user import UserContactForm from user.forms.team import TeamDataForm -from user.forms.user import UserNotificationForm, UserAPITokenForm +from user.forms.user import UserNotificationForm from user.models import User, Team from django.http import HttpRequest, Http404 from django.shortcuts import render, redirect, get_object_or_404 from django.utils.translation import gettext_lazy as _ from konova.contexts import BaseContext -from konova.decorators import any_group_check, default_group_required, login_required_modal +from konova.decorators import any_group_check, login_required_modal @login_required @@ -76,40 +74,6 @@ def notifications_view(request: HttpRequest): return render(request, template, context) -@login_required -@default_group_required -def api_token_view(request: HttpRequest): - """ Handles the request for user api frontend settings - - Args: - request (HttpRequest): The incoming request - - Returns: - - """ - template = "user/token.html" - user = request.user - form = UserAPITokenForm(request.POST or None, instance=user) - if request.method == "POST": - if form.is_valid(): - token = form.save() - messages.info(request, _("New token generated. Administrators need to validate.")) - mailer = Mailer() - mailer.send_mail_verify_api_token(user) - return redirect("user:api-token") - else: - messages.error(request, FORM_INVALID, extra_tags="danger") - elif request.method != "GET": - raise NotImplementedError - context = { - "user": user, - "form": form, - TAB_TITLE_IDENTIFIER: _("User API token"), - } - context = BaseContext(request, context).context - return render(request, template, context) - - @login_required_modal @login_required def contact_view(request: HttpRequest, id: str):