mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-29 02:31:00 +00:00
Call it admin rather than system settings
This commit is contained in:
parent
9d97fc92d8
commit
5b34ea46c3
27 changed files with 273 additions and 111 deletions
18
activities/migrations/0006_alter_post_hashtags.py
Normal file
18
activities/migrations/0006_alter_post_hashtags.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.1.3 on 2022-11-17 04:18
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("activities", "0005_post_hashtags_alter_fanout_type_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="post",
|
||||||
|
name="hashtags",
|
||||||
|
field=models.JSONField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -117,7 +117,7 @@ class Post(StatorModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Hashtags in the post
|
# Hashtags in the post
|
||||||
hashtags = models.JSONField(default=[])
|
hashtags = models.JSONField(blank=True, null=True)
|
||||||
|
|
||||||
# When the post was originally created (as opposed to when we received it)
|
# When the post was originally created (as opposed to when we received it)
|
||||||
published = models.DateTimeField(default=timezone.now)
|
published = models.DateTimeField(default=timezone.now)
|
||||||
|
@ -296,36 +296,38 @@ class Post(StatorModel):
|
||||||
"""
|
"""
|
||||||
Handles an incoming create request
|
Handles an incoming create request
|
||||||
"""
|
"""
|
||||||
# Ensure the Create actor is the Post's attributedTo
|
with transaction.atomic():
|
||||||
if data["actor"] != data["object"]["attributedTo"]:
|
# Ensure the Create actor is the Post's attributedTo
|
||||||
raise ValueError("Create actor does not match its Post object", data)
|
if data["actor"] != data["object"]["attributedTo"]:
|
||||||
# Create it
|
raise ValueError("Create actor does not match its Post object", data)
|
||||||
post = cls.by_ap(data["object"], create=True, update=True)
|
# Create it
|
||||||
# Make timeline events for followers
|
post = cls.by_ap(data["object"], create=True, update=True)
|
||||||
for follow in Follow.objects.filter(target=post.author, source__local=True):
|
# Make timeline events for followers
|
||||||
TimelineEvent.add_post(follow.source, post)
|
for follow in Follow.objects.filter(target=post.author, source__local=True):
|
||||||
# Make timeline events for mentions if they're local
|
TimelineEvent.add_post(follow.source, post)
|
||||||
for mention in post.mentions.all():
|
# Make timeline events for mentions if they're local
|
||||||
if mention.local:
|
for mention in post.mentions.all():
|
||||||
TimelineEvent.add_mentioned(mention, post)
|
if mention.local:
|
||||||
# Force it into fanned_out as it's not ours
|
TimelineEvent.add_mentioned(mention, post)
|
||||||
post.transition_perform(PostStates.fanned_out)
|
# Force it into fanned_out as it's not ours
|
||||||
|
post.transition_perform(PostStates.fanned_out)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_delete_ap(cls, data):
|
def handle_delete_ap(cls, data):
|
||||||
"""
|
"""
|
||||||
Handles an incoming create request
|
Handles an incoming create request
|
||||||
"""
|
"""
|
||||||
# Find our post by ID if we have one
|
with transaction.atomic():
|
||||||
try:
|
# Find our post by ID if we have one
|
||||||
post = cls.by_object_uri(data["object"]["id"])
|
try:
|
||||||
except cls.DoesNotExist:
|
post = cls.by_object_uri(data["object"]["id"])
|
||||||
# It's already been deleted
|
except cls.DoesNotExist:
|
||||||
return
|
# It's already been deleted
|
||||||
# Ensure the actor on the request authored the post
|
return
|
||||||
if not post.author.actor_uri == data["actor"]:
|
# Ensure the actor on the request authored the post
|
||||||
raise ValueError("Actor on delete does not match object")
|
if not post.author.actor_uri == data["actor"]:
|
||||||
post.delete()
|
raise ValueError("Actor on delete does not match object")
|
||||||
|
post.delete()
|
||||||
|
|
||||||
def debug_fetch(self):
|
def debug_fetch(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from activities.models.fan_out import FanOut
|
from activities.models.fan_out import FanOut
|
||||||
|
@ -272,31 +272,33 @@ class PostInteraction(StatorModel):
|
||||||
"""
|
"""
|
||||||
Handles an incoming announce/like
|
Handles an incoming announce/like
|
||||||
"""
|
"""
|
||||||
# Create it
|
with transaction.atomic():
|
||||||
interaction = cls.by_ap(data, create=True)
|
# Create it
|
||||||
# Boosts (announces) go to everyone who follows locally
|
interaction = cls.by_ap(data, create=True)
|
||||||
if interaction.type == cls.Types.boost:
|
# Boosts (announces) go to everyone who follows locally
|
||||||
for follow in Follow.objects.filter(
|
if interaction.type == cls.Types.boost:
|
||||||
target=interaction.identity, source__local=True
|
for follow in Follow.objects.filter(
|
||||||
):
|
target=interaction.identity, source__local=True
|
||||||
TimelineEvent.add_post_interaction(follow.source, interaction)
|
):
|
||||||
# Likes go to just the author of the post
|
TimelineEvent.add_post_interaction(follow.source, interaction)
|
||||||
elif interaction.type == cls.Types.like:
|
# Likes go to just the author of the post
|
||||||
TimelineEvent.add_post_interaction(interaction.post.author, interaction)
|
elif interaction.type == cls.Types.like:
|
||||||
# Force it into fanned_out as it's not ours
|
TimelineEvent.add_post_interaction(interaction.post.author, interaction)
|
||||||
interaction.transition_perform(PostInteractionStates.fanned_out)
|
# Force it into fanned_out as it's not ours
|
||||||
|
interaction.transition_perform(PostInteractionStates.fanned_out)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_undo_ap(cls, data):
|
def handle_undo_ap(cls, data):
|
||||||
"""
|
"""
|
||||||
Handles an incoming undo for a announce/like
|
Handles an incoming undo for a announce/like
|
||||||
"""
|
"""
|
||||||
# Find it
|
with transaction.atomic():
|
||||||
interaction = cls.by_ap(data["object"])
|
# Find it
|
||||||
# Verify the actor matches
|
interaction = cls.by_ap(data["object"])
|
||||||
if data["actor"] != interaction.identity.actor_uri:
|
# Verify the actor matches
|
||||||
raise ValueError("Actor mismatch on interaction undo")
|
if data["actor"] != interaction.identity.actor_uri:
|
||||||
# Delete all events that reference it
|
raise ValueError("Actor mismatch on interaction undo")
|
||||||
interaction.timeline_events.all().delete()
|
# Delete all events that reference it
|
||||||
# Force it into undone_fanned_out as it's not ours
|
interaction.timeline_events.all().delete()
|
||||||
interaction.transition_perform(PostInteractionStates.undone_fanned_out)
|
# Force it into undone_fanned_out as it's not ours
|
||||||
|
interaction.transition_perform(PostInteractionStates.undone_fanned_out)
|
||||||
|
|
|
@ -108,7 +108,6 @@ class Boost(View):
|
||||||
class Compose(FormView):
|
class Compose(FormView):
|
||||||
|
|
||||||
template_name = "activities/compose.html"
|
template_name = "activities/compose.html"
|
||||||
extra_context = {"top_section": "compose"}
|
|
||||||
|
|
||||||
class form_class(forms.Form):
|
class form_class(forms.Form):
|
||||||
text = forms.CharField(
|
text = forms.CharField(
|
||||||
|
|
|
@ -7,4 +7,5 @@ def config_context(request):
|
||||||
"config_identity": (
|
"config_identity": (
|
||||||
Config.load_identity(request.identity) if request.identity else None
|
Config.load_identity(request.identity) if request.identity else None
|
||||||
),
|
),
|
||||||
|
"top_section": request.path.strip("/").split("/")[0],
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,10 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
html:focus-within {
|
html:focus-within {
|
||||||
scroll-behavior: auto;
|
scroll-behavior: auto;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin as djadmin
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from activities.views import posts, timelines
|
from activities.views import posts, timelines
|
||||||
from core import views as core
|
from core import views as core
|
||||||
from stator import views as stator
|
from stator import views as stator
|
||||||
from users.views import activitypub, auth, identity, settings_identity, settings_system
|
from users.views import activitypub, admin, auth, identity, settings
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", core.homepage),
|
path("", core.homepage),
|
||||||
|
@ -13,16 +13,53 @@ urlpatterns = [
|
||||||
path("notifications/", timelines.Notifications.as_view()),
|
path("notifications/", timelines.Notifications.as_view()),
|
||||||
path("local/", timelines.Local.as_view()),
|
path("local/", timelines.Local.as_view()),
|
||||||
path("federated/", timelines.Federated.as_view()),
|
path("federated/", timelines.Federated.as_view()),
|
||||||
path("settings/", settings_identity.IdentitySettingsRoot.as_view()),
|
|
||||||
path("settings/interface/", settings_identity.InterfacePage.as_view()),
|
|
||||||
path("settings/system/", settings_system.SystemSettingsRoot.as_view()),
|
|
||||||
path("settings/system/basic/", settings_system.BasicPage.as_view()),
|
|
||||||
path("settings/system/domains/", settings_system.DomainsPage.as_view()),
|
|
||||||
path("settings/system/domains/create/", settings_system.DomainCreatePage.as_view()),
|
|
||||||
path("settings/system/domains/<domain>/", settings_system.DomainEditPage.as_view()),
|
|
||||||
path(
|
path(
|
||||||
"settings/system/domains/<domain>/delete/",
|
"settings/",
|
||||||
settings_system.DomainDeletePage.as_view(),
|
settings.SettingsRoot.as_view(),
|
||||||
|
name="settings",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"settings/interface/",
|
||||||
|
settings.InterfacePage.as_view(),
|
||||||
|
name="settings_interface",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/",
|
||||||
|
admin.AdminRoot.as_view(),
|
||||||
|
name="admin",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/basic/",
|
||||||
|
admin.BasicPage.as_view(),
|
||||||
|
name="admin_basic",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/domains/",
|
||||||
|
admin.DomainsPage.as_view(),
|
||||||
|
name="admin_domains",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/domains/create/",
|
||||||
|
admin.DomainCreatePage.as_view(),
|
||||||
|
name="admin_domains_create",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/domains/<domain>/",
|
||||||
|
admin.DomainEditPage.as_view(),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/domains/<domain>/delete/",
|
||||||
|
admin.DomainDeletePage.as_view(),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/users/",
|
||||||
|
admin.UsersPage.as_view(),
|
||||||
|
name="admin_users",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"admin/identities/",
|
||||||
|
admin.IdentitiesPage.as_view(),
|
||||||
|
name="admin_identities",
|
||||||
),
|
),
|
||||||
# Identity views
|
# Identity views
|
||||||
path("@<handle>/", identity.ViewIdentity.as_view()),
|
path("@<handle>/", identity.ViewIdentity.as_view()),
|
||||||
|
@ -49,5 +86,5 @@ urlpatterns = [
|
||||||
# Task runner
|
# Task runner
|
||||||
path(".stator/runner/", stator.RequestRunner.as_view()),
|
path(".stator/runner/", stator.RequestRunner.as_view()),
|
||||||
# Django admin
|
# Django admin
|
||||||
path("djadmin/", admin.site.urls),
|
path("djadmin/", djadmin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
6
templates/admin/_menu.html
Normal file
6
templates/admin/_menu.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<nav>
|
||||||
|
<a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %}>Basic</a>
|
||||||
|
<a href="{% url "admin_domains" %}" {% if section == "domains" %}class="selected"{% endif %}>Domains</a>
|
||||||
|
<a href="{% url "admin_users" %}" {% if section == "users" %}class="selected"{% endif %}>Users</a>
|
||||||
|
<a href="{% url "admin_identities" %}" {% if section == "identities" %}class="selected"{% endif %}>Identities</a>
|
||||||
|
</nav>
|
|
@ -1,10 +1,10 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}Add Domain - System Settings{% endblock %}
|
{% block title %}Add Domain - Admin{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{% include "settings/_settings_system_menu.html" %}
|
{% include "admin/_menu.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<form action="." method="POST">
|
<form action="." method="POST">
|
||||||
<h1>Add A Domain</h1>
|
<h1>Add A Domain</h1>
|
|
@ -1,10 +1,10 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}Delete {{ domain.domain }} - System Settings{% endblock %}
|
{% block title %}Delete {{ domain.domain }} - Admin{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{% include "settings/_settings_system_menu.html" %}
|
{% include "admin/_menu.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<form action="." method="POST">
|
<form action="." method="POST">
|
|
@ -1,10 +1,10 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}{{ domain.domain }} - System Settings{% endblock %}
|
{% block title %}{{ domain.domain }} - Admin{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{% include "settings/_settings_system_menu.html" %}
|
{% include "admin/_menu.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<form action="." method="POST">
|
<form action="." method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
|
@ -1,10 +1,10 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}{{ section.title }} - System Settings{% endblock %}
|
{% block title %}{{ section.title }} - Admin{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{% include "settings/_settings_system_menu.html" %}
|
{% include "admin/_menu.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<section class="icon-menu">
|
<section class="icon-menu">
|
||||||
{% for domain in domains %}
|
{% for domain in domains %}
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<p class="option empty">You have no domains set up.</p>
|
<p class="option empty">You have no domains set up.</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<a href="/settings/system/domains/create/" class="option new">
|
<a href="{% url "admin_domains_create" %}" class="option new">
|
||||||
<i class="fa-solid fa-plus"></i> Add a domain
|
<i class="fa-solid fa-plus"></i> Add a domain
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
14
templates/admin/identities.html
Normal file
14
templates/admin/identities.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Identities - Admin{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% block menu %}
|
||||||
|
{% include "admin/_menu.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
<form>
|
||||||
|
<p>
|
||||||
|
Please use the <a href="/djadmin/users/identity/">Django Admin</a> for now.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -1,10 +1,10 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block title %}{{ section.title }} - System Settings{% endblock %}
|
{% block title %}{{ section.title }} - Admin{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block menu %}
|
{% block menu %}
|
||||||
{% include "settings/_settings_system_menu.html" %}
|
{% include "admin/_menu.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<form action="." method="POST">
|
<form action="." method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
14
templates/admin/users.html
Normal file
14
templates/admin/users.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Users - Admin{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% block menu %}
|
||||||
|
{% include "admin/_menu.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
<form>
|
||||||
|
<p>
|
||||||
|
Please use the <a href="/djadmin/users/user/">Django Admin</a> for now.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -31,11 +31,11 @@
|
||||||
<a href="/compose/" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}>
|
<a href="/compose/" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}>
|
||||||
<i class="fa-solid fa-feather"></i> Compose
|
<i class="fa-solid fa-feather"></i> Compose
|
||||||
</a>
|
</a>
|
||||||
<a href="/settings/" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}>
|
<a href="{% url "settings" %}" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}>
|
||||||
<i class="fa-solid fa-gear"></i> Settings
|
<i class="fa-solid fa-gear"></i> Settings
|
||||||
</a>
|
</a>
|
||||||
{% if request.user.admin %}
|
{% if request.user.admin %}
|
||||||
<a href="/settings/system/" title="Admin" {% if top_section == "settings_system" %}class="selected"{% endif %}>
|
<a href="{% url "admin" %}" title="Admin" {% if top_section == "admin" %}class="selected"{% endif %}>
|
||||||
<i class="fa-solid fa-toolbox"></i> Admin
|
<i class="fa-solid fa-toolbox"></i> Admin
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<nav>
|
|
||||||
<a href="/settings/system/basic/" {% if section == "basic" %}class="selected"{% endif %}>Basic</a>
|
|
||||||
<a href="/settings/system/domains/" {% if section == "domains" %}class="selected"{% endif %}>Domains</a>
|
|
||||||
<a href="/settings/system/users/" {% if section == "users" %}class="selected"{% endif %}>Users</a>
|
|
||||||
</nav>
|
|
18
templates/settings/settings.html
Normal file
18
templates/settings/settings.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ section.title }} - Settings{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% block menu %}
|
||||||
|
{% include "settings/_menu.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
<form action="." method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% include "forms/_field.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
<div class="buttons">
|
||||||
|
<button>Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
||||||
{% extends "settings/settings_system.html" %}
|
|
||||||
|
|
||||||
{% block title %}{{ section.title }} - Settings{% endblock %}
|
|
||||||
|
|
||||||
{% block menu %}
|
|
||||||
{% include "settings/_settings_identity_menu.html" %}
|
|
||||||
{% endblock %}
|
|
|
@ -10,7 +10,7 @@ class DomainAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class UserAdmin(admin.ModelAdmin):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
pass
|
list_display = ["email", "created", "last_seen", "admin", "moderator", "banned"]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(UserEvent)
|
@admin.register(UserEvent)
|
||||||
|
@ -21,6 +21,7 @@ class UserEventAdmin(admin.ModelAdmin):
|
||||||
@admin.register(Identity)
|
@admin.register(Identity)
|
||||||
class IdentityAdmin(admin.ModelAdmin):
|
class IdentityAdmin(admin.ModelAdmin):
|
||||||
list_display = ["id", "handle", "actor_uri", "state", "local"]
|
list_display = ["id", "handle", "actor_uri", "state", "local"]
|
||||||
|
list_filter = ["local"]
|
||||||
raw_id_fields = ["users"]
|
raw_id_fields = ["users"]
|
||||||
actions = ["force_update"]
|
actions = ["force_update"]
|
||||||
readonly_fields = ["actor_json"]
|
readonly_fields = ["actor_json"]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from users.models import Identity
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from users.models import Identity, User
|
||||||
|
|
||||||
|
|
||||||
class IdentityMiddleware:
|
class IdentityMiddleware:
|
||||||
|
@ -17,6 +19,7 @@ class IdentityMiddleware:
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
request.identity = Identity.objects.get(id=identity_id)
|
request.identity = Identity.objects.get(id=identity_id)
|
||||||
|
User.objects.filter(pk=request.user.pk).update(last_seen=timezone.now())
|
||||||
except Identity.DoesNotExist:
|
except Identity.DoesNotExist:
|
||||||
request.identity = None
|
request.identity = None
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Generated by Django 4.1.3 on 2022-11-17 04:18
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("users", "0002_identity_public_key_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="user",
|
||||||
|
name="last_seen",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
auto_now_add=True, default=django.utils.timezone.now
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="identity",
|
||||||
|
name="domain",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="identities",
|
||||||
|
to="users.domain",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -49,10 +49,10 @@ class Domain(models.Model):
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
class urls(urlman.Urls):
|
class urls(urlman.Urls):
|
||||||
root = "/settings/system/domains/"
|
root = "/admin/domains/"
|
||||||
create = "/settings/system/domains/create/"
|
create = "/admin/domains/create/"
|
||||||
edit = "/settings/system/domains/{self.domain}/"
|
edit = "/admin/domains/{self.domain}/"
|
||||||
delete = "/settings/system/domains/{self.domain}/delete/"
|
delete = "/admin/domains/{self.domain}/delete/"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_remote_domain(cls, domain: str) -> "Domain":
|
def get_remote_domain(cls, domain: str) -> "Domain":
|
||||||
|
|
|
@ -38,6 +38,7 @@ class User(AbstractBaseUser):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
last_seen = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
USERNAME_FIELD = "email"
|
USERNAME_FIELD = "email"
|
||||||
EMAIL_FIELD = "email"
|
EMAIL_FIELD = "email"
|
||||||
|
|
|
@ -10,28 +10,26 @@ from django.views.generic import FormView, RedirectView, TemplateView
|
||||||
|
|
||||||
from core.models import Config
|
from core.models import Config
|
||||||
from users.decorators import admin_required
|
from users.decorators import admin_required
|
||||||
from users.models import Domain
|
from users.models import Domain, Identity, User
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class SystemSettingsRoot(RedirectView):
|
class AdminRoot(RedirectView):
|
||||||
url = "/settings/system/basic/"
|
pattern_name = "admin_basic"
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class SystemSettingsPage(FormView):
|
class AdminSettingsPage(FormView):
|
||||||
"""
|
"""
|
||||||
Shows a settings page dynamically created from our settings layout
|
Shows a settings page dynamically created from our settings layout
|
||||||
at the bottom of the page. Don't add this to a URL directly - subclass!
|
at the bottom of the page. Don't add this to a URL directly - subclass!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
template_name = "settings/settings_system.html"
|
template_name = "admin/settings.html"
|
||||||
options_class = Config.SystemOptions
|
options_class = Config.SystemOptions
|
||||||
section: ClassVar[str]
|
section: ClassVar[str]
|
||||||
options: Dict[str, Dict[str, str]]
|
options: Dict[str, Dict[str, str]]
|
||||||
|
|
||||||
extra_context = {"top_section": "settings_system"}
|
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
# Create the fields dict from the config object
|
# Create the fields dict from the config object
|
||||||
fields = {}
|
fields = {}
|
||||||
|
@ -84,7 +82,7 @@ class SystemSettingsPage(FormView):
|
||||||
return redirect(".")
|
return redirect(".")
|
||||||
|
|
||||||
|
|
||||||
class BasicPage(SystemSettingsPage):
|
class BasicPage(AdminSettingsPage):
|
||||||
|
|
||||||
section = "basic"
|
section = "basic"
|
||||||
|
|
||||||
|
@ -103,7 +101,7 @@ class BasicPage(SystemSettingsPage):
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class DomainsPage(TemplateView):
|
class DomainsPage(TemplateView):
|
||||||
|
|
||||||
template_name = "settings/settings_system_domains.html"
|
template_name = "admin/domains.html"
|
||||||
|
|
||||||
def get_context_data(self):
|
def get_context_data(self):
|
||||||
return {
|
return {
|
||||||
|
@ -115,7 +113,7 @@ class DomainsPage(TemplateView):
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class DomainCreatePage(FormView):
|
class DomainCreatePage(FormView):
|
||||||
|
|
||||||
template_name = "settings/settings_system_domain_create.html"
|
template_name = "admin/domain_create.html"
|
||||||
extra_context = {"section": "domains"}
|
extra_context = {"section": "domains"}
|
||||||
|
|
||||||
class form_class(forms.Form):
|
class form_class(forms.Form):
|
||||||
|
@ -175,7 +173,7 @@ class DomainCreatePage(FormView):
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class DomainEditPage(FormView):
|
class DomainEditPage(FormView):
|
||||||
|
|
||||||
template_name = "settings/settings_system_domain_edit.html"
|
template_name = "admin/domain_edit.html"
|
||||||
extra_context = {"section": "domains"}
|
extra_context = {"section": "domains"}
|
||||||
|
|
||||||
class form_class(forms.Form):
|
class form_class(forms.Form):
|
||||||
|
@ -221,7 +219,7 @@ class DomainEditPage(FormView):
|
||||||
@method_decorator(admin_required, name="dispatch")
|
@method_decorator(admin_required, name="dispatch")
|
||||||
class DomainDeletePage(TemplateView):
|
class DomainDeletePage(TemplateView):
|
||||||
|
|
||||||
template_name = "settings/settings_system_domain_delete.html"
|
template_name = "admin/domain_delete.html"
|
||||||
|
|
||||||
def dispatch(self, request, domain):
|
def dispatch(self, request, domain):
|
||||||
self.domain = get_object_or_404(
|
self.domain = get_object_or_404(
|
||||||
|
@ -241,3 +239,27 @@ class DomainDeletePage(TemplateView):
|
||||||
raise ValueError("Tried to delete domain with identities!")
|
raise ValueError("Tried to delete domain with identities!")
|
||||||
self.domain.delete()
|
self.domain.delete()
|
||||||
return redirect("/settings/system/domains/")
|
return redirect("/settings/system/domains/")
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(admin_required, name="dispatch")
|
||||||
|
class UsersPage(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")
|
||||||
|
class IdentitiesPage(TemplateView):
|
||||||
|
|
||||||
|
template_name = "admin/identities.html"
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
|
return {
|
||||||
|
"identities": Identity.objects.order_by("username"),
|
||||||
|
"section": "identities",
|
||||||
|
}
|
|
@ -3,24 +3,22 @@ from django.views.generic import RedirectView
|
||||||
|
|
||||||
from core.models import Config
|
from core.models import Config
|
||||||
from users.decorators import identity_required
|
from users.decorators import identity_required
|
||||||
from users.views.settings_system import SystemSettingsPage
|
from users.views.admin import AdminSettingsPage
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(identity_required, name="dispatch")
|
@method_decorator(identity_required, name="dispatch")
|
||||||
class IdentitySettingsRoot(RedirectView):
|
class SettingsRoot(RedirectView):
|
||||||
url = "/settings/interface/"
|
url = "/settings/interface/"
|
||||||
|
|
||||||
|
|
||||||
class IdentitySettingsPage(SystemSettingsPage):
|
class SettingsPage(AdminSettingsPage):
|
||||||
"""
|
"""
|
||||||
Shows a settings page dynamically created from our settings layout
|
Shows a settings page dynamically created from our settings layout
|
||||||
at the bottom of the page. Don't add this to a URL directly - subclass!
|
at the bottom of the page. Don't add this to a URL directly - subclass!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
extra_context = {"top_section": "settings"}
|
|
||||||
|
|
||||||
options_class = Config.IdentityOptions
|
options_class = Config.IdentityOptions
|
||||||
template_name = "settings/settings_identity.html"
|
template_name = "settings/settings.html"
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
return Config.load_identity(self.request.identity)
|
return Config.load_identity(self.request.identity)
|
||||||
|
@ -29,7 +27,7 @@ class IdentitySettingsPage(SystemSettingsPage):
|
||||||
Config.set_identity(self.request.identity, key, value)
|
Config.set_identity(self.request.identity, key, value)
|
||||||
|
|
||||||
|
|
||||||
class InterfacePage(IdentitySettingsPage):
|
class InterfacePage(SettingsPage):
|
||||||
|
|
||||||
section = "interface"
|
section = "interface"
|
||||||
|
|
Loading…
Reference in a new issue