Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable configuration of site logo via UI #374

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dmoj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,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

Expand Down
3 changes: 3 additions & 0 deletions dmoj/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.magazine import MagazinePage
from judge.views.problem_data import ProblemDataView, ProblemSubmissionDiff, \
problem_data_file, problem_init_view
Expand Down Expand Up @@ -421,6 +422,8 @@ def paged_list_view(view, name):
])),

path('magazine/', MagazinePage.as_view(), name='magazine'),

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',
Expand Down
18 changes: 18 additions & 0 deletions judge/migrations/0203_unique_miscconfig_key.py
Original file line number Diff line number Diff line change
@@ -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'),
),
]
2 changes: 1 addition & 1 deletion judge/models/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
37 changes: 37 additions & 0 deletions judge/views/misc_config.py
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions judge/views/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@
</ul>

<span id="user-links">
{% if request.user.is_superuser %}
<a href="{{ url('misc_config') }}" style="margin-right: 5px;">
<span title="{{ _('Site settings') }}">
<i class="fa fa-gear" style="color: rgb(200,200,200) ; font-size: 1.15em"></i>
</span>
</a>
{% endif %}
<a href=
{% if request.resolver_match.view_name == "problem_detail" and problem %}
"{{ url('new_problem_ticket', problem.code) }}"
Expand Down
38 changes: 38 additions & 0 deletions templates/misc_config/edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends "base.html" %}

{% block js_media %}
{{ form.media.js }}
{% endblock %}

{% block media %}
{{ form.media.css }}
<link rel="stylesheet" type="text/css" href="{{ static('ui_form.css') }}">
<style>
.hidden {
display: none;
}
</style>
{% endblock %}

{% block body %}
<div>
{% if request.user.is_staff %}
<div class="alert alert-warning alert-dismissable">
<a class="close">x</a>
<a href="{{ url('admin:judge_miscconfig_changelist') }}">{{ _('Configure in admin panel for more options') }}</a>
</div>
{% endif %}
<form action="" method="post" class="form-area" enctype="multipart/form-data" style="display: flex; justify-content: center; flex-direction: column;">
{% if form.errors %}
<p class="errornote"> {{ _('Please correct the error below.') }}</p>
{% endif %}
{% csrf_token %}
<table class="django-as-table">{{ form.as_table() }}</table>
<table>
<tr><td style="float: left;">
<td style="float: right;"><input type="submit" value="{{ _('Update') }}" class="button"></td></tr>
</table>
</form>
</div>
{% endblock %}

6 changes: 4 additions & 2 deletions templates/site-logo-fragment.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
{% elif logo_override_image is defined and logo_override_image %}
<img src="{{ logo_override_image|camo }}" alt="{{ SITE_NAME }}" height="40" style="border: none; padding-top: 4px">
{% else %}
<img src="{{ static('icons/logo.svg') }}" alt="{{ SITE_NAME }}" width="130" height="40"
onerror="this.src=&quot;{{ static('icons/logo.png') }}&quot;; this.onerror=null" style="border: none; padding-top: 4px">
{% with logo_url=misc_config.site_logo|default(static('icons/logo.svg'), true) %}
<img src="{{ logo_url }}" alt="{{ SITE_NAME }}" width="130" height="40"
onerror="this.src=&quot;{{ logo_url }}&quot;; this.onerror=null" style="border: none; padding-top: 4px">
{% endwith %}
{% endif %}
Loading