Skip to content
Snippets Groups Projects
Commit 5a4c92d5 authored by HeJ's avatar HeJ
Browse files

add UserCommunicationChannel type 'ActivityPub'

preparation for #572
parent e7544de6
Branches
Tags
No related merge requests found
......@@ -2261,6 +2261,9 @@ msgstr "Telefon/Handy"
msgid "UserCommunicationChannel__channel-matrix"
msgstr "Matrix (Riot/Element)"
msgid "UserCommunicationChannel__channel-activitypub"
msgstr "ActivityPub (z. B. Mastodon)"
msgid "UserCommunicationChannel__channel__help"
msgstr "Art des Kommunikationskanals"
......@@ -2273,6 +2276,12 @@ msgstr "Kontaktangabe in kanalspezifischer Notation"
msgid "UserCommunicationChannel__address"
msgstr "Ziel"
msgid "UserCommunicationChannel__caption__help"
msgstr "Freitext/Bezeichnung für den Kanal, wird ggf. öffentlich eingeblendet"
msgid "UserCommunicationChannel__caption"
msgstr "Beschriftung"
msgid "UserCommunicationChannel__is_verified__help"
msgstr "die Kontaktadresse wurde verifiziert"
......
......@@ -2259,6 +2259,9 @@ msgstr "phone/mobile"
msgid "UserCommunicationChannel__channel-matrix"
msgstr "Matrix (Riot/Element)"
msgid "UserCommunicationChannel__channel-activitypub"
msgstr "ActivityPub (e.g. Mastodon)"
msgid "UserCommunicationChannel__channel__help"
msgstr "the type of communication channel"
......@@ -2271,6 +2274,12 @@ msgstr "contact address in channel-specific notation"
msgid "UserCommunicationChannel__address"
msgstr "Address"
msgid "UserCommunicationChannel__caption__help"
msgstr "description of this communication channel, might be shown publicly"
msgid "UserCommunicationChannel__caption"
msgstr "caption"
msgid "UserCommunicationChannel__is_verified__help"
msgstr "this contact detail has been verified"
......
# Generated by Django 5.1.2 on 2024-11-17 17:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0159_event_banner_image_url_alter_event_banner_image'),
]
operations = [
migrations.AddField(
model_name='usercommunicationchannel',
name='caption',
field=models.CharField(blank=True, help_text='UserCommunicationChannel__caption__help', max_length=200, null=True, verbose_name='UserCommunicationChannel__caption'),
),
migrations.AlterField(
model_name='usercommunicationchannel',
name='channel',
field=models.CharField(choices=[('mail', 'UserCommunicationChannel__channel-mail'), ('xmpp', 'UserCommunicationChannel__channel-xmpp'), ('irc', 'UserCommunicationChannel__channel-irc'), ('dect', 'UserCommunicationChannel__channel-dect'), ('phone', 'UserCommunicationChannel__channel-phone'), ('matrix', 'UserCommunicationChannel__channel-matrix'), ('activitypub', 'UserCommunicationChannel__channel-activitypub')], help_text='UserCommunicationChannel__channel__help', max_length=20, verbose_name='UserCommunicationChannel__channel'),
),
]
......@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any
from uuid import uuid4
from timezone_field import TimeZoneField
from urllib3.util import parse_url
from django.conf import settings
from django.contrib.auth.models import AbstractUser, UserManager
......@@ -576,6 +577,7 @@ class UserCommunicationChannel(models.Model):
DECT = 'dect', _('UserCommunicationChannel__channel-dect')
PHONE = 'phone', _('UserCommunicationChannel__channel-phone')
MATRIX = 'matrix', _('UserCommunicationChannel__channel-matrix')
ACTIVITYPUB = 'activitypub', _('UserCommunicationChannel__channel-activitypub')
user = models.ForeignKey(PlatformUser, related_name='communication_channels', on_delete=models.CASCADE)
......@@ -584,6 +586,10 @@ class UserCommunicationChannel(models.Model):
)
address = models.CharField(max_length=255, help_text=_('UserCommunicationChannel__address__help'), verbose_name=_('UserCommunicationChannel__address'))
caption = models.CharField(
max_length=200, blank=True, null=True, help_text=_('UserCommunicationChannel__caption__help'), verbose_name=_('UserCommunicationChannel__caption')
)
is_verified = models.BooleanField(
default=False, help_text=_('UserCommunicationChannel__is_verified__help'), verbose_name=_('UserCommunicationChannel__is_verified')
)
......@@ -625,6 +631,14 @@ class UserCommunicationChannel(models.Model):
if _RE_TELEPHONE.match(address) is None and _RE_VANITY.match(address) is None:
raise ValidationError({'address': 'expected an international telephone number (starting with + or 00)'})
@staticmethod
def validate_activitypub_url(address):
url = parse_url(address)
if url.scheme not in ['http', 'https']:
raise ValidationError({'address': 'Expected https:// URL.'})
if url.host is None or url.path is None:
raise ValidationError({'address': 'Expected valid URL.'})
@property
def can_notify(self):
"""Signals whether this channel can be used for notifications."""
......@@ -644,6 +658,9 @@ class UserCommunicationChannel(models.Model):
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)
# TODO: verify the other channel types for correct syntax as well
if self.use_for_notifications:
......
......@@ -72,6 +72,23 @@ class UserCommunicationChannelStaticTests(TestCase):
self.check_good_bad('phone number', UserCommunicationChannel.validate_phone_number, good_numbers, bad_numbers)
def test_activitypub_validation(self):
good_addresses = [
'https://chaos.social/@ordnung',
'https://chaos.social/@events/113499399916580165',
'https://social.mrtoto.net/@apptest',
'https://social.mrtoto.net/@apptest/113085273939532302',
]
bad_addresses = [
'',
'foo',
'chaos.social/ordnung',
'chaos.social/@ordnung',
'ftp://ccc.de',
'hub@cccv.de',
]
self.check_good_bad('ActivityPub URL', UserCommunicationChannel.validate_activitypub_url, good_addresses, bad_addresses)
class UserCommunicationChannelTests(TestCase):
def setUp(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment