diff --git a/bookwyrm/forms/admin.py b/bookwyrm/forms/admin.py
index 2d69ef702..acff6cfaa 100644
--- a/bookwyrm/forms/admin.py
+++ b/bookwyrm/forms/admin.py
@@ -55,11 +55,45 @@ class CreateInviteForm(CustomForm):
class SiteForm(CustomForm):
class Meta:
model = models.SiteSettings
- exclude = ["admin_code", "install_mode", "imports_enabled"]
+ fields = [
+ "name",
+ "instance_tagline",
+ "instance_description",
+ "instance_short_description",
+ "default_theme",
+ "code_of_conduct",
+ "privacy_policy",
+ "impressum",
+ "show_impressum",
+ "logo",
+ "logo_small",
+ "favicon",
+ "support_link",
+ "support_title",
+ "admin_email",
+ "footer_item",
+ ]
widgets = {
"instance_short_description": forms.TextInput(
attrs={"aria-describedby": "desc_instance_short_description"}
),
+ }
+
+
+class RegistrationForm(CustomForm):
+ class Meta:
+ model = models.SiteSettings
+ fields = [
+ "allow_registration",
+ "allow_invite_requests",
+ "registration_closed_text",
+ "invite_request_text",
+ "invite_request_question",
+ "invite_question_text",
+ "require_confirm_email",
+ ]
+
+ widgets = {
"require_confirm_email": forms.CheckboxInput(
attrs={"aria-describedby": "desc_require_confirm_email"}
),
@@ -69,6 +103,23 @@ class SiteForm(CustomForm):
}
+class RegistrationLimitedForm(CustomForm):
+ class Meta:
+ model = models.SiteSettings
+ fields = [
+ "registration_closed_text",
+ "invite_request_text",
+ "invite_request_question",
+ "invite_question_text",
+ ]
+
+ widgets = {
+ "invite_request_text": forms.Textarea(
+ attrs={"aria-describedby": "desc_invite_request_text"}
+ ),
+ }
+
+
class ThemeForm(CustomForm):
class Meta:
model = models.Theme
diff --git a/bookwyrm/management/commands/initdb.py b/bookwyrm/management/commands/initdb.py
index 23020a0a6..fda40bd07 100644
--- a/bookwyrm/management/commands/initdb.py
+++ b/bookwyrm/management/commands/initdb.py
@@ -8,54 +8,64 @@ from bookwyrm import models
def init_groups():
"""permission levels"""
- groups = ["admin", "moderator", "editor"]
+ groups = ["admin", "owner", "moderator", "editor"]
for group in groups:
- Group.objects.create(name=group)
+ Group.objects.get_or_create(name=group)
def init_permissions():
"""permission types"""
permissions = [
+ {
+ "codename": "manage_registration",
+ "name": "allow or prevent user registration",
+ "groups": ["admin"],
+ },
+ {
+ "codename": "system_administration",
+ "name": "technical controls",
+ "groups": ["admin"],
+ },
{
"codename": "edit_instance_settings",
"name": "change the instance info",
- "groups": ["admin"],
+ "groups": ["admin", "owner"],
},
{
"codename": "set_user_group",
"name": "change what group a user is in",
- "groups": ["admin", "moderator"],
+ "groups": ["admin", "owner", "moderator"],
},
{
"codename": "control_federation",
"name": "control who to federate with",
- "groups": ["admin", "moderator"],
+ "groups": ["admin", "owner", "moderator"],
},
{
"codename": "create_invites",
"name": "issue invitations to join",
- "groups": ["admin", "moderator"],
+ "groups": ["admin", "owner", "moderator"],
},
{
"codename": "moderate_user",
"name": "deactivate or silence a user",
- "groups": ["admin", "moderator"],
+ "groups": ["admin", "owner", "moderator"],
},
{
"codename": "moderate_post",
"name": "delete other users' posts",
- "groups": ["admin", "moderator"],
+ "groups": ["admin", "owner", "moderator"],
},
{
"codename": "edit_book",
"name": "edit book info",
- "groups": ["admin", "moderator", "editor"],
+ "groups": ["admin", "owner", "moderator", "editor"],
},
]
content_type = ContentType.objects.get_for_model(models.User)
for permission in permissions:
- permission_obj = Permission.objects.create(
+ permission_obj, _ = Permission.objects.get_or_create(
codename=permission["codename"],
name=permission["name"],
content_type=content_type,
diff --git a/bookwyrm/migrations/0168_auto_20221205_2331.py b/bookwyrm/migrations/0168_auto_20221205_2331.py
new file mode 100644
index 000000000..901ca56f0
--- /dev/null
+++ b/bookwyrm/migrations/0168_auto_20221205_2331.py
@@ -0,0 +1,63 @@
+""" I added two new permission types and a new group to the management command that
+creates the database on install, this creates them for existing instances """
+# Generated by Django 3.2.16 on 2022-12-05 23:31
+
+from django.db import migrations
+
+
+def create_groups_and_perms(apps, schema_editor):
+ """create the new "owner" group and "system admin" permission"""
+ db_alias = schema_editor.connection.alias
+ group_model = apps.get_model("auth", "Group")
+ # Add the "owner" group, if needed
+ owner_group, group_created = group_model.objects.using(db_alias).get_or_create(
+ name="owner"
+ )
+
+ # Create perms, if needed
+ user_model = apps.get_model("bookwyrm", "User")
+ content_type_model = apps.get_model("contenttypes", "ContentType")
+ content_type = content_type_model.objects.get_for_model(user_model)
+ perms_model = apps.get_model("auth", "Permission")
+ reg_perm, perm_created = perms_model.objects.using(db_alias).get_or_create(
+ codename="manage_registration",
+ name="allow or prevent user registration",
+ content_type=content_type,
+ )
+ admin_perm, admin_perm_created = perms_model.objects.using(db_alias).get_or_create(
+ codename="system_administration",
+ name="technical controls",
+ content_type=content_type,
+ )
+
+ # Add perms to the group if anything was created
+ if group_created or perm_created or admin_perm_created:
+ perms = [
+ "edit_instance_settings",
+ "set_user_group",
+ "control_federation",
+ "create_invites",
+ "moderate_user",
+ "moderate_post",
+ "edit_book",
+ ]
+ owner_group.permissions.set(
+ perms_model.objects.using(db_alias).filter(codename__in=perms).all()
+ )
+
+ # also extend these perms to admins
+ # This is get or create so the tests don't fail -- it should already exist
+ admin_group, _ = group_model.objects.using(db_alias).get_or_create(name="admin")
+ admin_group.permissions.add(reg_perm)
+ admin_group.permissions.add(admin_perm)
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("bookwyrm", "0167_auto_20221125_1900"),
+ ]
+
+ operations = [
+ migrations.RunPython(create_groups_and_perms, migrations.RunPython.noop)
+ ]
diff --git a/bookwyrm/migrations/0170_merge_0168_auto_20221205_2331_0169_auto_20221206_0902.py b/bookwyrm/migrations/0170_merge_0168_auto_20221205_2331_0169_auto_20221206_0902.py
new file mode 100644
index 000000000..3e199b014
--- /dev/null
+++ b/bookwyrm/migrations/0170_merge_0168_auto_20221205_2331_0169_auto_20221206_0902.py
@@ -0,0 +1,13 @@
+# Generated by Django 3.2.16 on 2022-12-11 20:00
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("bookwyrm", "0168_auto_20221205_2331"),
+ ("bookwyrm", "0169_auto_20221206_0902"),
+ ]
+
+ operations = []
diff --git a/bookwyrm/templates/settings/layout.html b/bookwyrm/templates/settings/layout.html
index 729bc1efd..b87fdf974 100644
--- a/bookwyrm/templates/settings/layout.html
+++ b/bookwyrm/templates/settings/layout.html
@@ -103,10 +103,21 @@
{% trans "Site Settings" %}
{% block site-subtabs %}{% endblock %}
+
+ {% if perms.bookwyrm.manage_registration %}
+ {% url 'settings-registration' as url %}
+ {% trans "Registration" %}
+ {% else %}
+ {% url 'settings-registration-limited' as url %}
+ {% trans "Registration" %}
+ {% endif %}
+
+ {% if perms.bookwyrm.system_administration %}
{% url 'settings-themes' as url %}
{% trans "Themes" %}
+ {% endif %}
{% endif %}
diff --git a/bookwyrm/templates/settings/registration.html b/bookwyrm/templates/settings/registration.html
new file mode 100644
index 000000000..6126f6b92
--- /dev/null
+++ b/bookwyrm/templates/settings/registration.html
@@ -0,0 +1,83 @@
+{% extends 'settings/layout.html' %}
+{% load i18n %}
+
+{% block title %}{% trans "Registration" %}{% endblock %}
+
+{% block header %}{% trans "Registration" %}{% endblock %}
+
+{% block panel %}
+{% if success %}
+
+
+
+ {% trans "Settings saved" %}
+
+
+{% endif %}
+
+{% if form.errors %}
+
+
+
+ {% trans "Unable to save settings" %}
+
+
+{% endif %}
+
+
+{% endblock %}
+
diff --git a/bookwyrm/templates/settings/registration_limited.html b/bookwyrm/templates/settings/registration_limited.html
new file mode 100644
index 000000000..343afefbb
--- /dev/null
+++ b/bookwyrm/templates/settings/registration_limited.html
@@ -0,0 +1,81 @@
+{% extends 'settings/layout.html' %}
+{% load i18n %}
+
+{% block title %}{% trans "Registration" %}{% endblock %}
+
+{% block header %}{% trans "Registration" %}{% endblock %}
+
+{% block panel %}
+{% if success %}
+
+
+
+ {% trans "Settings saved" %}
+
+
+{% endif %}
+
+{% if form.errors %}
+
+
+
+ {% trans "Unable to save settings" %}
+
+
+{% endif %}
+
+{% if site.allow_registration %}
+
+ {% trans "Registration is enabled on this instance" %}
+
+{% else %}
+
+{% endif %}
+{% endblock %}
+
diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html
index 4cfa531e5..b6aef774a 100644
--- a/bookwyrm/templates/settings/site.html
+++ b/bookwyrm/templates/settings/site.html
@@ -10,7 +10,6 @@
{% trans "Instance Info" %}
{% trans "Display" %}
{% trans "Footer Content" %}
- {% trans "Registration" %}
{% endblock %}
@@ -141,55 +140,6 @@
-
-
-
- {% trans "Registration" %}
-
-
-
-
-
-
-
{% trans "(Recommended if registration is open)" %}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ site_form.registration_closed_text }}
-
-
-
- {{ site_form.invite_request_text }}
-
- {% include 'snippets/form_errors.html' with errors_list=site_form.invite_request_text.errors id="desc_invite_request_text" %}
-
-
-
-
diff --git a/bookwyrm/tests/management/test_initdb.py b/bookwyrm/tests/management/test_initdb.py
index a00c6d674..229009a55 100644
--- a/bookwyrm/tests/management/test_initdb.py
+++ b/bookwyrm/tests/management/test_initdb.py
@@ -12,7 +12,7 @@ class InitDB(TestCase):
def test_init_groups(self):
"""Create groups"""
initdb.init_groups()
- self.assertEqual(Group.objects.count(), 3)
+ self.assertEqual(Group.objects.count(), 4)
self.assertTrue(Group.objects.filter(name="admin").exists())
self.assertTrue(Group.objects.filter(name="moderator").exists())
self.assertTrue(Group.objects.filter(name="editor").exists())
@@ -87,7 +87,7 @@ class InitDB(TestCase):
command.handle()
# everything should have been called
- self.assertEqual(Group.objects.count(), 3)
+ self.assertEqual(Group.objects.count(), 4)
self.assertTrue(Permission.objects.exists())
self.assertEqual(models.Connector.objects.count(), 3)
self.assertEqual(models.SiteSettings.objects.count(), 1)
@@ -99,7 +99,7 @@ class InitDB(TestCase):
command.handle(limit="group")
# everything should have been called
- self.assertEqual(Group.objects.count(), 3)
+ self.assertEqual(Group.objects.count(), 4)
self.assertEqual(models.Connector.objects.count(), 0)
self.assertEqual(models.SiteSettings.objects.count(), 0)
self.assertEqual(models.LinkDomain.objects.count(), 0)
diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py
index ee260a6e6..ac3a80580 100644
--- a/bookwyrm/urls.py
+++ b/bookwyrm/urls.py
@@ -86,6 +86,16 @@ urlpatterns = [
r"^settings/dashboard/?$", views.Dashboard.as_view(), name="settings-dashboard"
),
re_path(r"^settings/site-settings/?$", views.Site.as_view(), name="settings-site"),
+ re_path(
+ r"^settings/site-registration/?$",
+ views.RegistrationLimited.as_view(),
+ name="settings-registration-limited",
+ ),
+ re_path(
+ r"^settings/site-registration-admin/?$",
+ views.Registration.as_view(),
+ name="settings-registration",
+ ),
re_path(r"^settings/themes/?$", views.Themes.as_view(), name="settings-themes"),
re_path(
r"^settings/themes/(?P\d+)/delete/?$",
diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py
index 4fcaed3a9..db88f1ae2 100644
--- a/bookwyrm/views/__init__.py
+++ b/bookwyrm/views/__init__.py
@@ -24,7 +24,7 @@ from .admin.reports import (
unsuspend_user,
moderator_delete_user,
)
-from .admin.site import Site
+from .admin.site import Site, Registration, RegistrationLimited
from .admin.themes import Themes, delete_theme
from .admin.user_admin import UserAdmin, UserAdminList
diff --git a/bookwyrm/views/admin/site.py b/bookwyrm/views/admin/site.py
index e2574f1dd..4b0a9c7d8 100644
--- a/bookwyrm/views/admin/site.py
+++ b/bookwyrm/views/admin/site.py
@@ -33,3 +33,57 @@ class Site(View):
data = {"site_form": forms.SiteForm(instance=site), "success": True}
return TemplateResponse(request, "settings/site.html", data)
+
+
+@method_decorator(login_required, name="dispatch")
+@method_decorator(
+ permission_required("bookwyrm.edit_instance_settings", raise_exception=True),
+ name="dispatch",
+)
+class RegistrationLimited(View):
+ """Things related to registering that non-admins owners can change"""
+
+ def get(self, request):
+ """edit form"""
+ site = models.SiteSettings.objects.get()
+ data = {"form": forms.RegistrationLimitedForm(instance=site)}
+ return TemplateResponse(request, "settings/registration_limited.html", data)
+
+ def post(self, request):
+ """edit the site settings"""
+ site = models.SiteSettings.objects.get()
+ form = forms.RegistrationLimitedForm(request.POST, request.FILES, instance=site)
+ if not form.is_valid():
+ data = {"form": form}
+ return TemplateResponse(request, "settings/registration_limited.html", data)
+ site = form.save(request)
+
+ data = {"form": forms.RegistrationLimitedForm(instance=site), "success": True}
+ return TemplateResponse(request, "settings/registration_limited.html", data)
+
+
+@method_decorator(login_required, name="dispatch")
+@method_decorator(
+ permission_required("bookwyrm.manage_registration", raise_exception=True),
+ name="dispatch",
+)
+class Registration(View):
+ """Control everything about registration"""
+
+ def get(self, request):
+ """edit form"""
+ site = models.SiteSettings.objects.get()
+ data = {"form": forms.RegistrationForm(instance=site)}
+ return TemplateResponse(request, "settings/registration.html", data)
+
+ def post(self, request):
+ """edit the site settings"""
+ site = models.SiteSettings.objects.get()
+ form = forms.RegistrationForm(request.POST, request.FILES, instance=site)
+ if not form.is_valid():
+ data = {"form": form}
+ return TemplateResponse(request, "settings/registration.html", data)
+ site = form.save(request)
+
+ data = {"form": forms.RegistrationForm(instance=site), "success": True}
+ return TemplateResponse(request, "settings/registration.html", data)
diff --git a/bookwyrm/views/admin/themes.py b/bookwyrm/views/admin/themes.py
index 4d795fbe0..5658d243a 100644
--- a/bookwyrm/views/admin/themes.py
+++ b/bookwyrm/views/admin/themes.py
@@ -12,7 +12,7 @@ from bookwyrm import forms, models
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
@method_decorator(
- permission_required("bookwyrm.edit_instance_settings", raise_exception=True),
+ permission_required("bookwyrm.system_administration", raise_exception=True),
name="dispatch",
)
class Themes(View):
@@ -46,7 +46,7 @@ def get_view_data():
@require_POST
-@permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
+@permission_required("bookwyrm.system_administration", raise_exception=True)
# pylint: disable=unused-argument
def delete_theme(request, theme_id):
"""Remove a theme"""