From 3dfbb3272e35877400da907d4d958ea8554b995a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 27 Feb 2022 10:00:50 -0800 Subject: [PATCH] Theme selector --- bookwyrm/forms.py | 18 +++ ...226_2047.py => 0142_auto_20220227_1752.py} | 17 +-- bookwyrm/models/site.py | 13 +-- bookwyrm/models/user.py | 6 +- bookwyrm/templates/settings/layout.html | 4 + bookwyrm/templates/settings/site.html | 9 +- bookwyrm/templates/settings/themes.html | 109 ++++++++++++++++++ bookwyrm/urls.py | 1 + bookwyrm/views/__init__.py | 1 + bookwyrm/views/admin/site.py | 2 +- bookwyrm/views/admin/themes.py | 38 ++++++ 11 files changed, 184 insertions(+), 34 deletions(-) rename bookwyrm/migrations/{0142_auto_20220226_2047.py => 0142_auto_20220227_1752.py} (76%) create mode 100644 bookwyrm/templates/settings/themes.html create mode 100644 bookwyrm/views/admin/themes.py diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 7ae4e446f..926aaecf3 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -454,6 +454,24 @@ class SiteForm(CustomForm): } +class SiteThemeForm(CustomForm): + class Meta: + model = models.SiteSettings + fields = ["default_theme"] + + +class ThemeForm(CustomForm): + class Meta: + model = models.Theme + fields = ["name", "path"] + widgets = { + "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), + "path": ClearableFileInputWithWarning( + attrs={"aria-describedby": "desc_path"} + ), + } + + class AnnouncementForm(CustomForm): class Meta: model = models.Announcement diff --git a/bookwyrm/migrations/0142_auto_20220226_2047.py b/bookwyrm/migrations/0142_auto_20220227_1752.py similarity index 76% rename from bookwyrm/migrations/0142_auto_20220226_2047.py rename to bookwyrm/migrations/0142_auto_20220227_1752.py index 928d556c7..75f0e1e75 100644 --- a/bookwyrm/migrations/0142_auto_20220226_2047.py +++ b/bookwyrm/migrations/0142_auto_20220227_1752.py @@ -1,6 +1,5 @@ -# Generated by Django 3.2.12 on 2022-02-26 20:47 +# Generated by Django 3.2.12 on 2022-02-27 17:52 -import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -40,19 +39,7 @@ class Migration(migrations.Migration): ), ("created_date", models.DateTimeField(auto_now_add=True)), ("name", models.CharField(max_length=50, unique=True)), - ( - "theme_file", - models.FileField( - null=True, - upload_to="css/", - validators=[ - django.core.validators.FileExtensionValidator( - ["scss", "sass"] - ) - ], - ), - ), - ("path", models.CharField(blank=True, max_length=50, null=True)), + ("path", models.CharField(max_length=50, unique=True)), ], ), migrations.AddField( diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index 13cfdc756..c6c53f765 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -3,7 +3,6 @@ import datetime from urllib.parse import urljoin import uuid -from django.core.validators import FileExtensionValidator from django.db import models, IntegrityError from django.dispatch import receiver from django.utils import timezone @@ -113,22 +112,12 @@ class Theme(models.Model): created_date = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=50, unique=True) - theme_file = models.FileField( - upload_to="css/", - validators=[FileExtensionValidator(["scss", "sass"])], - null=True, - ) - path = models.CharField(max_length=50, blank=True, null=True) + path = models.CharField(max_length=50, unique=True) def __str__(self): # pylint: disable=invalid-str-returned return self.name - @property - def theme_path(self): - """get the theme given the user/site""" - return self.theme_file.path if self.theme_file else self.path - class SiteInvite(models.Model): """gives someone access to create an account on the instance""" diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 3d12e6044..85702ae56 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -176,14 +176,14 @@ class User(OrderedCollectionPageMixin, AbstractUser): @property def get_theme(self): """get the theme given the user/site""" + path = "bookwyrm-light.scss" if self.theme: - path = self.theme.theme_path + path = self.theme.path else: site_model = apps.get_model("bookwyrm", "SiteSettings", require_ready=True) site = site_model.objects.get() if site.default_theme: - path = site.default_theme.theme_path - path = path or "light.scss" + path = site.default_theme.path return f"css/{path}" @property diff --git a/bookwyrm/templates/settings/layout.html b/bookwyrm/templates/settings/layout.html index c5a3c5af2..d76c954dc 100644 --- a/bookwyrm/templates/settings/layout.html +++ b/bookwyrm/templates/settings/layout.html @@ -86,6 +86,10 @@ {% trans "Site Settings" %} {% block site-subtabs %}{% endblock %} +
  • + {% url 'settings-themes' as url %} + {% trans "Themes" %} +
  • {% endif %} diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html index 0afbd64f6..4d9dbe400 100644 --- a/bookwyrm/templates/settings/site.html +++ b/bookwyrm/templates/settings/site.html @@ -33,7 +33,12 @@ {% endif %} -
    + {% csrf_token %}

    {% trans "Instance Info" %}

    @@ -95,8 +100,6 @@
    {{ site_form.default_theme }}
    - -
    diff --git a/bookwyrm/templates/settings/themes.html b/bookwyrm/templates/settings/themes.html new file mode 100644 index 000000000..6e9eb61c5 --- /dev/null +++ b/bookwyrm/templates/settings/themes.html @@ -0,0 +1,109 @@ +{% extends 'settings/layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Themes" %}{% endblock %} + +{% block header %}{% trans "Themes" %}{% endblock %} + +{% block panel %} +{% if success %} +
    + + + {% trans "Successfully added theme" %} + +
    +{% endif %} + +
    +
    +

    + {% trans "Default theme:" %} {{ site.default_theme.name }} +

    + +

    + + {% trans "Set default theme" %} + +

    +
    +
    + +
    +

    {% trans "Upload theme" %}

    + + {% if theme_form.errors %} +
    + + + {% trans "Unable to save theme" %} + +
    + {% endif %} + + + {% csrf_token %} +
    +
    + +
    + {{ theme_form.name }} + {% include 'snippets/form_errors.html' with errors_list=theme_form.name.errors id="desc_name" %} +
    +
    + +
    + +
    + {{ theme_form.theme_file }} + {% include 'snippets/form_errors.html' with errors_list=theme_form.theme_file.errors id="desc_theme_file" %} +
    +
    +
    + + + +
    + +
    +

    {% trans "Available Themes" %}

    +
    + + + + + + + {% for theme in themes %} + + + + + + {% endfor %} +
    + {% trans "Theme name" %} + + {% trans "File" %} + + {% trans "Actions" %} +
    {{ theme.name }}{{ theme.theme_path }} + {% if theme.theme_file %} +
    + +
    + {% endif %} +
    +
    +
    + +{% endblock %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index d2caa76ea..bef6786d7 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -86,6 +86,7 @@ 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/themes/?$", views.Themes.as_view(), name="settings-themes"), re_path( r"^settings/announcements/?$", views.Announcements.as_view(), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 76e9ff024..675221cb8 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -21,6 +21,7 @@ from .admin.reports import ( moderator_delete_user, ) from .admin.site import Site +from .admin.themes import Themes from .admin.user_admin import UserAdmin, UserAdminList # user preferences diff --git a/bookwyrm/views/admin/site.py b/bookwyrm/views/admin/site.py index 7e75a8204..f345d9970 100644 --- a/bookwyrm/views/admin/site.py +++ b/bookwyrm/views/admin/site.py @@ -29,7 +29,7 @@ class Site(View): if not form.is_valid(): data = {"site_form": form} return TemplateResponse(request, "settings/site.html", data) - form.save() + site = form.save() data = {"site_form": forms.SiteForm(instance=site), "success": True} return TemplateResponse(request, "settings/site.html", data) diff --git a/bookwyrm/views/admin/themes.py b/bookwyrm/views/admin/themes.py new file mode 100644 index 000000000..cf11bc6d0 --- /dev/null +++ b/bookwyrm/views/admin/themes.py @@ -0,0 +1,38 @@ +""" manage themes """ +from django.contrib.auth.decorators import login_required, permission_required +from django.template.response import TemplateResponse +from django.utils.decorators import method_decorator +from django.views import View + +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), + name="dispatch", +) +class Themes(View): + """manage things like the instance name""" + + def get(self, request): + """view existing themes and set defaults""" + data = { + "themes": models.Theme.objects.all(), + "theme_form": forms.ThemeForm(), + } + return TemplateResponse(request, "settings/themes.html", data) + + def post(self, request): + """edit the site settings""" + form = forms.ThemeForm(request.POST, request.FILES) + data = { + "themes": models.Theme.objects.all(), + "theme_form": form, + } + if form.is_valid(): + form.save() + data["success"] = True + data["theme_form"] = forms.ThemeForm() + return TemplateResponse(request, "settings/themes.html", data)