From b0586ebebf9b039db5af5955ef361af9b97a087b Mon Sep 17 00:00:00 2001 From: Lucas Brandstaetter <lucas@brandstaetter.tech> Date: Fri, 27 Dec 2024 01:48:02 +0000 Subject: [PATCH] Add dect to user profile --- src/core/locale/de/LC_MESSAGES/django.po | 3 ++ src/core/locale/en/LC_MESSAGES/django.po | 3 ++ src/core/models/users.py | 39 ++++++++++++------- src/plainui/forms.py | 25 ++++++++++++ .../plainui/components/form_elements.html.j2 | 5 +++ src/plainui/tests/test_views.py | 3 ++ src/plainui/views/user_profile.py | 15 +++++++ 7 files changed, 80 insertions(+), 13 deletions(-) diff --git a/src/core/locale/de/LC_MESSAGES/django.po b/src/core/locale/de/LC_MESSAGES/django.po index 54605f17f..d2e9c7ad8 100644 --- a/src/core/locale/de/LC_MESSAGES/django.po +++ b/src/core/locale/de/LC_MESSAGES/django.po @@ -2339,6 +2339,9 @@ msgstr "Adresse öffentlich anzeigen" msgid "UserCommunicationChannel__show_public" msgstr "öffentlich" +msgid "UserCommunicationChannel__cannot_notify__dect" +msgstr "Dieser Kanal kann nicht für Benachrichtigungen benutzt werden." + msgid "UserCommunicationChannel__cannot_notify__phone" msgstr "Eine Benachrichtigung per Telefon wird nicht unterstützt." diff --git a/src/core/locale/en/LC_MESSAGES/django.po b/src/core/locale/en/LC_MESSAGES/django.po index d5c6b5e3d..00a10f3dc 100644 --- a/src/core/locale/en/LC_MESSAGES/django.po +++ b/src/core/locale/en/LC_MESSAGES/django.po @@ -2337,6 +2337,9 @@ msgstr "show this address publicly" msgid "UserCommunicationChannel__show_public" msgstr "public" +msgid "UserCommunicationChannel__cannot_notify__dect" +msgstr "cannot use this channel for notifications" + msgid "UserCommunicationChannel__cannot_notify__phone" msgstr "notifications via phone/mobile are not supported" diff --git a/src/core/models/users.py b/src/core/models/users.py index 0ab6c716e..9b73e9633 100644 --- a/src/core/models/users.py +++ b/src/core/models/users.py @@ -563,6 +563,17 @@ class UserCommunicationChannel(models.Model): if url.host is None or url.path is None: raise ValidationError({'address': 'Expected valid URL.'}) + @staticmethod + def validate_dect(address): + try: + if len(str(address)) > 10: + raise ValidationError({'address': 'Dect may not be longer than 10 chars.'}) + if address[0] == '0': + raise ValidationError({'address': 'Dect must not start with a 0.'}) + int(address) + except ValueError: + raise ValidationError({'address': 'Dect must be a number.'}) + @property def can_notify(self): """Signals whether this channel can be used for notifications.""" @@ -571,19 +582,21 @@ class UserCommunicationChannel(models.Model): def clean(self): super().clean() - if self.channel == self.Channel.MAIL: - self.validate_email(self.address) - - elif self.channel == self.Channel.MATRIX: - self.validate_matrix_room_url(self.address) - - elif self.channel == self.Channel.PHONE: - self.validate_phone_number(self.address) - if self.use_for_notifications: - raise ValidationError({'use_for_notifications': _('UserCommunicationChannel__cannot_notify__phone')}) - - elif self.channel == self.Channel.ACTIVITYPUB: - self.validate_activitypub_url(self.address) + match self.channel: + case self.Channel.MAIL: + self.validate_email(self.address) + case self.Channel.MATRIX: + self.validate_matrix_room_url(self.address) + case self.Channel.DECT: + self.validate_dect(self.address) + if self.use_for_notifications: + raise ValidationError({'use_for_notifications': _('UserCommunicationChannel__cannot_notify__dect')}) + case self.Channel.PHONE: + self.validate_phone_number(self.address) + if self.use_for_notifications: + raise ValidationError({'use_for_notifications': _('UserCommunicationChannel__cannot_notify__phone')}) + case self.Channel.ACTIVITYPUB: + self.validate_activitypub_url(self.address) # TODO: verify the other channel types for correct syntax as well diff --git a/src/plainui/forms.py b/src/plainui/forms.py index 2f62412aa..bb7c18cc8 100644 --- a/src/plainui/forms.py +++ b/src/plainui/forms.py @@ -92,6 +92,8 @@ class ExampleForm(forms.Form): class ProfileEditForm(TranslatedFieldsForm): + dect = forms.IntegerField(required=False) + class Meta: model = PlatformUser fields = [ @@ -99,6 +101,29 @@ class ProfileEditForm(TranslatedFieldsForm): 'timezone', ] + def __init__(self, *args, instance, **kwargs): + super().__init__(*args, instance=instance, **kwargs) + + dect_channel = UserCommunicationChannel.objects.filter( + user=instance, + channel=UserCommunicationChannel.Channel.DECT, + ).first() + if dect_channel: + self.fields['dect'].initial = dect_channel.address + + def clean_dect(self): + dect = self.cleaned_data['dect'] + # TODO: De-Duplicate with UserCommunicationChannel + try: + if len(str(dect)) > 10: + raise ValidationError('Dect may not be longer than 10 chars.') + if str(dect)[0] == '0': + raise ValidationError('Dect must not start with a 0.') + int(dect) + except ValueError: + raise ValidationError('Dect must be a number.') + return dect + class ProfileDescriptionEditForm(TranslatedFieldsForm): class Meta: diff --git a/src/plainui/jinja2/plainui/components/form_elements.html.j2 b/src/plainui/jinja2/plainui/components/form_elements.html.j2 index 6da3b51b2..facb377b6 100644 --- a/src/plainui/jinja2/plainui/components/form_elements.html.j2 +++ b/src/plainui/jinja2/plainui/components/form_elements.html.j2 @@ -37,6 +37,9 @@ {% macro password(form, name) -%} {{ input(form, name, 'password') }} {%- endmacro %} +{% macro number(form, name) -%} + {{ input(form, name, 'number') }} +{%- endmacro %} {% macro textarea(form, name) -%} {% set el = form[name] -%} {% set my_id = unique_id() -%} @@ -132,6 +135,8 @@ {{ text(form, field_name) }} {% elif field.widget_type == 'password' -%} {{ password(form, field_name) }} + {% elif field.widget_type == 'number' -%} + {{ number(form, field_name) }} {% elif field.widget_type == 'checkbox' -%} {{ checkbox(form, field_name) }} {% elif field.widget_type == 'select' -%} diff --git a/src/plainui/tests/test_views.py b/src/plainui/tests/test_views.py index 373612288..daef90c5d 100644 --- a/src/plainui/tests/test_views.py +++ b/src/plainui/tests/test_views.py @@ -47,6 +47,7 @@ from core.models import ( StaticPageRevision, TagItem, UserBadge, + UserCommunicationChannel, UserDereferrerAllowlist, ) from core.templatetags.hub_absolute import hub_absolute @@ -1297,6 +1298,7 @@ class ViewsTest(ViewsTestBase): 'default_badge_visibility': 'private', 'pronouns': 'they', 'timezone': 'Europe/Berlin', + 'dect': '1337', }, ) self.assertRedirects(resp, reverse('plainui:userprofile')) @@ -1307,6 +1309,7 @@ class ViewsTest(ViewsTestBase): self.assertEqual(self.user.pronouns, 'they') self.assertEqual(self.user.timezone.key, 'Europe/Berlin') self.assertSetsMessage(resp, 'Updated Profile') + self.assertTrue(UserCommunicationChannel.objects.filter(user=self.user, channel=UserCommunicationChannel.Channel.DECT, address='1337').exists()) @override_locale('en') def test_ModifyThemeView_get(self): diff --git a/src/plainui/views/user_profile.py b/src/plainui/views/user_profile.py index 2ba4f6639..cbf955451 100644 --- a/src/plainui/views/user_profile.py +++ b/src/plainui/views/user_profile.py @@ -24,6 +24,7 @@ from core.models import ( Event, Project, UserBadge, + UserCommunicationChannel, UserDereferrerAllowlist, ) from core.sso import SSO @@ -120,6 +121,20 @@ class ProfileView(ConferenceRequiredMixin, UpdateView): form1.instance.timezone = form1.cleaned_data['timezone'] form1.instance.save() + if form1.cleaned_data['dect']: + UserCommunicationChannel.objects.update_or_create( + user=self.request.user, + channel=UserCommunicationChannel.Channel.DECT, + defaults={'address': form1.cleaned_data['dect']}, + ) + else: + dect_channel = UserCommunicationChannel.objects.filter( + user=self.request.user, + channel=UserCommunicationChannel.Channel.DECT, + ) + if dect_channel.exists(): + dect_channel.delete() + # TODO: Update after deciding oh one or more conferences in #648 cm = form1.instance.conferences.filter(conference=self.conf).first() if cm: -- GitLab