Theme selector

This commit is contained in:
Mouse Reeve 2022-02-27 10:00:50 -08:00
parent 6e96c1eee7
commit 3dfbb3272e
11 changed files with 184 additions and 34 deletions

View file

@ -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 AnnouncementForm(CustomForm):
class Meta: class Meta:
model = models.Announcement model = models.Announcement

View file

@ -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 from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -40,19 +39,7 @@ class Migration(migrations.Migration):
), ),
("created_date", models.DateTimeField(auto_now_add=True)), ("created_date", models.DateTimeField(auto_now_add=True)),
("name", models.CharField(max_length=50, unique=True)), ("name", models.CharField(max_length=50, unique=True)),
( ("path", 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)),
], ],
), ),
migrations.AddField( migrations.AddField(

View file

@ -3,7 +3,6 @@ import datetime
from urllib.parse import urljoin from urllib.parse import urljoin
import uuid import uuid
from django.core.validators import FileExtensionValidator
from django.db import models, IntegrityError from django.db import models, IntegrityError
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
@ -113,22 +112,12 @@ class Theme(models.Model):
created_date = models.DateTimeField(auto_now_add=True) created_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=50, unique=True) name = models.CharField(max_length=50, unique=True)
theme_file = models.FileField( path = models.CharField(max_length=50, unique=True)
upload_to="css/",
validators=[FileExtensionValidator(["scss", "sass"])],
null=True,
)
path = models.CharField(max_length=50, blank=True, null=True)
def __str__(self): def __str__(self):
# pylint: disable=invalid-str-returned # pylint: disable=invalid-str-returned
return self.name 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): class SiteInvite(models.Model):
"""gives someone access to create an account on the instance""" """gives someone access to create an account on the instance"""

View file

@ -176,14 +176,14 @@ class User(OrderedCollectionPageMixin, AbstractUser):
@property @property
def get_theme(self): def get_theme(self):
"""get the theme given the user/site""" """get the theme given the user/site"""
path = "bookwyrm-light.scss"
if self.theme: if self.theme:
path = self.theme.theme_path path = self.theme.path
else: else:
site_model = apps.get_model("bookwyrm", "SiteSettings", require_ready=True) site_model = apps.get_model("bookwyrm", "SiteSettings", require_ready=True)
site = site_model.objects.get() site = site_model.objects.get()
if site.default_theme: if site.default_theme:
path = site.default_theme.theme_path path = site.default_theme.path
path = path or "light.scss"
return f"css/{path}" return f"css/{path}"
@property @property

View file

@ -86,6 +86,10 @@
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Site Settings" %}</a> <a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Site Settings" %}</a>
{% block site-subtabs %}{% endblock %} {% block site-subtabs %}{% endblock %}
</li> </li>
<li>
{% url 'settings-themes' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Themes" %}</a>
</li>
</ul> </ul>
{% endif %} {% endif %}
</nav> </nav>

View file

@ -33,7 +33,12 @@
</div> </div>
{% endif %} {% endif %}
<form action="{% url 'settings-site' %}" method="POST" class="content" enctype="multipart/form-data"> <form
action="{% url 'settings-site' %}"
method="POST"
class="content"
enctype="multipart/form-data"
>
{% csrf_token %} {% csrf_token %}
<section class="block" id="instance_info"> <section class="block" id="instance_info">
<h2 class="title is-4">{% trans "Instance Info" %}</h2> <h2 class="title is-4">{% trans "Instance Info" %}</h2>
@ -95,8 +100,6 @@
<div class="select"> <div class="select">
{{ site_form.default_theme }} {{ site_form.default_theme }}
</div> </div>
<button type="button" class="button">{% trans "Upload theme" %}</button>
</div> </div>
</div> </div>
</section> </section>

View file

@ -0,0 +1,109 @@
{% extends 'settings/layout.html' %}
{% load i18n %}
{% block title %}{% trans "Themes" %}{% endblock %}
{% block header %}{% trans "Themes" %}{% endblock %}
{% block panel %}
{% if success %}
<div class="notification is-success is-light">
<span class="icon icon-check" aria-hidden="true"></span>
<span>
{% trans "Successfully added theme" %}
</span>
</div>
{% endif %}
<section class="block">
<div class="notification">
<p>
<strong>{% trans "Default theme:" %}</strong> {{ site.default_theme.name }}
</p>
<p>
<a href="{% url 'settings-site' %}#display">
{% trans "Set default theme" %}
</a>
</p>
</div>
</section>
<section class="block content">
<h2 class="title is-4">{% trans "Upload theme" %}</h2>
{% if theme_form.errors %}
<div class="notification is-danger is-light">
<span class="icon icon-x" aria-hidden="true"></span>
<span>
{% trans "Unable to save theme" %}
</span>
</div>
{% endif %}
<form
method="POST"
action="{% url 'settings-themes' %}"
class="box"
enctype="multipart/form-data"
>
{% csrf_token %}
<div class="columns">
<div class="column is-half">
<label class="label" for="id_name">
{% trans "Theme name" %}
</label>
<div class="control">
{{ theme_form.name }}
{% include 'snippets/form_errors.html' with errors_list=theme_form.name.errors id="desc_name" %}
</div>
</div>
<div class="column">
<label class="label" for="id_theme_file">
{% trans "Theme file" %}
</label>
<div class="control">
{{ theme_form.theme_file }}
{% include 'snippets/form_errors.html' with errors_list=theme_form.theme_file.errors id="desc_theme_file" %}
</div>
</div>
</div>
<button type="submit" class="button">{% trans "Upload theme" %}</button>
</form>
</section>
<section class="block content">
<h2 class="title is-4">{% trans "Available Themes" %}</h2>
<div class="table-container">
<table class="table is-striped">
<tr>
<th>
{% trans "Theme name" %}
</th>
<th>
{% trans "File" %}
</th>
<th>
{% trans "Actions" %}
</th>
</tr>
{% for theme in themes %}
<tr>
<td>{{ theme.name }}</td>
<td><code>{{ theme.theme_path }}</code></td>
<td>
{% if theme.theme_file %}
<form>
<button type="submit" class="button is-danger is-light">{% trans "Delete" %}</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
</section>
{% endblock %}

View file

@ -86,6 +86,7 @@ urlpatterns = [
r"^settings/dashboard/?$", views.Dashboard.as_view(), name="settings-dashboard" 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-settings/?$", views.Site.as_view(), name="settings-site"),
re_path(r"^settings/themes/?$", views.Themes.as_view(), name="settings-themes"),
re_path( re_path(
r"^settings/announcements/?$", r"^settings/announcements/?$",
views.Announcements.as_view(), views.Announcements.as_view(),

View file

@ -21,6 +21,7 @@ from .admin.reports import (
moderator_delete_user, moderator_delete_user,
) )
from .admin.site import Site from .admin.site import Site
from .admin.themes import Themes
from .admin.user_admin import UserAdmin, UserAdminList from .admin.user_admin import UserAdmin, UserAdminList
# user preferences # user preferences

View file

@ -29,7 +29,7 @@ class Site(View):
if not form.is_valid(): if not form.is_valid():
data = {"site_form": form} data = {"site_form": form}
return TemplateResponse(request, "settings/site.html", data) return TemplateResponse(request, "settings/site.html", data)
form.save() site = form.save()
data = {"site_form": forms.SiteForm(instance=site), "success": True} data = {"site_form": forms.SiteForm(instance=site), "success": True}
return TemplateResponse(request, "settings/site.html", data) return TemplateResponse(request, "settings/site.html", data)

View file

@ -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)