diff --git a/README.md b/README.md index 4656695..b340c76 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Those files are markdown files that are rendered as HTML. You have to supply the The following files are expected: * `imprint.md` with your imprint information. +* `privacy_policy.md` with your privacy policy. ## Settings @@ -176,3 +177,13 @@ BORG_SERVER_PUBKEYS = [ ), ] ``` + +## Webserver Config / Content-Security-Policy + +This page by itself does not load external sources and does not use inline javascript/styling. +It only loads javascript, CSS and images. +Therefore the following Content-Security-Policy should work fine in the standard cases: + +``` +Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self';" +``` diff --git a/community_backup/community_backup/settings.py b/community_backup/community_backup/settings.py index fc103fc..b733ca7 100644 --- a/community_backup/community_backup/settings.py +++ b/community_backup/community_backup/settings.py @@ -120,7 +120,7 @@ STATICFILES_DIRS = [ BASE_DIR / "static", ] -RELEASE_VERSION = "0.13" +RELEASE_VERSION = "0.15" # Import settings from configuration.py try: diff --git a/community_backup/static/webui/css/webui.css b/community_backup/static/webui/css/webui.css index 40cedd4..514ae45 100644 --- a/community_backup/static/webui/css/webui.css +++ b/community_backup/static/webui/css/webui.css @@ -1,5 +1,7 @@ -h1 { - font-size: 2rem +body { + display: flex; + flex-direction: column; + height: 100vh; } .pixelart { @@ -10,3 +12,7 @@ h1 { min-width: 32px; min-height: 32px; } + +.word-break-break-all { + word-break: break-all; +} diff --git a/community_backup/static/webui/js/webui.js b/community_backup/static/webui/js/webui.js new file mode 100644 index 0000000..03da0f8 --- /dev/null +++ b/community_backup/static/webui/js/webui.js @@ -0,0 +1,24 @@ +// +// Activation script for the bulma burger menu. +// Copied from https://bulma.io/documentation/components/navbar/ +// +document.addEventListener('DOMContentLoaded', () => { + + // Get all "navbar-burger" elements + const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); + + // Add a click event on each of them + $navbarBurgers.forEach( el => { + el.addEventListener('click', () => { + + // Get the target from the "data-target" attribute + const target = el.dataset.target; + const $target = document.getElementById(target); + + // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu" + el.classList.toggle('is-active'); + $target.classList.toggle('is-active'); + + }); + }); +}); diff --git a/community_backup/webui/admin.py b/community_backup/webui/admin.py index 7d0a18b..8f5f29c 100644 --- a/community_backup/webui/admin.py +++ b/community_backup/webui/admin.py @@ -1,7 +1,50 @@ from django.contrib import admin +from django.urls import path, reverse_lazy +from django.views.generic import FormView +from django import forms from .models import BorgRepository, Voucher + +class VoucherBulkCreateForm(forms.Form): + prefix = forms.CharField(label="Voucher prefix") + amount = forms.IntegerField(label="Amount") + + +class VoucherBulkCreateView(FormView): + form_class = VoucherBulkCreateForm + template_name = "admin/bulk_generate.html" + admin_site_context = None + success_url = reverse_lazy("admin:webui_voucher_changelist") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # inject the admin site context so that the navbar and other things work as intended + context.update(self.admin_site_context.each_context(self.request)) + return context + + def form_valid(self, form): + Voucher.bulk_generate(form.cleaned_data["prefix"], form.cleaned_data["amount"]) + return super().form_valid(form) + + +@admin.register(Voucher) +class VoucherAdmin(admin.ModelAdmin): + change_list_template = "admin/voucher_change_list.html" + + def get_urls(self): + urls = super().get_urls() + my_urls = [ + path( + "bulk_generate/", + self.admin_site.admin_view( + VoucherBulkCreateView.as_view(admin_site_context=self.admin_site) + ), + name="voucher_bulk_generate", + ) + ] + return my_urls + urls + + # Register your models here. admin.site.register(BorgRepository) -admin.site.register(Voucher) diff --git a/community_backup/webui/management/commands/add_vouchers.py b/community_backup/webui/management/commands/add_vouchers.py index f983d92..f8d0e6b 100644 --- a/community_backup/webui/management/commands/add_vouchers.py +++ b/community_backup/webui/management/commands/add_vouchers.py @@ -11,14 +11,7 @@ class Command(BaseCommand): parser.add_argument("amount", type=int) def handle(self, *args, **options): - unabmigous_characters = "abcdefghijkmnopqrstuvwxyzACDEFHJKLMNPQRTUVWXY1234679" - - vouchers = set() - while len(vouchers) < options["amount"]: - random_part = "".join(choices(unabmigous_characters, k=5)) - vouchers.add(f"{options['prefix']}-{random_part}") - - Voucher.objects.bulk_create(Voucher(code=v) for v in vouchers) + vouchers = Voucher.bulk_generate(options["prefix"], options["amount"]) header = r"""\begin{document} \begin{center} diff --git a/community_backup/webui/models.py b/community_backup/webui/models.py index 36e338f..f5686d8 100644 --- a/community_backup/webui/models.py +++ b/community_backup/webui/models.py @@ -1,11 +1,12 @@ +from random import choices +from pathlib import Path + from django.db import models from django.contrib.auth.models import User from django.core.validators import RegexValidator from django.conf import settings -from pathlib import Path import msgpack - from .tasks import update_user @@ -121,3 +122,20 @@ class Voucher(models.Model): def __str__(self): return f"Voucher '{self.code}'" + + @classmethod + def bulk_generate(cls, prefix: str, amount: int) -> set[str]: + """ + Create `amount` vouchers starting with `prefix`. + + Returns a set of the voucher codes as strings. + """ + unabmigous_characters = "abcdefghijkmnopqrstuvwxyzACDEFHJKLMNPQRTUVWXY1234679" + + vouchers = set() + while len(vouchers) < amount: + random_part = "".join(choices(unabmigous_characters, k=5)) + vouchers.add(f"{prefix}-{random_part}") + + Voucher.objects.bulk_create(Voucher(code=v) for v in vouchers) + return vouchers diff --git a/community_backup/webui/templates/admin/bulk_generate.html b/community_backup/webui/templates/admin/bulk_generate.html new file mode 100644 index 0000000..b44f745 --- /dev/null +++ b/community_backup/webui/templates/admin/bulk_generate.html @@ -0,0 +1,8 @@ +{% extends "admin/base_site.html" %} +{% block content %} +
+{% endblock %} diff --git a/community_backup/webui/templates/admin/voucher_change_list.html b/community_backup/webui/templates/admin/voucher_change_list.html new file mode 100644 index 0000000..6283e9c --- /dev/null +++ b/community_backup/webui/templates/admin/voucher_change_list.html @@ -0,0 +1,6 @@ +{% extends "admin/change_list.html" %} + +{% block object-tools-items %} + {{ block.super }} +