From 65e1cb4cdaa4291298d67eb65525e58855610eea Mon Sep 17 00:00:00 2001 From: magnified103 <29406816+magnified103@users.noreply.github.com> Date: Fri, 5 Jan 2024 01:59:31 +0700 Subject: [PATCH] Make site logo configurable in `misc_config` --- dmoj/settings.py | 3 ++ dmoj/urls.py | 3 ++ .../migrations/0203_unique_miscconfig_key.py | 18 +++++++++ judge/models/interface.py | 2 +- judge/views/misc_config.py | 37 ++++++++++++++++++ judge/views/widgets.py | 11 ++++++ templates/base.html | 7 ++++ templates/misc_config/edit.html | 38 +++++++++++++++++++ templates/site-logo-fragment.html | 6 ++- 9 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 judge/migrations/0203_unique_miscconfig_key.py create mode 100644 judge/views/misc_config.py create mode 100644 templates/misc_config/edit.html diff --git a/dmoj/settings.py b/dmoj/settings.py index 59f4ad7f9..9d640f95d 100755 --- a/dmoj/settings.py +++ b/dmoj/settings.py @@ -630,6 +630,9 @@ SUBMISSION_FILE_UPLOAD_URL_PREFIX = '/submission_file' SUBMISSION_FILE_UPLOAD_MEDIA_DIR = 'submission_file' +STATIC_UPLOAD_URL_PREFIX = '/static-upload' +STATIC_UPLOAD_MEDIA_DIR = 'static-upload' + # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases diff --git a/dmoj/urls.py b/dmoj/urls.py index 103e0171f..337562e24 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -18,6 +18,7 @@ from judge.views import TitledTemplateView, api, blog, comment, contests, language, license, mailgun, organization, \ preview, problem, problem_manage, ranked_submission, register, stats, status, submission, tag, tasks, ticket, \ two_factor, user, widgets +from judge.views.misc_config import MiscConfigEdit from judge.views.problem_data import ProblemDataView, ProblemSubmissionDiff, \ problem_data_file, problem_init_view from judge.views.register import ActivationView, RegistrationView @@ -415,6 +416,8 @@ def paged_list_view(view, name): path('failure', tasks.demo_failure), path('progress', tasks.demo_progress), ])), + + path('misc_config/', MiscConfigEdit.as_view(), name='misc_config'), ] favicon_paths = ['apple-touch-icon-180x180.png', 'apple-touch-icon-114x114.png', 'android-chrome-72x72.png', diff --git a/judge/migrations/0203_unique_miscconfig_key.py b/judge/migrations/0203_unique_miscconfig_key.py new file mode 100644 index 000000000..96935c6a3 --- /dev/null +++ b/judge/migrations/0203_unique_miscconfig_key.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.23 on 2024-01-12 10:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('judge', '0202_import_polygon_package'), + ] + + operations = [ + migrations.AlterField( + model_name='miscconfig', + name='key', + field=models.CharField(max_length=30, unique=True, verbose_name='key'), + ), + ] diff --git a/judge/models/interface.py b/judge/models/interface.py index 0949af4ab..1d69b480a 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -16,7 +16,7 @@ class MiscConfig(models.Model): - key = models.CharField(max_length=30, verbose_name=_('key'), db_index=True) + key = models.CharField(max_length=30, verbose_name=_('key'), unique=True) value = models.TextField(verbose_name=_('value'), blank=True) def __str__(self): diff --git a/judge/views/misc_config.py b/judge/views/misc_config.py new file mode 100644 index 000000000..f2e698878 --- /dev/null +++ b/judge/views/misc_config.py @@ -0,0 +1,37 @@ +from django import forms +from django.db import transaction +from django.http import Http404 +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django.views.generic import FormView + +from judge.models import MiscConfig +from judge.utils.views import TitleMixin +from judge.views.widgets import static_uploader + + +class MiscConfigForm(forms.Form): + logo = forms.FileField(help_text='The site logo e.g. the image in the top left corner.') + + +class MiscConfigEdit(TitleMixin, FormView): + template_name = 'misc_config/edit.html' + form_class = MiscConfigForm + title = _('Site settings') + + def get_success_url(self): + return reverse('home') + + def form_valid(self, form): + with transaction.atomic(): + logo = form.files.get('logo', default=None) + if logo is not None: + logo_url = static_uploader(logo) + config, _ = MiscConfig.objects.update_or_create(key='site_logo', defaults={'value': logo_url}) + config.save() + return super().form_valid(form) + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_superuser: + raise Http404 + return super().dispatch(request, *args, **kwargs) diff --git a/judge/views/widgets.py b/judge/views/widgets.py index bcaa2f75e..2a838e39e 100755 --- a/judge/views/widgets.py +++ b/judge/views/widgets.py @@ -88,6 +88,17 @@ def martor_image_uploader(request): return HttpResponse(data, content_type='application/json') +def static_uploader(static_file): + ext = os.path.splitext(static_file.name)[1] + name = str(uuid.uuid4()) + ext + default_storage.save(os.path.join(settings.STATIC_UPLOAD_MEDIA_DIR, name), static_file) + url_base = getattr(settings, 'STATIC_UPLOAD_URL_PREFIX', + urljoin(settings.MEDIA_URL, settings.STATIC_UPLOAD_MEDIA_DIR)) + if not url_base.endswith('/'): + url_base += '/' + return urljoin(url_base, name) + + def csrf_failure(request: HttpRequest, reason=''): # Redirect to the same page in case of CSRF failure # So that we can turn on cloudflare DDOS protection without diff --git a/templates/base.html b/templates/base.html index e084e7462..9ca99749c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -269,6 +269,13 @@ + {% if request.user.is_superuser %} + + + + + + {% endif %} + +{% endblock %} + +{% block body %} +
+ {% if request.user.is_staff %} + + {% endif %} +
+ {% if form.errors %} +

{{ _('Please correct the error below.') }}

+ {% endif %} + {% csrf_token %} + {{ form.as_table() }}
+ + +
+
+
+
+{% endblock %} + diff --git a/templates/site-logo-fragment.html b/templates/site-logo-fragment.html index 9f315571f..5aa48d413 100644 --- a/templates/site-logo-fragment.html +++ b/templates/site-logo-fragment.html @@ -3,6 +3,8 @@ {% elif logo_override_image is defined and logo_override_image %} {{ SITE_NAME }} {% else %} - {{ SITE_NAME }} + {% with logo_url=misc_config.site_logo|default(static('icons/logo.svg'), true) %} + {{ SITE_NAME }} + {% endwith %} {% endif %}