summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activities/migrations/0006_alter_post_hashtags.py18
-rw-r--r--activities/models/post.py52
-rw-r--r--activities/models/post_interaction.py48
-rw-r--r--activities/views/posts.py1
-rw-r--r--core/context.py1
-rw-r--r--static/css/style.css4
-rw-r--r--takahe/urls.py61
-rw-r--r--templates/admin/_menu.html6
-rw-r--r--templates/admin/domain_create.html (renamed from templates/settings/settings_system_domain_create.html)4
-rw-r--r--templates/admin/domain_delete.html (renamed from templates/settings/settings_system_domain_delete.html)4
-rw-r--r--templates/admin/domain_edit.html (renamed from templates/settings/settings_system_domain_edit.html)4
-rw-r--r--templates/admin/domains.html (renamed from templates/settings/settings_system_domains.html)6
-rw-r--r--templates/admin/identities.html14
-rw-r--r--templates/admin/settings.html (renamed from templates/settings/settings_system.html)4
-rw-r--r--templates/admin/users.html14
-rw-r--r--templates/base.html4
-rw-r--r--templates/settings/_menu.html (renamed from templates/settings/_settings_identity_menu.html)0
-rw-r--r--templates/settings/_settings_system_menu.html5
-rw-r--r--templates/settings/settings.html18
-rw-r--r--templates/settings/settings_identity.html7
-rw-r--r--users/admin.py3
-rw-r--r--users/middleware.py5
-rw-r--r--users/migrations/0003_user_last_seen_alter_identity_domain.py34
-rw-r--r--users/models/domain.py8
-rw-r--r--users/models/user.py1
-rw-r--r--users/views/admin.py (renamed from users/views/settings_system.py)46
-rw-r--r--users/views/settings.py (renamed from users/views/settings_identity.py)12
27 files changed, 273 insertions, 111 deletions
diff --git a/activities/migrations/0006_alter_post_hashtags.py b/activities/migrations/0006_alter_post_hashtags.py
new file mode 100644
index 0000000..b6149ea
--- /dev/null
+++ b/activities/migrations/0006_alter_post_hashtags.py
@@ -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),
+ ),
+ ]
diff --git a/activities/models/post.py b/activities/models/post.py
index 4896e58..3847b63 100644
--- a/activities/models/post.py
+++ b/activities/models/post.py
@@ -117,7 +117,7 @@ class Post(StatorModel):
)
# 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)
published = models.DateTimeField(default=timezone.now)
@@ -296,36 +296,38 @@ class Post(StatorModel):
"""
Handles an incoming create request
"""
- # Ensure the Create actor is the Post's attributedTo
- if data["actor"] != data["object"]["attributedTo"]:
- raise ValueError("Create actor does not match its Post object", data)
- # Create it
- post = cls.by_ap(data["object"], create=True, update=True)
- # Make timeline events for followers
- for follow in Follow.objects.filter(target=post.author, source__local=True):
- TimelineEvent.add_post(follow.source, post)
- # Make timeline events for mentions if they're local
- for mention in post.mentions.all():
- if mention.local:
- TimelineEvent.add_mentioned(mention, post)
- # Force it into fanned_out as it's not ours
- post.transition_perform(PostStates.fanned_out)
+ with transaction.atomic():
+ # Ensure the Create actor is the Post's attributedTo
+ if data["actor"] != data["object"]["attributedTo"]:
+ raise ValueError("Create actor does not match its Post object", data)
+ # Create it
+ post = cls.by_ap(data["object"], create=True, update=True)
+ # Make timeline events for followers
+ for follow in Follow.objects.filter(target=post.author, source__local=True):
+ TimelineEvent.add_post(follow.source, post)
+ # Make timeline events for mentions if they're local
+ for mention in post.mentions.all():
+ if mention.local:
+ TimelineEvent.add_mentioned(mention, post)
+ # Force it into fanned_out as it's not ours
+ post.transition_perform(PostStates.fanned_out)
@classmethod
def handle_delete_ap(cls, data):
"""
Handles an incoming create request
"""
- # Find our post by ID if we have one
- try:
- post = cls.by_object_uri(data["object"]["id"])
- except cls.DoesNotExist:
- # It's already been deleted
- return
- # Ensure the actor on the request authored the post
- if not post.author.actor_uri == data["actor"]:
- raise ValueError("Actor on delete does not match object")
- post.delete()
+ with transaction.atomic():
+ # Find our post by ID if we have one
+ try:
+ post = cls.by_object_uri(data["object"]["id"])
+ except cls.DoesNotExist:
+ # It's already been deleted
+ return
+ # Ensure the actor on the request authored the post
+ if not post.author.actor_uri == data["actor"]:
+ raise ValueError("Actor on delete does not match object")
+ post.delete()
def debug_fetch(self):
"""
diff --git a/activities/models/post_interaction.py b/activities/models/post_interaction.py
index 4f1eb03..e9248e8 100644
--- a/activities/models/post_interaction.py
+++ b/activities/models/post_interaction.py
@@ -1,6 +1,6 @@
from typing import Dict
-from django.db import models
+from django.db import models, transaction
from django.utils import timezone
from activities.models.fan_out import FanOut
@@ -272,31 +272,33 @@ class PostInteraction(StatorModel):
"""
Handles an incoming announce/like
"""
- # Create it
- interaction = cls.by_ap(data, create=True)
- # Boosts (announces) go to everyone who follows locally
- if interaction.type == cls.Types.boost:
- 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
- elif interaction.type == cls.Types.like:
- TimelineEvent.add_post_interaction(interaction.post.author, interaction)
- # Force it into fanned_out as it's not ours
- interaction.transition_perform(PostInteractionStates.fanned_out)
+ with transaction.atomic():
+ # Create it
+ interaction = cls.by_ap(data, create=True)
+ # Boosts (announces) go to everyone who follows locally
+ if interaction.type == cls.Types.boost:
+ 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
+ elif interaction.type == cls.Types.like:
+ TimelineEvent.add_post_interaction(interaction.post.author, interaction)
+ # Force it into fanned_out as it's not ours
+ interaction.transition_perform(PostInteractionStates.fanned_out)
@classmethod
def handle_undo_ap(cls, data):
"""
Handles an incoming undo for a announce/like
"""
- # Find it
- interaction = cls.by_ap(data["object"])
- # Verify the actor matches
- if data["actor"] != interaction.identity.actor_uri:
- raise ValueError("Actor mismatch on interaction undo")
- # Delete all events that reference it
- interaction.timeline_events.all().delete()
- # Force it into undone_fanned_out as it's not ours
- interaction.transition_perform(PostInteractionStates.undone_fanned_out)
+ with transaction.atomic():
+ # Find it
+ interaction = cls.by_ap(data["object"])
+ # Verify the actor matches
+ if data["actor"] != interaction.identity.actor_uri:
+ raise ValueError("Actor mismatch on interaction undo")
+ # Delete all events that reference it
+ interaction.timeline_events.all().delete()
+ # Force it into undone_fanned_out as it's not ours
+ interaction.transition_perform(PostInteractionStates.undone_fanned_out)
diff --git a/activities/views/posts.py b/activities/views/posts.py
index d0ad813..3ee35cc 100644
--- a/activities/views/posts.py
+++ b/activities/views/posts.py
@@ -108,7 +108,6 @@ class Boost(View):
class Compose(FormView):
template_name = "activities/compose.html"
- extra_context = {"top_section": "compose"}
class form_class(forms.Form):
text = forms.CharField(
diff --git a/core/context.py b/core/context.py
index 4346cbb..a4aabf5 100644
--- a/core/context.py
+++ b/core/context.py
@@ -7,4 +7,5 @@ def config_context(request):
"config_identity": (
Config.load_identity(request.identity) if request.identity else None
),
+ "top_section": request.path.strip("/").split("/")[0],
}
diff --git a/static/css/style.css b/static/css/style.css
index 9c45eb3..f07d78c 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -58,6 +58,10 @@ a {
text-decoration: none;
}
+p a {
+ text-decoration: underline;
+}
+
@media (prefers-reduced-motion: reduce) {
html:focus-within {
scroll-behavior: auto;
diff --git a/takahe/urls.py b/takahe/urls.py
index 0643440..638dabd 100644
--- a/takahe/urls.py
+++ b/takahe/urls.py
@@ -1,10 +1,10 @@
-from django.contrib import admin
+from django.contrib import admin as djadmin
from django.urls import path
from activities.views import posts, timelines
from core import views as core
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 = [
path("", core.homepage),
@@ -13,16 +13,53 @@ urlpatterns = [
path("notifications/", timelines.Notifications.as_view()),
path("local/", timelines.Local.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(
- "settings/system/domains/<domain>/delete/",
- settings_system.DomainDeletePage.as_view(),
+ "settings/",
+ 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
path("@<handle>/", identity.ViewIdentity.as_view()),
@@ -49,5 +86,5 @@ urlpatterns = [
# Task runner
path(".stator/runner/", stator.RequestRunner.as_view()),
# Django admin
- path("djadmin/", admin.site.urls),
+ path("djadmin/", djadmin.site.urls),
]
diff --git a/templates/admin/_menu.html b/templates/admin/_menu.html
new file mode 100644
index 0000000..8f0bc60
--- /dev/null
+++ b/templates/admin/_menu.html
@@ -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>
diff --git a/templates/settings/settings_system_domain_create.html b/templates/admin/domain_create.html
index 54d3640..09dbc23 100644
--- a/templates/settings/settings_system_domain_create.html
+++ b/templates/admin/domain_create.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block title %}Add Domain - System Settings{% endblock %}
+{% block title %}Add Domain - Admin{% endblock %}
{% block content %}
{% block menu %}
- {% include "settings/_settings_system_menu.html" %}
+ {% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST">
<h1>Add A Domain</h1>
diff --git a/templates/settings/settings_system_domain_delete.html b/templates/admin/domain_delete.html
index 220bbb9..d47a673 100644
--- a/templates/settings/settings_system_domain_delete.html
+++ b/templates/admin/domain_delete.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block title %}Delete {{ domain.domain }} - System Settings{% endblock %}
+{% block title %}Delete {{ domain.domain }} - Admin{% endblock %}
{% block content %}
{% block menu %}
- {% include "settings/_settings_system_menu.html" %}
+ {% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST">
diff --git a/templates/settings/settings_system_domain_edit.html b/templates/admin/domain_edit.html
index c05d5d5..64e195c 100644
--- a/templates/settings/settings_system_domain_edit.html
+++ b/templates/admin/domain_edit.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block title %}{{ domain.domain }} - System Settings{% endblock %}
+{% block title %}{{ domain.domain }} - Admin{% endblock %}
{% block content %}
{% block menu %}
- {% include "settings/_settings_system_menu.html" %}
+ {% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST">
{% csrf_token %}
diff --git a/templates/settings/settings_system_domains.html b/templates/admin/domains.html
index dccde65..b7925da 100644
--- a/templates/settings/settings_system_domains.html
+++ b/templates/admin/domains.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block title %}{{ section.title }} - System Settings{% endblock %}
+{% block title %}{{ section.title }} - Admin{% endblock %}
{% block content %}
{% block menu %}
- {% include "settings/_settings_system_menu.html" %}
+ {% include "admin/_menu.html" %}
{% endblock %}
<section class="icon-menu">
{% for domain in domains %}
@@ -21,7 +21,7 @@
{% empty %}
<p class="option empty">You have no domains set up.</p>
{% 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
</a>
</section>
diff --git a/templates/admin/identities.html b/templates/admin/identities.html
new file mode 100644
index 0000000..86e70db
--- /dev/null
+++ b/templates/admin/identities.html
@@ -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 %}
diff --git a/templates/settings/settings_system.html b/templates/admin/settings.html
index c10964f..e031347 100644
--- a/templates/settings/settings_system.html
+++ b/templates/admin/settings.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block title %}{{ section.title }} - System Settings{% endblock %}
+{% block title %}{{ section.title }} - Admin{% endblock %}
{% block content %}
{% block menu %}
- {% include "settings/_settings_system_menu.html" %}
+ {% include "admin/_menu.html" %}
{% endblock %}
<form action="." method="POST">
{% csrf_token %}
diff --git a/templates/admin/users.html b/templates/admin/users.html
new file mode 100644
index 0000000..0b75b88
--- /dev/null
+++ b/templates/admin/users.html
@@ -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 %}
diff --git a/templates/base.html b/templates/base.html
index 402dcd3..bce5e1b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -31,11 +31,11 @@
<a href="/compose/" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}>
<i class="fa-solid fa-feather"></i> Compose
</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
</a>
{% 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
</a>
{% endif %}
diff --git a/templates/settings/_settings_identity_menu.html b/templates/settings/_menu.html
index bdae143..bdae143 100644
--- a/templates/settings/_settings_identity_menu.html
+++ b/templates/settings/_menu.html
diff --git a/templates/settings/_settings_system_menu.html b/templates/settings/_settings_system_menu.html
deleted file mode 100644
index 9206045..0000000
--- a/templates/settings/_settings_system_menu.html
+++ /dev/null
@@ -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>
diff --git a/templates/settings/settings.html b/templates/settings/settings.html
new file mode 100644
index 0000000..016eebb
--- /dev/null
+++ b/templates/settings/settings.html
@@ -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 %}
diff --git a/templates/settings/settings_identity.html b/templates/settings/settings_identity.html
deleted file mode 100644
index cdbf197..0000000
--- a/templates/settings/settings_identity.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "settings/settings_system.html" %}
-
-{% block title %}{{ section.title }} - Settings{% endblock %}
-
-{% block menu %}
- {% include "settings/_settings_identity_menu.html" %}
-{% endblock %}
diff --git a/users/admin.py b/users/admin.py
index 5364880..7c3750d 100644
--- a/users/admin.py
+++ b/users/admin.py
@@ -10,7 +10,7 @@ class DomainAdmin(admin.ModelAdmin):
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
- pass
+ list_display = ["email", "created", "last_seen", "admin", "moderator", "banned"]
@admin.register(UserEvent)
@@ -21,6 +21,7 @@ class UserEventAdmin(admin.ModelAdmin):
@admin.register(Identity)
class IdentityAdmin(admin.ModelAdmin):
list_display = ["id", "handle", "actor_uri", "state", "local"]
+ list_filter = ["local"]
raw_id_fields = ["users"]
actions = ["force_update"]
readonly_fields = ["actor_json"]
diff --git a/users/middleware.py b/users/middleware.py
index aa22178..e6d4036 100644
--- a/users/middleware.py
+++ b/users/middleware.py
@@ -1,4 +1,6 @@
-from users.models import Identity
+from django.utils import timezone
+
+from users.models import Identity, User
class IdentityMiddleware:
@@ -17,6 +19,7 @@ class IdentityMiddleware:
else:
try:
request.identity = Identity.objects.get(id=identity_id)
+ User.objects.filter(pk=request.user.pk).update(last_seen=timezone.now())
except Identity.DoesNotExist:
request.identity = None
diff --git a/users/migrations/0003_user_last_seen_alter_identity_domain.py b/users/migrations/0003_user_last_seen_alter_identity_domain.py
new file mode 100644
index 0000000..b6c49d1
--- /dev/null
+++ b/users/migrations/0003_user_last_seen_alter_identity_domain.py
@@ -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",
+ ),
+ ),
+ ]
diff --git a/users/models/domain.py b/users/models/domain.py
index af0bbab..4743503 100644
--- a/users/models/domain.py
+++ b/users/models/domain.py
@@ -49,10 +49,10 @@ class Domain(models.Model):
updated = models.DateTimeField(auto_now=True)
class urls(urlman.Urls):
- root = "/settings/system/domains/"
- create = "/settings/system/domains/create/"
- edit = "/settings/system/domains/{self.domain}/"
- delete = "/settings/system/domains/{self.domain}/delete/"
+ root = "/admin/domains/"
+ create = "/admin/domains/create/"
+ edit = "/admin/domains/{self.domain}/"
+ delete = "/admin/domains/{self.domain}/delete/"
@classmethod
def get_remote_domain(cls, domain: str) -> "Domain":
diff --git a/users/models/user.py b/users/models/user.py
index 6435bf5..08a703e 100644
--- a/users/models/user.py
+++ b/users/models/user.py
@@ -38,6 +38,7 @@ class User(AbstractBaseUser):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
+ last_seen = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = "email"
EMAIL_FIELD = "email"
diff --git a/users/views/settings_system.py b/users/views/admin.py
index e5e9e85..c1210f1 100644
--- a/users/views/settings_system.py
+++ b/users/views/admin.py
@@ -10,28 +10,26 @@ from django.views.generic import FormView, RedirectView, TemplateView
from core.models import Config
from users.decorators import admin_required
-from users.models import Domain
+from users.models import Domain, Identity, User
@method_decorator(admin_required, name="dispatch")
-class SystemSettingsRoot(RedirectView):
- url = "/settings/system/basic/"
+class AdminRoot(RedirectView):
+ pattern_name = "admin_basic"
@method_decorator(admin_required, name="dispatch")
-class SystemSettingsPage(FormView):
+class AdminSettingsPage(FormView):
"""
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!
"""
- template_name = "settings/settings_system.html"
+ template_name = "admin/settings.html"
options_class = Config.SystemOptions
section: ClassVar[str]
options: Dict[str, Dict[str, str]]
- extra_context = {"top_section": "settings_system"}
-
def get_form_class(self):
# Create the fields dict from the config object
fields = {}
@@ -84,7 +82,7 @@ class SystemSettingsPage(FormView):
return redirect(".")
-class BasicPage(SystemSettingsPage):
+class BasicPage(AdminSettingsPage):
section = "basic"
@@ -103,7 +101,7 @@ class BasicPage(SystemSettingsPage):
@method_decorator(admin_required, name="dispatch")
class DomainsPage(TemplateView):
- template_name = "settings/settings_system_domains.html"
+ template_name = "admin/domains.html"
def get_context_data(self):
return {
@@ -115,7 +113,7 @@ class DomainsPage(TemplateView):
@method_decorator(admin_required, name="dispatch")
class DomainCreatePage(FormView):
- template_name = "settings/settings_system_domain_create.html"
+ template_name = "admin/domain_create.html"
extra_context = {"section": "domains"}
class form_class(forms.Form):
@@ -175,7 +173,7 @@ class DomainCreatePage(FormView):
@method_decorator(admin_required, name="dispatch")
class DomainEditPage(FormView):
- template_name = "settings/settings_system_domain_edit.html"
+ template_name = "admin/domain_edit.html"
extra_context = {"section": "domains"}
class form_class(forms.Form):
@@ -221,7 +219,7 @@ class DomainEditPage(FormView):
@method_decorator(admin_required, name="dispatch")
class DomainDeletePage(TemplateView):
- template_name = "settings/settings_system_domain_delete.html"
+ template_name = "admin/domain_delete.html"
def dispatch(self, request, domain):
self.domain = get_object_or_404(
@@ -241,3 +239,27 @@ class DomainDeletePage(TemplateView):
raise ValueError("Tried to delete domain with identities!")
self.domain.delete()
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",
+ }
diff --git a/users/views/settings_identity.py b/users/views/settings.py
index f35928a..877ad01 100644
--- a/users/views/settings_identity.py
+++ b/users/views/settings.py
@@ -3,24 +3,22 @@ from django.views.generic import RedirectView
from core.models import Config
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")
-class IdentitySettingsRoot(RedirectView):
+class SettingsRoot(RedirectView):
url = "/settings/interface/"
-class IdentitySettingsPage(SystemSettingsPage):
+class SettingsPage(AdminSettingsPage):
"""
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!
"""
- extra_context = {"top_section": "settings"}
-
options_class = Config.IdentityOptions
- template_name = "settings/settings_identity.html"
+ template_name = "settings/settings.html"
def load_config(self):
return Config.load_identity(self.request.identity)
@@ -29,7 +27,7 @@ class IdentitySettingsPage(SystemSettingsPage):
Config.set_identity(self.request.identity, key, value)
-class InterfacePage(IdentitySettingsPage):
+class InterfacePage(SettingsPage):
section = "interface"