mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-06-02 21:39:23 +00:00
Merge branch 'main' into user-migration
This commit is contained in:
commit
0276c15948
18
bookwyrm/migrations/0188_theme_loads.py
Normal file
18
bookwyrm/migrations/0188_theme_loads.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.23 on 2023-11-20 18:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookwyrm", "0187_partial_publication_dates"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="theme",
|
||||||
|
name="loads",
|
||||||
|
field=models.BooleanField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -150,6 +150,7 @@ class Theme(SiteModel):
|
||||||
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)
|
path = models.CharField(max_length=50, unique=True)
|
||||||
|
loads = models.BooleanField(null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# pylint: disable=invalid-str-returned
|
# pylint: disable=invalid-str-returned
|
||||||
|
|
20
bookwyrm/templates/403.html
Normal file
20
bookwyrm/templates/403.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load utilities %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Oh no!" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="block">
|
||||||
|
<h1 class="title">{% trans "Permission Denied" %}</h1>
|
||||||
|
<p class="content">
|
||||||
|
{% blocktrans trimmed with level=request.user|get_user_permission %}
|
||||||
|
You do not have permission to view this page or perform this action. Your user permission level is <code>{{ level }}</code>.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p class="content">{% trans "If you think you should have access, please speak to your BookWyrm server administrator." %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -12,6 +12,15 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block panel %}
|
{% block panel %}
|
||||||
|
{% if broken_theme %}
|
||||||
|
<div class="notification is-danger">
|
||||||
|
<span class="icon icon-warning" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "One of your themes appears to be broken. Selecting this theme will make the application unusable." %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if success %}
|
{% if success %}
|
||||||
<div class="notification is-success is-light">
|
<div class="notification is-success is-light">
|
||||||
<span class="icon icon-check" aria-hidden="true"></span>
|
<span class="icon icon-check" aria-hidden="true"></span>
|
||||||
|
@ -98,6 +107,9 @@
|
||||||
<th>
|
<th>
|
||||||
{% trans "Actions" %}
|
{% trans "Actions" %}
|
||||||
</th>
|
</th>
|
||||||
|
<th>
|
||||||
|
{% trans "Status" %}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for theme in themes %}
|
{% for theme in themes %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -112,6 +124,37 @@
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if theme.loads is None %}
|
||||||
|
|
||||||
|
<form method="POST" action="{% url 'settings-themes-test' theme.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="button is-small">
|
||||||
|
<span class="icon icon-question-circle" aria-hidden="true"></span>
|
||||||
|
<span>{% trans "Test theme" %}</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% elif not theme.loads %}
|
||||||
|
|
||||||
|
<span class="tag is-danger">
|
||||||
|
<span class="icon icon-warning" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Broken theme" %}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<span class="tag is-success">
|
||||||
|
<span class="icon icon-check" aria-hidden="true"></span>
|
||||||
|
<span>
|
||||||
|
{% trans "Loaded successfully" %}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -132,6 +132,7 @@ def id_to_username(user_id):
|
||||||
def get_file_size(file):
|
def get_file_size(file):
|
||||||
"""display the size of a file in human readable terms"""
|
"""display the size of a file in human readable terms"""
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw_size = os.stat(file.path).st_size
|
raw_size = os.stat(file.path).st_size
|
||||||
if raw_size < 1024:
|
if raw_size < 1024:
|
||||||
|
@ -144,7 +145,14 @@ def get_file_size(file):
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="get_user_permission")
|
||||||
|
def get_user_permission(user):
|
||||||
|
"""given a user, return their permission level"""
|
||||||
|
|
||||||
|
return user.groups.first() or "User"
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="is_instance_admin")
|
@register.filter(name="is_instance_admin")
|
||||||
def is_instance_admin(localname):
|
def is_instance_admin(localname):
|
||||||
"""Returns a boolean indicating whether the user is the instance admin account"""
|
"""Returns a boolean indicating whether the user is the instance admin account"""
|
||||||
|
|
|
@ -86,3 +86,25 @@ class AdminThemesViews(TestCase):
|
||||||
|
|
||||||
with self.assertRaises(PermissionDenied):
|
with self.assertRaises(PermissionDenied):
|
||||||
view(request)
|
view(request)
|
||||||
|
|
||||||
|
def test_test_theme(self):
|
||||||
|
"""Testing testing testing test"""
|
||||||
|
theme = models.Theme.objects.first()
|
||||||
|
self.assertIsNone(theme.loads)
|
||||||
|
request = self.factory.post("")
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
views.test_theme(request, theme.id)
|
||||||
|
theme.refresh_from_db()
|
||||||
|
self.assertTrue(theme.loads)
|
||||||
|
|
||||||
|
def test_test_theme_broken(self):
|
||||||
|
"""Testing test for testing when it's a bad theme"""
|
||||||
|
theme = models.Theme.objects.create(name="bad theme", path="dsf/sdf/sdf.sdf")
|
||||||
|
self.assertIsNone(theme.loads)
|
||||||
|
request = self.factory.post("")
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
views.test_theme(request, theme.id)
|
||||||
|
theme.refresh_from_db()
|
||||||
|
self.assertIs(False, theme.loads)
|
||||||
|
|
|
@ -109,6 +109,11 @@ urlpatterns = [
|
||||||
views.delete_theme,
|
views.delete_theme,
|
||||||
name="settings-themes-delete",
|
name="settings-themes-delete",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r"^settings/themes/(?P<theme_id>\d+)/test/?$",
|
||||||
|
views.test_theme,
|
||||||
|
name="settings-themes-test",
|
||||||
|
),
|
||||||
re_path(
|
re_path(
|
||||||
r"^settings/announcements/?$",
|
r"^settings/announcements/?$",
|
||||||
views.Announcements.as_view(),
|
views.Announcements.as_view(),
|
||||||
|
@ -813,3 +818,6 @@ urlpatterns.extend(staticfiles_urlpatterns())
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
handler500 = "bookwyrm.views.server_error"
|
handler500 = "bookwyrm.views.server_error"
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
handler403 = "bookwyrm.views.permission_denied"
|
||||||
|
|
|
@ -32,7 +32,7 @@ from .admin.reports import (
|
||||||
moderator_delete_user,
|
moderator_delete_user,
|
||||||
)
|
)
|
||||||
from .admin.site import Site, Registration, RegistrationLimited
|
from .admin.site import Site, Registration, RegistrationLimited
|
||||||
from .admin.themes import Themes, delete_theme
|
from .admin.themes import Themes, delete_theme, test_theme
|
||||||
from .admin.user_admin import UserAdmin, UserAdminList, ActivateUserAdmin
|
from .admin.user_admin import UserAdmin, UserAdminList, ActivateUserAdmin
|
||||||
|
|
||||||
# user preferences
|
# user preferences
|
||||||
|
@ -169,3 +169,4 @@ from .annual_summary import (
|
||||||
summary_revoke_key,
|
summary_revoke_key,
|
||||||
)
|
)
|
||||||
from .server_error import server_error
|
from .server_error import server_error
|
||||||
|
from .permission_denied import permission_denied
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.utils.decorators import method_decorator
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
|
from sass_processor.processor import sass_processor
|
||||||
|
|
||||||
from bookwyrm import forms, models
|
from bookwyrm import forms, models
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ class Themes(View):
|
||||||
def get_view_data():
|
def get_view_data():
|
||||||
"""data for view"""
|
"""data for view"""
|
||||||
return {
|
return {
|
||||||
|
"broken_theme": models.Theme.objects.filter(loads=False).exists(),
|
||||||
"themes": models.Theme.objects.all(),
|
"themes": models.Theme.objects.all(),
|
||||||
"theme_form": forms.ThemeForm(),
|
"theme_form": forms.ThemeForm(),
|
||||||
}
|
}
|
||||||
|
@ -52,3 +55,20 @@ def delete_theme(request, theme_id):
|
||||||
"""Remove a theme"""
|
"""Remove a theme"""
|
||||||
get_object_or_404(models.Theme, id=theme_id).delete()
|
get_object_or_404(models.Theme, id=theme_id).delete()
|
||||||
return redirect("settings-themes")
|
return redirect("settings-themes")
|
||||||
|
|
||||||
|
|
||||||
|
@require_POST
|
||||||
|
@permission_required("bookwyrm.system_administration", raise_exception=True)
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def test_theme(request, theme_id):
|
||||||
|
"""Remove a theme"""
|
||||||
|
theme = get_object_or_404(models.Theme, id=theme_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sass_processor(theme.path)
|
||||||
|
theme.loads = True
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
theme.loads = False
|
||||||
|
|
||||||
|
theme.save()
|
||||||
|
return redirect("settings-themes")
|
||||||
|
|
15
bookwyrm/views/permission_denied.py
Normal file
15
bookwyrm/views/permission_denied.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
"""custom 403 handler to enable context processors"""
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
|
||||||
|
from .helpers import is_api_request
|
||||||
|
|
||||||
|
|
||||||
|
def permission_denied(request, exception): # pylint: disable=unused-argument
|
||||||
|
"""permission denied page"""
|
||||||
|
|
||||||
|
if request.method == "POST" or is_api_request(request):
|
||||||
|
return HttpResponse(status=403)
|
||||||
|
|
||||||
|
return TemplateResponse(request, "403.html")
|
Loading…
Reference in a new issue