mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-25 16:51:00 +00:00
User admin and LD schema fixes
This commit is contained in:
parent
1bcdff79e7
commit
45c6978bc3
13 changed files with 189 additions and 23 deletions
|
@ -659,7 +659,7 @@ class Post(StatorModel):
|
||||||
if update or created:
|
if update or created:
|
||||||
post.content = data["content"]
|
post.content = data["content"]
|
||||||
post.summary = data.get("summary")
|
post.summary = data.get("summary")
|
||||||
post.sensitive = data.get("as:sensitive", False)
|
post.sensitive = data.get("sensitive", False)
|
||||||
post.url = data.get("url")
|
post.url = data.get("url")
|
||||||
post.published = parse_ld_date(data.get("published"))
|
post.published = parse_ld_date(data.get("published"))
|
||||||
post.edited = parse_ld_date(data.get("updated"))
|
post.edited = parse_ld_date(data.get("updated"))
|
||||||
|
@ -670,7 +670,7 @@ class Post(StatorModel):
|
||||||
if tag["type"].lower() == "mention":
|
if tag["type"].lower() == "mention":
|
||||||
mention_identity = Identity.by_actor_uri(tag["href"], create=True)
|
mention_identity = Identity.by_actor_uri(tag["href"], create=True)
|
||||||
post.mentions.add(mention_identity)
|
post.mentions.add(mention_identity)
|
||||||
elif tag["type"].lower() == "as:hashtag":
|
elif tag["type"].lower() == "hashtag":
|
||||||
post.hashtags.append(tag["name"].lower().lstrip("#"))
|
post.hashtags.append(tag["name"].lower().lstrip("#"))
|
||||||
elif tag["type"].lower() == "http://joinmastodon.org/ns#emoji":
|
elif tag["type"].lower() == "http://joinmastodon.org/ns#emoji":
|
||||||
emoji = Emoji.by_ap_tag(post.author.domain, tag, create=True)
|
emoji = Emoji.by_ap_tag(post.author.domain, tag, create=True)
|
||||||
|
|
|
@ -415,6 +415,7 @@ def canonicalise(json_data: dict, include_security: bool = False) -> dict:
|
||||||
"votersCount": "toot:votersCount",
|
"votersCount": "toot:votersCount",
|
||||||
"Hashtag": "as:Hashtag",
|
"Hashtag": "as:Hashtag",
|
||||||
"Public": "as:Public",
|
"Public": "as:Public",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if include_security:
|
if include_security:
|
||||||
|
|
|
@ -96,9 +96,14 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"admin/users/",
|
"admin/users/",
|
||||||
admin.Users.as_view(),
|
admin.UsersRoot.as_view(),
|
||||||
name="admin_users",
|
name="admin_users",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"admin/users/<id>/",
|
||||||
|
admin.UserEdit.as_view(),
|
||||||
|
name="admin_user_edit",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"admin/identities/",
|
"admin/identities/",
|
||||||
admin.Identities.as_view(),
|
admin.Identities.as_view(),
|
||||||
|
|
36
templates/admin/user_edit.html
Normal file
36
templates/admin/user_edit.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends "settings/base.html" %}
|
||||||
|
|
||||||
|
{% block subtitle %}{{ user.email }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{ editing_user.email }}</h1>
|
||||||
|
<form action="." method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>Permissions</legend>
|
||||||
|
{% include "forms/_field.html" with field=form.status %}
|
||||||
|
{% if same_user %}
|
||||||
|
<ul class="errorlist">
|
||||||
|
<li>You cannot edit your own permission status!</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Identities</legend>
|
||||||
|
{% for identity in editing_user.identities.all %}
|
||||||
|
{% include "activities/_identity.html" %}
|
||||||
|
{% empty %}
|
||||||
|
<p>This user has no identities yet.</p>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Dates</legend>
|
||||||
|
<p>Last seen: <time title="{{ editing_user.last_seen }} UTC">{{ editing_user.last_seen | timesince }} ago</time></p>
|
||||||
|
<p>Created: <time title="{{ editing_user.created }} UTC">{{ editing_user.created | timesince }} ago</time></p>
|
||||||
|
</fieldset>
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="{{ editing_user.urls.admin }}" class="button secondary left">Back</a>
|
||||||
|
<button>Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -3,7 +3,40 @@
|
||||||
{% block subtitle %}Users{% endblock %}
|
{% block subtitle %}Users{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>
|
<form action="." class="search">
|
||||||
Please use the <a href="/djadmin/users/user/">Django Admin</a> for now.
|
<input type="search" name="query" value="{{ query }}" placeholder="Search by email">
|
||||||
</p>
|
<button><i class="fa-solid fa-search"></i></button>
|
||||||
|
</form>
|
||||||
|
<section class="icon-menu">
|
||||||
|
{% for user in page_obj %}
|
||||||
|
<a class="option" href="{{ user.urls.admin_edit }}">
|
||||||
|
<i class="fa-solid fa-user"></i>
|
||||||
|
<span class="handle">
|
||||||
|
{{ user.email }}
|
||||||
|
<small>
|
||||||
|
{{ user.num_identities }} identit{{ user.num_identities|pluralize:"y,ies" }}
|
||||||
|
</small>
|
||||||
|
</span>
|
||||||
|
{% if user.banned %}
|
||||||
|
<span class="pill bad">Banned</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
{% empty %}
|
||||||
|
<p class="option empty">
|
||||||
|
{% if query %}
|
||||||
|
No users match your query.
|
||||||
|
{% else %}
|
||||||
|
There are no users yet.
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="load-more">
|
||||||
|
{% if page_obj.has_previous %}
|
||||||
|
<a class="button" href=".?page={{ page_obj.previous_page_number }}">Previous Page</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if page_obj.has_next %}
|
||||||
|
<a class="button" href=".?page={{ page_obj.next_page_number }}">Next Page</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -155,7 +155,7 @@ def test_fetch_actor(httpx_mock, config_system):
|
||||||
"mediaType": "image/jpeg",
|
"mediaType": "image/jpeg",
|
||||||
"url": "https://example.com/image.jpg",
|
"url": "https://example.com/image.jpg",
|
||||||
},
|
},
|
||||||
"as:manuallyApprovesFollowers": False,
|
"manuallyApprovesFollowers": False,
|
||||||
"name": "Test User",
|
"name": "Test User",
|
||||||
"preferredUsername": "test",
|
"preferredUsername": "test",
|
||||||
"published": "2022-11-02T00:00:00Z",
|
"published": "2022-11-02T00:00:00Z",
|
||||||
|
|
|
@ -89,7 +89,13 @@ class PasswordResetAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(InboxMessage)
|
@admin.register(InboxMessage)
|
||||||
class InboxMessageAdmin(admin.ModelAdmin):
|
class InboxMessageAdmin(admin.ModelAdmin):
|
||||||
list_display = ["id", "state", "state_changed", "message_type", "message_actor"]
|
list_display = [
|
||||||
|
"id",
|
||||||
|
"state",
|
||||||
|
"state_changed",
|
||||||
|
"message_type_full",
|
||||||
|
"message_actor",
|
||||||
|
]
|
||||||
list_filter = ("state",)
|
list_filter = ("state",)
|
||||||
search_fields = ["message"]
|
search_fields = ["message"]
|
||||||
actions = ["reset_state"]
|
actions = ["reset_state"]
|
||||||
|
|
|
@ -438,7 +438,7 @@ class Identity(StatorModel):
|
||||||
self.username = self.username["@value"]
|
self.username = self.username["@value"]
|
||||||
if self.username:
|
if self.username:
|
||||||
self.username = self.username.lower()
|
self.username = self.username.lower()
|
||||||
self.manually_approves_followers = document.get("as:manuallyApprovesFollowers")
|
self.manually_approves_followers = document.get("manuallyApprovesFollowers")
|
||||||
self.public_key = document.get("publicKey", {}).get("publicKeyPem")
|
self.public_key = document.get("publicKey", {}).get("publicKeyPem")
|
||||||
self.public_key_id = document.get("publicKey", {}).get("id")
|
self.public_key_id = document.get("publicKey", {}).get("id")
|
||||||
self.icon_uri = document.get("icon", {}).get("url")
|
self.icon_uri = document.get("icon", {}).get("url")
|
||||||
|
|
|
@ -115,6 +115,13 @@ class InboxMessage(StatorModel):
|
||||||
def message_object_type(self):
|
def message_object_type(self):
|
||||||
return self.message["object"]["type"].lower()
|
return self.message["object"]["type"].lower()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message_type_full(self):
|
||||||
|
if isinstance(self.message.get("object"), dict):
|
||||||
|
return f"{self.message_type}.{self.message_object_type}"
|
||||||
|
else:
|
||||||
|
return f"{self.message_type}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message_actor(self):
|
def message_actor(self):
|
||||||
return self.message.get("actor")
|
return self.message.get("actor")
|
||||||
|
|
|
@ -48,7 +48,7 @@ class SystemActor:
|
||||||
},
|
},
|
||||||
"preferredUsername": self.username,
|
"preferredUsername": self.username,
|
||||||
"url": self.profile_uri,
|
"url": self.profile_uri,
|
||||||
"as:manuallyApprovesFollowers": True,
|
"manuallyApprovesFollowers": True,
|
||||||
"publicKey": {
|
"publicKey": {
|
||||||
"id": self.public_key_id,
|
"id": self.public_key_id,
|
||||||
"owner": self.actor_uri,
|
"owner": self.actor_uri,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import urlman
|
||||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
@ -44,6 +45,10 @@ class User(AbstractBaseUser):
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
class urls(urlman.Urls):
|
||||||
|
admin = "/admin/users/"
|
||||||
|
admin_edit = "{admin}{self.pk}/"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return not (self.deleted or self.banned)
|
return not (self.deleted or self.banned)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import FormView, RedirectView, TemplateView
|
from django.views.generic import FormView, RedirectView, TemplateView
|
||||||
|
|
||||||
from users.decorators import admin_required
|
from users.decorators import admin_required
|
||||||
from users.models import Identity, User
|
from users.models import Identity
|
||||||
from users.views.admin.domains import ( # noqa
|
from users.views.admin.domains import ( # noqa
|
||||||
DomainCreate,
|
DomainCreate,
|
||||||
DomainDelete,
|
DomainDelete,
|
||||||
|
@ -23,6 +23,7 @@ from users.views.admin.settings import ( # noqa
|
||||||
TuningSettings,
|
TuningSettings,
|
||||||
)
|
)
|
||||||
from users.views.admin.stator import Stator # noqa
|
from users.views.admin.stator import Stator # noqa
|
||||||
|
from users.views.admin.users import UserEdit, UsersRoot # noqa
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
|
@ -30,18 +31,6 @@ class AdminRoot(RedirectView):
|
||||||
pattern_name = "admin_basic"
|
pattern_name = "admin_basic"
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(admin_required, name="dispatch")
|
|
||||||
class Users(TemplateView):
|
|
||||||
|
|
||||||
template_name = "admin/users.html"
|
|
||||||
|
|
||||||
def get_context_data(self):
|
|
||||||
return {
|
|
||||||
"users": User.objects.order_by("email"),
|
|
||||||
"section": "users",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class Identities(TemplateView):
|
class Identities(TemplateView):
|
||||||
|
|
||||||
|
|
84
users/views/admin/users.py
Normal file
84
users/views/admin/users.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
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, ListView
|
||||||
|
|
||||||
|
from users.decorators import admin_required
|
||||||
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(admin_required, name="dispatch")
|
||||||
|
class UsersRoot(ListView):
|
||||||
|
|
||||||
|
template_name = "admin/users.html"
|
||||||
|
paginate_by = 50
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.query = request.GET.get("query")
|
||||||
|
self.extra_context = {
|
||||||
|
"section": "users",
|
||||||
|
"query": self.query or "",
|
||||||
|
}
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
users = User.objects.annotate(
|
||||||
|
num_identities=models.Count("identities")
|
||||||
|
).order_by("created")
|
||||||
|
if self.query:
|
||||||
|
users = users.filter(email__icontains=self.query)
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(admin_required, name="dispatch")
|
||||||
|
class UserEdit(FormView):
|
||||||
|
|
||||||
|
template_name = "admin/user_edit.html"
|
||||||
|
extra_context = {
|
||||||
|
"section": "users",
|
||||||
|
}
|
||||||
|
|
||||||
|
class form_class(forms.Form):
|
||||||
|
status = forms.ChoiceField(
|
||||||
|
choices=[
|
||||||
|
("normal", "Normal User"),
|
||||||
|
("moderator", "Moderator"),
|
||||||
|
("admin", "Admin"),
|
||||||
|
("banned", "Banned"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def dispatch(self, request, id, *args, **kwargs):
|
||||||
|
self.user = get_object_or_404(User, id=id)
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
status = "normal"
|
||||||
|
if self.user.moderator:
|
||||||
|
status = "moderator"
|
||||||
|
if self.user.admin:
|
||||||
|
status = "admin"
|
||||||
|
if self.user.banned:
|
||||||
|
status = "banned"
|
||||||
|
return {
|
||||||
|
"email": self.user.email,
|
||||||
|
"status": status,
|
||||||
|
}
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
# Don't let them change themselves
|
||||||
|
if self.user == self.request.user:
|
||||||
|
return redirect(".")
|
||||||
|
status = form.cleaned_data["status"]
|
||||||
|
self.user.banned = status == "banned"
|
||||||
|
self.user.moderator = status == "moderator"
|
||||||
|
self.user.admin = status == "admin"
|
||||||
|
self.user.save()
|
||||||
|
return redirect(self.user.urls.admin)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["editing_user"] = self.user
|
||||||
|
context["same_user"] = self.user == self.request.user
|
||||||
|
return context
|
Loading…
Reference in a new issue