Merge branch 'main' into code-scanning
This commit is contained in:
@ -8,8 +8,20 @@ def site_settings(request): # pylint: disable=unused-argument
|
||||
if not request.is_secure():
|
||||
request_protocol = "http://"
|
||||
|
||||
site = models.SiteSettings.objects.get()
|
||||
theme = "css/themes/bookwyrm-light.scss"
|
||||
if (
|
||||
hasattr(request, "user")
|
||||
and request.user.is_authenticated
|
||||
and request.user.theme
|
||||
):
|
||||
theme = request.user.theme.path
|
||||
elif site.default_theme:
|
||||
theme = site.default_theme.path
|
||||
|
||||
return {
|
||||
"site": models.SiteSettings.objects.get(),
|
||||
"site": site,
|
||||
"site_theme": theme,
|
||||
"active_announcements": models.Announcement.active_announcements(),
|
||||
"thumbnail_generation_enabled": settings.ENABLE_THUMBNAIL_GENERATION,
|
||||
"media_full_url": settings.MEDIA_FULL_URL,
|
||||
|
@ -156,6 +156,7 @@ class EditUserForm(CustomForm):
|
||||
"hide_follows",
|
||||
"preferred_timezone",
|
||||
"preferred_language",
|
||||
"theme",
|
||||
]
|
||||
help_texts = {f: None for f in fields}
|
||||
widgets = {
|
||||
@ -455,6 +456,22 @@ 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": forms.Select(attrs={"aria-describedby": "desc_path"}),
|
||||
}
|
||||
|
||||
|
||||
class AnnouncementForm(CustomForm):
|
||||
class Meta:
|
||||
model = models.Announcement
|
||||
|
68
bookwyrm/migrations/0142_auto_20220227_1752.py
Normal file
68
bookwyrm/migrations/0142_auto_20220227_1752.py
Normal file
@ -0,0 +1,68 @@
|
||||
# Generated by Django 3.2.12 on 2022-02-27 17:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def add_default_themes(apps, schema_editor):
|
||||
"""add light and dark themes"""
|
||||
db_alias = schema_editor.connection.alias
|
||||
theme_model = apps.get_model("bookwyrm", "Theme")
|
||||
theme_model.objects.using(db_alias).create(
|
||||
name="BookWyrm Light",
|
||||
path="css/themes/bookwyrm-light.scss",
|
||||
)
|
||||
theme_model.objects.using(db_alias).create(
|
||||
name="BookWyrm Dark",
|
||||
path="css/themes/bookwyrm-dark.scss",
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0141_alter_report_status"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Theme",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_date", models.DateTimeField(auto_now_add=True)),
|
||||
("name", models.CharField(max_length=50, unique=True)),
|
||||
("path", models.CharField(max_length=50, unique=True)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="sitesettings",
|
||||
name="default_theme",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="bookwyrm.theme",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="user",
|
||||
name="theme",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="bookwyrm.theme",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
add_default_themes, reverse_code=migrations.RunPython.noop
|
||||
),
|
||||
]
|
@ -0,0 +1,13 @@
|
||||
# Generated by Django 3.2.12 on 2022-02-28 21:28
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0142_auto_20220227_1752"),
|
||||
("bookwyrm", "0142_user_hide_follows"),
|
||||
]
|
||||
|
||||
operations = []
|
39
bookwyrm/migrations/0144_alter_announcement_display_type.py
Normal file
39
bookwyrm/migrations/0144_alter_announcement_display_type.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Generated by Django 3.2.12 on 2022-03-01 18:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def remove_white(apps, schema_editor):
|
||||
"""don't hardcode white announcements"""
|
||||
db_alias = schema_editor.connection.alias
|
||||
announcement_model = apps.get_model("bookwyrm", "Announcement")
|
||||
announcement_model.objects.using(db_alias).filter(display_type="white-ter").update(
|
||||
display_type=None
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0143_merge_0142_auto_20220227_1752_0142_user_hide_follows"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="announcement",
|
||||
name="display_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("primary-light", "Primary"),
|
||||
("success-light", "Success"),
|
||||
("link-light", "Link"),
|
||||
("warning-light", "Warning"),
|
||||
("danger-light", "Danger"),
|
||||
],
|
||||
max_length=20,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.RunPython(remove_white, reverse_code=migrations.RunPython.noop),
|
||||
]
|
@ -26,7 +26,7 @@ from .group import Group, GroupMember, GroupMemberInvitation
|
||||
|
||||
from .import_job import ImportJob, ImportItem
|
||||
|
||||
from .site import SiteSettings, SiteInvite
|
||||
from .site import SiteSettings, Theme, SiteInvite
|
||||
from .site import PasswordReset, InviteRequest
|
||||
from .announcement import Announcement
|
||||
from .antispam import EmailBlocklist, IPBlocklist, AutoMod, automod_task
|
||||
|
@ -8,7 +8,6 @@ from .base_model import BookWyrmModel
|
||||
|
||||
|
||||
DisplayTypes = [
|
||||
("white-ter", _("None")),
|
||||
("primary-light", _("Primary")),
|
||||
("success-light", _("Success")),
|
||||
("link-light", _("Link")),
|
||||
@ -28,11 +27,7 @@ class Announcement(BookWyrmModel):
|
||||
end_date = models.DateTimeField(blank=True, null=True)
|
||||
active = models.BooleanField(default=True)
|
||||
display_type = models.CharField(
|
||||
max_length=20,
|
||||
blank=False,
|
||||
null=False,
|
||||
choices=DisplayTypes,
|
||||
default="white-ter",
|
||||
max_length=20, choices=DisplayTypes, null=True, blank=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -24,6 +24,9 @@ class SiteSettings(models.Model):
|
||||
)
|
||||
instance_description = models.TextField(default="This instance has no description.")
|
||||
instance_short_description = models.CharField(max_length=255, blank=True, null=True)
|
||||
default_theme = models.ForeignKey(
|
||||
"Theme", null=True, blank=True, on_delete=models.SET_NULL
|
||||
)
|
||||
|
||||
# admin setup options
|
||||
install_mode = models.BooleanField(default=False)
|
||||
@ -104,6 +107,18 @@ class SiteSettings(models.Model):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class Theme(models.Model):
|
||||
"""Theme files"""
|
||||
|
||||
created_date = models.DateTimeField(auto_now_add=True)
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
path = models.CharField(max_length=50, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
# pylint: disable=invalid-str-returned
|
||||
return self.name
|
||||
|
||||
|
||||
class SiteInvite(models.Model):
|
||||
"""gives someone access to create an account on the instance"""
|
||||
|
||||
|
@ -136,6 +136,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||
updated_date = models.DateTimeField(auto_now=True)
|
||||
last_active_date = models.DateTimeField(default=timezone.now)
|
||||
manually_approves_followers = fields.BooleanField(default=False)
|
||||
theme = models.ForeignKey("Theme", null=True, blank=True, on_delete=models.SET_NULL)
|
||||
hide_follows = fields.BooleanField(default=False)
|
||||
|
||||
# options to turn features on and off
|
||||
|
@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
env = Env()
|
||||
env.read_env()
|
||||
DOMAIN = env("DOMAIN")
|
||||
VERSION = "0.3.1"
|
||||
VERSION = "0.3.2"
|
||||
|
||||
RELEASE_API = env(
|
||||
"RELEASE_API",
|
||||
@ -21,7 +21,7 @@ RELEASE_API = env(
|
||||
PAGE_LENGTH = env("PAGE_LENGTH", 15)
|
||||
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
|
||||
|
||||
JS_CACHE = "c7144efb"
|
||||
JS_CACHE = "c7154efb"
|
||||
|
||||
# email
|
||||
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
||||
|
@ -1,7 +1,4 @@
|
||||
@charset "utf-8";
|
||||
|
||||
@import "instance-settings";
|
||||
@import "themes/light.scss";
|
||||
@import "vendor/bulma/bulma.sass";
|
||||
@import "vendor/icons.css";
|
||||
@import "bookwyrm/all.scss";
|
||||
|
@ -116,7 +116,7 @@ button .button-invisible-overlay {
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background: rgba($scheme-invert, 0.66);
|
||||
background: $invisible-overlay-background-color;
|
||||
color: white;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
|
@ -53,7 +53,7 @@ details.dropdown .dropdown-menu a:focus-visible {
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
details.dropdown[open] summary.dropdown-trigger::before {
|
||||
background-color: rgba($scheme-invert, 0.5);
|
||||
background-color: $modal-background-background-color;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
.toggle-button[aria-pressed="true"],
|
||||
.toggle-button[aria-pressed="true"]:hover {
|
||||
background-color: hsl(171deg, 100%, 41%);
|
||||
background-color: $primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
84
bookwyrm/static/css/themes/bookwyrm-dark.scss
Normal file
84
bookwyrm/static/css/themes/bookwyrm-dark.scss
Normal file
@ -0,0 +1,84 @@
|
||||
@import "../vendor/bulma/sass/utilities/initial-variables.sass";
|
||||
|
||||
/* Colors
|
||||
******************************************************************************/
|
||||
|
||||
/* states */
|
||||
$primary: #005e50;
|
||||
$primary-light: #1d2b28;
|
||||
$info: #1f4666;
|
||||
$success: #246447;
|
||||
$warning: #8b6c15;
|
||||
$danger: #872538;
|
||||
$danger-light: #481922;
|
||||
$light: #393939;
|
||||
$red: #ffa1b4;
|
||||
|
||||
/* book cover standins */
|
||||
$no-cover-color: #002549;
|
||||
|
||||
/* background colors */
|
||||
$scheme-main: rgb(24, 27, 28);
|
||||
$scheme-invert: #fff;
|
||||
$scheme-main-bis: rgb(28, 30, 32);
|
||||
$scheme-main-ter: rgb(32, 34, 36);
|
||||
$background-body: rgb(24, 27, 28);
|
||||
$background-secondary: rgb(28, 30, 32);
|
||||
$background-tertiary: rgb(32, 34, 36);
|
||||
$modal-background-background-color: rgba($black, 0.8);
|
||||
|
||||
/* highlight colors */
|
||||
$primary-highlight: $primary;
|
||||
$info-highlight: $info;
|
||||
$success-highlight: $success;
|
||||
|
||||
/* borders */
|
||||
$border: #2b3031;
|
||||
$border-light: #444;
|
||||
$border-hover: #51595d;
|
||||
|
||||
/* text */
|
||||
$text: $grey-lightest;
|
||||
$text-light: $grey-lighter;
|
||||
$text-strong: $white-ter;
|
||||
|
||||
/* links */
|
||||
$link: #2e7eb9;
|
||||
$link-background: $background-tertiary;
|
||||
$link-hover: $white-bis;
|
||||
$link-hover-border: #51595d;
|
||||
$link-focus: $white-bis;
|
||||
$link-active: $white-bis;
|
||||
|
||||
/* bulma overrides */
|
||||
$background: $background-secondary;
|
||||
$menu-item-active-background-color: $link-background;
|
||||
$navbar-dropdown-item-hover-color: $white;
|
||||
|
||||
/* These element's colors are hardcoded, probably a bug in bulma? */
|
||||
@media screen and (min-width: 769px) {
|
||||
.navbar-dropdown {
|
||||
box-shadow: 0 8px 8px rgba($black, 0.2) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.navbar-menu {
|
||||
box-shadow: 0 8px 8px rgba($black, 0.2) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* misc */
|
||||
$shadow: 0 0.5em 1em -0.125em rgba($black, 0.2), 0 0px 0 1px rgba($black, 0.02);
|
||||
$card-header-shadow: 0 0.125em 0.25em rgba($black, 0.1);
|
||||
$invisible-overlay-background-color: rgba($black, 0.66);
|
||||
$progress-value-background-color: $border-light;
|
||||
|
||||
/* Fonts
|
||||
******************************************************************************/
|
||||
$family-primary: $family-sans-serif;
|
||||
$family-secondary: $family-sans-serif;
|
||||
|
||||
|
||||
@import "../bookwyrm.scss";
|
||||
@import "../vendor/icons.css";
|
@ -47,7 +47,13 @@ $link-active: $grey-darker;
|
||||
$background: $background-secondary;
|
||||
$menu-item-active-background-color: $link-background;
|
||||
|
||||
/* misc */
|
||||
$invisible-overlay-background-color: rgba($scheme-invert, 0.66);
|
||||
|
||||
/* Fonts
|
||||
******************************************************************************/
|
||||
$family-primary: $family-sans-serif;
|
||||
$family-secondary: $family-sans-serif;
|
||||
|
||||
@import "../bookwyrm.scss";
|
||||
@import "../vendor/icons.css";
|
@ -1,55 +0,0 @@
|
||||
@import "../vendor/bulma/sass/utilities/derived-variables.sass";
|
||||
|
||||
/* Colors
|
||||
******************************************************************************/
|
||||
|
||||
/* states */
|
||||
$primary: #016a5b;
|
||||
$info: #1f4666;
|
||||
$success: #246447;
|
||||
$warning: #8b6c15;
|
||||
$danger: #872538;
|
||||
|
||||
/* book cover standins */
|
||||
$no-cover-color: #002549;
|
||||
|
||||
/* background colors */
|
||||
$scheme-main: $grey-darker;
|
||||
$scheme-main-bis: $black-ter;
|
||||
$background-body: $grey-darker;
|
||||
$background-secondary: $grey-dark;
|
||||
$background-tertiary: #555;
|
||||
|
||||
/* highlight colors */
|
||||
$primary-highlight: $primary;
|
||||
$info-highlight: $info;
|
||||
$success-highlight: $success;
|
||||
|
||||
/* borders */
|
||||
$border: $grey;
|
||||
$border-hover: $grey-light;
|
||||
$border-light: $grey;
|
||||
$border-light-hover: $grey-light;
|
||||
|
||||
/* text */
|
||||
$text: $grey-lightest;
|
||||
$text-light: $grey-lighter;
|
||||
$text-strong: $white-ter;
|
||||
|
||||
/* links */
|
||||
$link: $white;
|
||||
$link-background: $background-tertiary;
|
||||
$link-hover: $white-bis;
|
||||
$link-focus: $white-bis;
|
||||
$link-active: $white-bis;
|
||||
|
||||
/* misc */
|
||||
|
||||
/* bulma overrides */
|
||||
$background: $background-secondary;
|
||||
$menu-item-active-background-color: $link-background;
|
||||
|
||||
/* Fonts
|
||||
******************************************************************************/
|
||||
$family-primary: $family-sans-serif;
|
||||
$family-secondary: $family-sans-serif;
|
@ -14,23 +14,25 @@
|
||||
{% cache 604800 about_page %}
|
||||
|
||||
{% get_book_superlatives as superlatives %}
|
||||
<section class="content pb-4">
|
||||
<h2>
|
||||
{% blocktrans with site_name=site.name %}Welcome to {{ site_name }}!{% endblocktrans %}
|
||||
</h2>
|
||||
<section class=" pb-4">
|
||||
<div class="content">
|
||||
<h2>
|
||||
{% blocktrans with site_name=site.name %}Welcome to {{ site_name }}!{% endblocktrans %}
|
||||
</h2>
|
||||
|
||||
<p class="subtitle notification has-background-primary-highlight">
|
||||
{% blocktrans trimmed with site_name=site.name %}
|
||||
{{ site_name }} is part of <em>BookWyrm</em>, a network of independent, self-directed communities for readers.
|
||||
While you can interact seamlessly with users anywhere in the <a href="https://joinbookwyrm.com/instances/" target="_blank">BookWyrm network</a>, this community is unique.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="subtitle notification has-background-primary-highlight">
|
||||
{% blocktrans trimmed with site_name=site.name %}
|
||||
{{ site_name }} is part of <em>BookWyrm</em>, a network of independent, self-directed communities for readers.
|
||||
While you can interact seamlessly with users anywhere in the <a href="https://joinbookwyrm.com/instances/" target="_blank">BookWyrm network</a>, this community is unique.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
{% if superlatives.top_rated %}
|
||||
{% with book=superlatives.top_rated.default_edition rating=superlatives.top_rated.rating %}
|
||||
<div class="column is-one-third is-flex">
|
||||
<div class="media notification">
|
||||
<div class="media notification is-clipped">
|
||||
<div class="media-left">
|
||||
<a href="{{ book.local_path }}">
|
||||
{% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' size='medium' aria='show' %}
|
||||
@ -49,7 +51,7 @@
|
||||
{% if superlatives.wanted %}
|
||||
{% with book=superlatives.wanted.default_edition %}
|
||||
<div class="column is-one-third is-flex">
|
||||
<div class="media notification">
|
||||
<div class="media notification is-clipped">
|
||||
<div class="media-left">
|
||||
<a href="{{ book.local_path }}">
|
||||
{% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' size='medium' aria='show' %}
|
||||
@ -68,7 +70,7 @@
|
||||
{% if superlatives.controversial %}
|
||||
{% with book=superlatives.controversial.default_edition %}
|
||||
<div class="column is-one-third is-flex">
|
||||
<div class="media notification">
|
||||
<div class="media notification is-clipped">
|
||||
<div class="media-left">
|
||||
<a href="{{ book.local_path }}">
|
||||
{% include 'snippets/book_cover.html' with book=book cover_class='is-h-m' size='medium' aria='show' %}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<head>
|
||||
<title>{% block title %}BookWyrm{% endblock %} - {{ site.name }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="{% sass_src 'css/bookwyrm.scss' %}" rel="stylesheet" type="text/css" />
|
||||
<link href="{% sass_src 'css/themes/bookwyrm-light.scss' %}" rel="stylesheet" type="text/css" />
|
||||
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="{% url 'opensearch' %}" title="{% blocktrans with site_name=site.name %}{{ site_name }} search{% endblocktrans %}" />
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
{% block profile-tabs %}
|
||||
<ul class="menu-list">
|
||||
<li><a href="#profile">{% trans "Profile" %}</a></li>
|
||||
<li><a href="#display-preferences">{% trans "Display preferences" %}</a></li>
|
||||
<li><a href="#display-preferences">{% trans "Display" %}</a></li>
|
||||
<li><a href="#privacy">{% trans "Privacy" %}</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
@ -61,7 +61,7 @@
|
||||
<hr aria-hidden="true">
|
||||
|
||||
<section class="block" id="display-preferences">
|
||||
<h2 class="title is-4">{% trans "Display preferences" %}</h2>
|
||||
<h2 class="title is-4">{% trans "Display" %}</h2>
|
||||
<div class="box">
|
||||
<div class="field">
|
||||
<label class="checkbox label" for="id_show_goal">
|
||||
@ -97,6 +97,12 @@
|
||||
{{ form.preferred_language }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_them">{% trans "Theme:" %}</label>
|
||||
<div class="select">
|
||||
{{ form.theme }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -8,7 +8,7 @@
|
||||
{% block site-subtabs %}
|
||||
<ul class="menu-list">
|
||||
<li><a href="#instance-info">{% trans "Instance Info" %}</a></li>
|
||||
<li><a href="#images">{% trans "Images" %}</a></li>
|
||||
<li><a href="#display">{% trans "Display" %}</a></li>
|
||||
<li><a href="#footer">{% trans "Footer Content" %}</a></li>
|
||||
<li><a href="#registration">{% trans "Registration" %}</a></li>
|
||||
</ul>
|
||||
@ -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>
|
||||
@ -68,20 +73,33 @@
|
||||
|
||||
<hr aria-hidden="true">
|
||||
|
||||
<section class="block" id="images">
|
||||
<h2 class="title is-4">{% trans "Images" %}</h2>
|
||||
<div class="box is-flex">
|
||||
<div>
|
||||
<label class="label" for="id_logo">{% trans "Logo:" %}</label>
|
||||
{{ site_form.logo }}
|
||||
<section class="block" id="display">
|
||||
<h2 class="title is-4">{% trans "Display" %}</h2>
|
||||
<div class="box">
|
||||
<h3 class="title is-5">{% trans "Images" %}</h3>
|
||||
<div class="block is-flex">
|
||||
<div>
|
||||
<label class="label" for="id_logo">{% trans "Logo:" %}</label>
|
||||
{{ site_form.logo }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="label" for="id_logo_small">{% trans "Logo small:" %}</label>
|
||||
{{ site_form.logo_small }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="label" for="id_favicon">{% trans "Favicon:" %}</label>
|
||||
{{ site_form.favicon }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label" for="id_logo_small">{% trans "Logo small:" %}</label>
|
||||
{{ site_form.logo_small }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="label" for="id_favicon">{% trans "Favicon:" %}</label>
|
||||
{{ site_form.favicon }}
|
||||
|
||||
<h3 class="title is-5">{% trans "Themes" %}</h3>
|
||||
<div class="block">
|
||||
<label class="label" for="id_default_theme">
|
||||
{% trans "Default theme:" %}
|
||||
</label>
|
||||
<div class="select">
|
||||
{{ site_form.default_theme }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
140
bookwyrm/templates/settings/themes.html
Normal file
140
bookwyrm/templates/settings/themes.html
Normal file
@ -0,0 +1,140 @@
|
||||
{% extends 'settings/layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Themes" %}{% endblock %}
|
||||
|
||||
{% block header %}{% trans "Themes" %}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<a class="subtitle help is-link" href="{% url 'settings-site' %}/#display">
|
||||
{% trans "Set instance default theme" %}
|
||||
</a>
|
||||
{% 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 content">
|
||||
<h2 class="title is-5">{% trans "How to add a theme" %}</h2>
|
||||
<ol>
|
||||
<li>
|
||||
{% trans "Copy the theme file into the <code>bookwyrm/static/css/themes</code> directory on your server from the command line." %}
|
||||
</li>
|
||||
<li>
|
||||
{% trans "Run <code>./bw-dev compilescss</code>." %}
|
||||
</li>
|
||||
<li>
|
||||
{% trans "Add the file name using the form below to make it available in the application interface." %}
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="block content">
|
||||
<h2 class="title is-4">{% trans "Add 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"
|
||||
>
|
||||
{% if not choices %}
|
||||
<div class="notification is-warning">
|
||||
{% trans "No available theme files detected" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<fieldset {% if not choices %}disabled{% endif %}>
|
||||
{% 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_path">
|
||||
{% trans "Theme filename" %}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select
|
||||
name="path"
|
||||
aria-describedby="desc_path"
|
||||
class=""
|
||||
id="id_path"
|
||||
>
|
||||
{% for choice in choices %}
|
||||
<option value="{{ choice }}">
|
||||
{{ choice }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% include 'snippets/form_errors.html' with errors_list=theme_form.path.errors id="desc_path" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">{% trans "Add theme" %}</button>
|
||||
</fieldset>
|
||||
</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.path }}</code></td>
|
||||
<td>
|
||||
<form method="POST" action="{% url 'settings-themes-delete' theme.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger is-light is-small">
|
||||
<span class="icon icon-x" aria-hideen="true"></span>
|
||||
<span>{% trans "Remove theme" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
@ -1,7 +1,7 @@
|
||||
{% load humanize %}{% load i18n %}{% load utilities %}
|
||||
{% with announcement.id|uuid as uuid %}
|
||||
<aside
|
||||
class="notification mb-1 p-3{% if not admin_mode %} is-hidden{% endif %} transition-y has-background-{{ announcement.display_type }}"
|
||||
class="notification mb-1 p-3{% if not admin_mode %} is-hidden{% endif %} transition-y {% if announcement.display_type %}has-background-{{ announcement.display_type }}{% endif %}"
|
||||
{% if not admin_mode %}data-hide="hide_announcement_{{ announcement.id }}"{% endif %}
|
||||
>
|
||||
<details>
|
||||
|
@ -69,7 +69,6 @@
|
||||
|
||||
{% block card-bonus %}
|
||||
{% if request.user.is_authenticated and not moderation_mode and not no_interact %}
|
||||
{% with status.id|uuid as uuid %}
|
||||
<section class="reply-panel is-hidden" id="show_comment_{{ status.id }}">
|
||||
<div class="card-footer">
|
||||
<div class="card-footer-item">
|
||||
@ -77,6 +76,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -16,11 +16,15 @@ def get_book_superlatives():
|
||||
models.Work.objects.annotate(
|
||||
rating=Avg(
|
||||
"editions__review__rating",
|
||||
filter=Q(editions__review__local=True, editions__review__deleted=False),
|
||||
filter=Q(
|
||||
editions__review__user__local=True, editions__review__deleted=False
|
||||
),
|
||||
),
|
||||
rating_count=Count(
|
||||
"editions__review",
|
||||
filter=Q(editions__review__local=True, editions__review__deleted=False),
|
||||
filter=Q(
|
||||
editions__review__user__local=True, editions__review__deleted=False
|
||||
),
|
||||
),
|
||||
)
|
||||
.annotate(weighted=F("rating") * F("rating_count") / total_ratings)
|
||||
@ -33,11 +37,15 @@ def get_book_superlatives():
|
||||
models.Work.objects.annotate(
|
||||
deviation=StdDev(
|
||||
"editions__review__rating",
|
||||
filter=Q(editions__review__local=True, editions__review__deleted=False),
|
||||
filter=Q(
|
||||
editions__review__user__local=True, editions__review__deleted=False
|
||||
),
|
||||
),
|
||||
rating_count=Count(
|
||||
"editions__review",
|
||||
filter=Q(editions__review__local=True, editions__review__deleted=False),
|
||||
filter=Q(
|
||||
editions__review__user__local=True, editions__review__deleted=False
|
||||
),
|
||||
),
|
||||
)
|
||||
.annotate(weighted=F("deviation") * F("rating_count") / total_ratings)
|
||||
|
86
bookwyrm/tests/test_context_processors.py
Normal file
86
bookwyrm/tests/test_context_processors.py
Normal file
@ -0,0 +1,86 @@
|
||||
""" test for context processor """
|
||||
from unittest.mock import patch
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.context_processors import site_settings
|
||||
|
||||
|
||||
class ContextProcessor(TestCase):
|
||||
"""pages you land on without really trying"""
|
||||
|
||||
def setUp(self):
|
||||
"""we need basic test data and mocks"""
|
||||
self.factory = RequestFactory()
|
||||
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
|
||||
"bookwyrm.activitystreams.populate_stream_task.delay"
|
||||
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
|
||||
self.local_user = models.User.objects.create_user(
|
||||
"mouse@local.com",
|
||||
"mouse@mouse.mouse",
|
||||
"password",
|
||||
local=True,
|
||||
localname="mouse",
|
||||
)
|
||||
self.anonymous_user = AnonymousUser
|
||||
self.anonymous_user.is_authenticated = False
|
||||
self.site = models.SiteSettings.objects.create()
|
||||
|
||||
def test_theme_unset(self):
|
||||
"""logged in user, no selected theme"""
|
||||
request = self.factory.get("")
|
||||
request.user = self.local_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-light.scss")
|
||||
|
||||
def test_theme_unset_logged_out(self):
|
||||
"""logged out user, no selected theme"""
|
||||
request = self.factory.get("")
|
||||
request.user = self.anonymous_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-light.scss")
|
||||
|
||||
def test_theme_instance_default(self):
|
||||
"""logged in user, instance default theme"""
|
||||
self.site.default_theme = models.Theme.objects.get(name="BookWyrm Dark")
|
||||
self.site.save()
|
||||
|
||||
request = self.factory.get("")
|
||||
request.user = self.local_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-dark.scss")
|
||||
|
||||
def test_theme_instance_default_logged_out(self):
|
||||
"""logged out user, instance default theme"""
|
||||
self.site.default_theme = models.Theme.objects.get(name="BookWyrm Dark")
|
||||
self.site.save()
|
||||
|
||||
request = self.factory.get("")
|
||||
request.user = self.anonymous_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-dark.scss")
|
||||
|
||||
def test_theme_user_set(self):
|
||||
"""logged in user, user theme"""
|
||||
self.local_user.theme = models.Theme.objects.get(name="BookWyrm Dark")
|
||||
self.local_user.save(broadcast=False, update_fields=["theme"])
|
||||
|
||||
request = self.factory.get("")
|
||||
request.user = self.local_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-dark.scss")
|
||||
|
||||
def test_theme_user_set_instance_default(self):
|
||||
"""logged in user, instance default theme"""
|
||||
self.site.default_theme = models.Theme.objects.get(name="BookWyrm Dark")
|
||||
self.site.save()
|
||||
|
||||
self.local_user.theme = models.Theme.objects.get(name="BookWyrm Light")
|
||||
self.local_user.save(broadcast=False, update_fields=["theme"])
|
||||
|
||||
request = self.factory.get("")
|
||||
request.user = self.local_user
|
||||
settings = site_settings(request)
|
||||
self.assertEqual(settings["site_theme"], "css/themes/bookwyrm-light.scss")
|
@ -86,6 +86,12 @@ 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/themes/(?P<theme_id>\d+)/delete/?$",
|
||||
views.delete_theme,
|
||||
name="settings-themes-delete",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/announcements/?$",
|
||||
views.Announcements.as_view(),
|
||||
|
@ -21,6 +21,7 @@ from .admin.reports import (
|
||||
moderator_delete_user,
|
||||
)
|
||||
from .admin.site import Site
|
||||
from .admin.themes import Themes, delete_theme
|
||||
from .admin.user_admin import UserAdmin, UserAdminList
|
||||
|
||||
# user preferences
|
||||
|
@ -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)
|
||||
|
59
bookwyrm/views/admin/themes.py
Normal file
59
bookwyrm/views/admin/themes.py
Normal file
@ -0,0 +1,59 @@
|
||||
""" manage themes """
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.staticfiles.utils import get_files
|
||||
from django.contrib.staticfiles.storage import StaticFilesStorage
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
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"""
|
||||
return TemplateResponse(request, "settings/themes.html", get_view_data())
|
||||
|
||||
def post(self, request):
|
||||
"""edit the site settings"""
|
||||
form = forms.ThemeForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
|
||||
data = get_view_data()
|
||||
|
||||
if not form.is_valid():
|
||||
data["theme_form"] = form
|
||||
else:
|
||||
data["success"] = True
|
||||
return TemplateResponse(request, "settings/themes.html", data)
|
||||
|
||||
|
||||
def get_view_data():
|
||||
"""data for view"""
|
||||
choices = list(get_files(StaticFilesStorage(), location="css/themes"))
|
||||
current = models.Theme.objects.values_list("path", flat=True)
|
||||
return {
|
||||
"themes": models.Theme.objects.all(),
|
||||
"choices": [c for c in choices if c not in current and c[-5:] == ".scss"],
|
||||
"theme_form": forms.ThemeForm(),
|
||||
}
|
||||
|
||||
|
||||
@require_POST
|
||||
@permission_required("bookwyrm.edit_instance_settings", raise_exception=True)
|
||||
# pylint: disable=unused-argument
|
||||
def delete_theme(request, theme_id):
|
||||
"""Remove a theme"""
|
||||
get_object_or_404(models.Theme, id=theme_id).delete()
|
||||
return redirect("settings-themes")
|
Reference in New Issue
Block a user