diff --git a/static/css/style.css b/static/css/style.css index 737d9bf..dae2253 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -365,6 +365,10 @@ nav a i { margin: 0 5px 0 5px; } +.icon-menu .option .pill.bad { + background: var(--color-delete); +} + .handle { vertical-align: middle; display: inline-block; diff --git a/takahe/urls.py b/takahe/urls.py index ddffa68..b94f205 100644 --- a/takahe/urls.py +++ b/takahe/urls.py @@ -68,6 +68,16 @@ urlpatterns = [ "admin/domains//delete/", admin.DomainDelete.as_view(), ), + path( + "admin/federation/", + admin.FederationRoot.as_view(), + name="admin_federation", + ), + path( + "admin/federation//", + admin.FederationEdit.as_view(), + name="admin_federation_edit", + ), path( "admin/users/", admin.Users.as_view(), diff --git a/templates/admin/federation.html b/templates/admin/federation.html new file mode 100644 index 0000000..1a48176 --- /dev/null +++ b/templates/admin/federation.html @@ -0,0 +1,24 @@ +{% extends "settings/base.html" %} + +{% block subtitle %}Federation{% endblock %} + +{% block content %} +
+ {% for domain in domains %} + + + + {{ domain.domain }} + + {{ domain.num_users }} remote identit{{ domain.num_users|pluralize:"y,ies" }} + + + {% if domain.blocked %} + Blocked + {% endif %} + + {% empty %} +

There are no federation links yet.

+ {% endfor %} +
+{% endblock %} diff --git a/templates/admin/federation_edit.html b/templates/admin/federation_edit.html new file mode 100644 index 0000000..f96e7ae --- /dev/null +++ b/templates/admin/federation_edit.html @@ -0,0 +1,19 @@ +{% extends "settings/base.html" %} + +{% block subtitle %}{{ domain.domain }}{% endblock %} + +{% block content %} +
+ {% csrf_token %} +

{{ domain }}

+
+ Federation Controls + {% include "forms/_field.html" with field=form.blocked %} +
+
+ Back + Delete + +
+
+{% endblock %} diff --git a/templates/settings/_menu.html b/templates/settings/_menu.html index 49a5d68..531febb 100644 --- a/templates/settings/_menu.html +++ b/templates/settings/_menu.html @@ -24,6 +24,9 @@ Domains + + Federation + Users diff --git a/users/models/domain.py b/users/models/domain.py index 40a3e22..622085d 100644 --- a/users/models/domain.py +++ b/users/models/domain.py @@ -56,6 +56,8 @@ class Domain(models.Model): create = "/admin/domains/create/" edit = "/admin/domains/{self.domain}/" delete = "{edit}delete/" + root_federation = "/admin/federation/" + edit_federation = "/admin/federation/{self.domain}/" @classmethod def get_remote_domain(cls, domain: str) -> "Domain": diff --git a/users/views/admin/__init__.py b/users/views/admin/__init__.py index 231e027..d1a4db1 100644 --- a/users/views/admin/__init__.py +++ b/users/views/admin/__init__.py @@ -10,6 +10,7 @@ from users.views.admin.domains import ( # noqa DomainEdit, Domains, ) +from users.views.admin.federation import FederationEdit, FederationRoot # noqa from users.views.admin.settings import BasicSettings # noqa diff --git a/users/views/admin/federation.py b/users/views/admin/federation.py new file mode 100644 index 0000000..da8209a --- /dev/null +++ b/users/views/admin/federation.py @@ -0,0 +1,57 @@ +from django import forms +from django.db import models +from django.shortcuts import get_object_or_404, redirect +from django.utils.decorators import method_decorator +from django.views.generic import FormView, TemplateView + +from users.decorators import admin_required +from users.models import Domain + + +@method_decorator(admin_required, name="dispatch") +class FederationRoot(TemplateView): + + template_name = "admin/federation.html" + + def get_context_data(self): + return { + "domains": Domain.objects.filter(local=False) + .annotate(num_users=models.Count("identities")) + .order_by("domain"), + "section": "federation", + } + + +@method_decorator(admin_required, name="dispatch") +class FederationEdit(FormView): + + template_name = "admin/federation_edit.html" + extra_context = {"section": "federation"} + + class form_class(forms.Form): + blocked = forms.BooleanField( + help_text="If this domain is blocked from interacting with this server", + widget=forms.Select(choices=[(True, "Blocked"), (False, "Not Blocked")]), + required=False, + ) + + def dispatch(self, request, domain): + self.domain = get_object_or_404( + Domain.objects.filter(local=False), domain=domain + ) + return super().dispatch(request) + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context["domain"] = self.domain + return context + + def form_valid(self, form): + self.domain.blocked = form.cleaned_data["blocked"] + self.domain.save() + return redirect(Domain.urls.root_federation) + + def get_initial(self): + return { + "blocked": self.domain.blocked, + }