diff --git a/src/membership/admin.py b/src/membership/admin.py index 465764f..fef4adc 100644 --- a/src/membership/admin.py +++ b/src/membership/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from .models import Membership from .models import MembershipType +from .models import ServiceAccess from .models import SubscriptionPeriod @@ -18,3 +19,8 @@ class MembershipTypeAdmin(admin.ModelAdmin): @admin.register(SubscriptionPeriod) class SubscriptionPeriodAdmin(admin.ModelAdmin): pass + + +@admin.register(ServiceAccess) +class ServiceAccessAdmin(admin.ModelAdmin): + pass diff --git a/src/membership/migrations/0006_serviceaccess_serviceaccess_unique_user_service.py b/src/membership/migrations/0006_serviceaccess_serviceaccess_unique_user_service.py new file mode 100644 index 0000000..9354287 --- /dev/null +++ b/src/membership/migrations/0006_serviceaccess_serviceaccess_unique_user_service.py @@ -0,0 +1,64 @@ +# Generated by Django 5.0.1 on 2024-01-13 19:20 + +import django.db.models.deletion +import django_registries.registry +import services.registry +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("membership", "0005_member"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="ServiceAccess", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "modified", + models.DateTimeField(auto_now=True, verbose_name="modified"), + ), + ( + "created", + models.DateTimeField(auto_now_add=True, verbose_name="created"), + ), + ( + "service", + django_registries.registry.ChoicesField( + choices=[], + registry=services.registry.ServiceRegistry, + verbose_name="service", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "service access", + "verbose_name_plural": "service accesses", + }, + ), + migrations.AddConstraint( + model_name="serviceaccess", + constraint=models.UniqueConstraint( + fields=("user", "service"), name="unique_user_service" + ), + ), + ] diff --git a/src/membership/models.py b/src/membership/models.py index 29adc35..7b5ae3f 100644 --- a/src/membership/models.py +++ b/src/membership/models.py @@ -6,6 +6,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext as _ +from services.registry import ServiceRegistry from utils.mixins import CreatedModifiedAbstract @@ -119,3 +120,22 @@ class MembershipType(CreatedModifiedAbstract): def __str__(self): return self.name + + +class ServiceAccess(CreatedModifiedAbstract): + class Meta: + verbose_name = _("service access") + verbose_name_plural = _("service accesses") + constraints = [ + models.UniqueConstraint( + fields=["user", "service"], + name="unique_user_service", + ), + ] + + user = models.ForeignKey("auth.User", on_delete=models.PROTECT) + + service = ServiceRegistry.choices_field(verbose_name=_("service")) + + def __str__(self): + return f"{self.user} - {self.service}" diff --git a/src/project/settings.py b/src/project/settings.py index 349ee9e..9c445eb 100644 --- a/src/project/settings.py +++ b/src/project/settings.py @@ -40,12 +40,14 @@ DJANGO_APPS = [ THIRD_PARTY_APPS = [ "allauth", "allauth.account", + "django_registries", ] LOCAL_APPS = [ "utils", "accounting", "membership", + "services", ] INSTALLED_APPS = [ diff --git a/src/services/__init__.py b/src/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/services/admin.py b/src/services/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/src/services/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/src/services/apps.py b/src/services/apps.py new file mode 100644 index 0000000..11e745d --- /dev/null +++ b/src/services/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ServicesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "services" diff --git a/src/services/migrations/__init__.py b/src/services/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/services/models.py b/src/services/models.py new file mode 100644 index 0000000..6b20219 --- /dev/null +++ b/src/services/models.py @@ -0,0 +1 @@ +# Create your models here. diff --git a/src/services/registry.py b/src/services/registry.py new file mode 100644 index 0000000..8fc430e --- /dev/null +++ b/src/services/registry.py @@ -0,0 +1,21 @@ +from django_registries.registry import Interface +from django_registries.registry import Registry + + +class ServiceRegistry(Registry): + """Registry for services""" + + implementations_module = "services" + + +class ServiceInterface(Interface): + """Interface for services""" + + registry = ServiceRegistry + + public: bool = False + + # TODO: add a way to add a something which defines the required fields for a service + # - maybe a list of tuples with the field name and the type of the field + # this could be used to generate a form for the service, and also to validate + # the data saved in a JSONField on the ServiceAccess model diff --git a/src/services/services.py b/src/services/services.py new file mode 100644 index 0000000..e5580ac --- /dev/null +++ b/src/services/services.py @@ -0,0 +1,27 @@ +from .registry import ServiceInterface + + +class MailService(ServiceInterface): + slug = "mail" + + +class MatrixService(ServiceInterface): + slug = "matrix" + + +class MastodonService(ServiceInterface): + slug = "mastodon" + + +class NextcloudService(ServiceInterface): + slug = "nextcloud" + + +class HedgeDocService(ServiceInterface): + slug = "hedgedoc" + public = True + + +class RalllyService(ServiceInterface): + slug = "rallly" + public = True diff --git a/src/services/tests.py b/src/services/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/src/services/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/src/services/views.py b/src/services/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/src/services/views.py @@ -0,0 +1 @@ +# Create your views here.