Merge branch 'main' into user-migration

This commit is contained in:
Hugh Rundle 2023-11-22 21:00:04 +11:00 committed by GitHub
commit 0276c15948
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 158 additions and 2 deletions

View 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),
),
]

View file

@ -150,6 +150,7 @@ class Theme(SiteModel):
created_date = models.DateTimeField(auto_now_add=True)
name = 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):
# pylint: disable=invalid-str-returned

View 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 %}

View file

@ -12,6 +12,15 @@
{% endblock %}
{% 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 %}
<div class="notification is-success is-light">
<span class="icon icon-check" aria-hidden="true"></span>
@ -98,6 +107,9 @@
<th>
{% trans "Actions" %}
</th>
<th>
{% trans "Status" %}
</th>
</tr>
{% for theme in themes %}
<tr>
@ -112,6 +124,37 @@
</button>
</form>
</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>
{% endfor %}
</table>

View file

@ -132,6 +132,7 @@ def id_to_username(user_id):
def get_file_size(file):
"""display the size of a file in human readable terms"""
try:
raw_size = os.stat(file.path).st_size
if raw_size < 1024:
@ -144,7 +145,14 @@ def get_file_size(file):
except Exception: # pylint: disable=broad-except
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")
def is_instance_admin(localname):
"""Returns a boolean indicating whether the user is the instance admin account"""

View file

@ -86,3 +86,25 @@ class AdminThemesViews(TestCase):
with self.assertRaises(PermissionDenied):
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)

View file

@ -109,6 +109,11 @@ urlpatterns = [
views.delete_theme,
name="settings-themes-delete",
),
re_path(
r"^settings/themes/(?P<theme_id>\d+)/test/?$",
views.test_theme,
name="settings-themes-test",
),
re_path(
r"^settings/announcements/?$",
views.Announcements.as_view(),
@ -813,3 +818,6 @@ urlpatterns.extend(staticfiles_urlpatterns())
# pylint: disable=invalid-name
handler500 = "bookwyrm.views.server_error"
# pylint: disable=invalid-name
handler403 = "bookwyrm.views.permission_denied"

View file

@ -32,7 +32,7 @@ from .admin.reports import (
moderator_delete_user,
)
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
# user preferences
@ -169,3 +169,4 @@ from .annual_summary import (
summary_revoke_key,
)
from .server_error import server_error
from .permission_denied import permission_denied

View file

@ -6,6 +6,8 @@ from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.http import require_POST
from sass_processor.processor import sass_processor
from bookwyrm import forms, models
@ -40,6 +42,7 @@ class Themes(View):
def get_view_data():
"""data for view"""
return {
"broken_theme": models.Theme.objects.filter(loads=False).exists(),
"themes": models.Theme.objects.all(),
"theme_form": forms.ThemeForm(),
}
@ -52,3 +55,20 @@ def delete_theme(request, theme_id):
"""Remove a theme"""
get_object_or_404(models.Theme, id=theme_id).delete()
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")

View 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")