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 Meta:
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
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(

View file

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

View file

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

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>
{% block site-subtabs %}{% endblock %}
</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>
{% endif %}
</nav>

View file

@ -33,7 +33,12 @@
</div>
{% 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 %}
<section class="block" id="instance_info">
<h2 class="title is-4">{% trans "Instance Info" %}</h2>
@ -95,8 +100,6 @@
<div class="select">
{{ site_form.default_theme }}
</div>
<button type="button" class="button">{% trans "Upload theme" %}</button>
</div>
</div>
</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"
),
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(),

View file

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

View file

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

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)