Skip to content

Commit

Permalink
Add app for a custom welcome page (#402)
Browse files Browse the repository at this point in the history
* Add welcomepage app

* Fix typo

* Welcome page only visible when any message exists

* Add tests
  • Loading branch information
MasloMaslane authored Sep 11, 2024
1 parent 6ea47fe commit 0139795
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 2 deletions.
2 changes: 2 additions & 0 deletions oioioi/base/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
from oioioi.contests.models import Contest
from oioioi.contests.utils import is_contest_admin
from oioioi.szkopul.views import main_page_view as szkopul_main_page
from oioioi.welcomepage.views import welcome_page_view

if not getattr(settings, 'TESTS', False):
print(
Expand Down Expand Up @@ -163,6 +164,7 @@ class TestIndexNoContest(TestCase):

@override_settings(DEFAULT_GLOBAL_PORTAL_AS_MAIN_PAGE=False)
def test_no_contest(self):
unregister_main_page_view(welcome_page_view)
unregister_main_page_view(szkopul_main_page)
with self.assertNumQueriesLessThan(50):
response = self.client.get('/')
Expand Down
3 changes: 1 addition & 2 deletions oioioi/deployment/settings.py.template
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ INSTALLED_APPS = (
# 'oioioi.problemsharing',
# 'oioioi.usergroups',
# 'oioioi.usercontests',
# 'oioioi.welcomepage',
) + INSTALLED_APPS

# If set to locations of flite and sox executables, enables audio playback
Expand Down Expand Up @@ -580,5 +581,3 @@ ZEUS_INSTANCES = {

# Experimental
# USE_ACE_EDITOR = False


6 changes: 6 additions & 0 deletions oioioi/szkopul/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
from django.test.utils import override_settings
from django.urls import NoReverseMatch, reverse

from oioioi.base.main_page import unregister_main_page_view
from oioioi.base.tests import TestCase
from oioioi.contests.current_contest import ContestMode
from oioioi.contests.models import ProblemInstance, Submission
from oioioi.problems.models import Problem
from oioioi.welcomepage.views import welcome_page_view


class TestMainPageView(TestCase):
Expand All @@ -23,6 +25,10 @@ class TestMainPageView(TestCase):
'test_submission',
]

def setUp(self):
super(TestMainPageView, self).setUp()
unregister_main_page_view(welcome_page_view)

@override_settings(CONTEST_MODE=ContestMode.neutral)
def test_navbar_links(self):
try:
Expand Down
1 change: 1 addition & 0 deletions oioioi/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'oioioi.problemsharing',
'oioioi.usercontests',
'oioioi.mp',
'oioioi.welcomepage',
) + INSTALLED_APPS

TEMPLATES[0]['OPTIONS']['context_processors'] += [
Expand Down
1 change: 1 addition & 0 deletions oioioi/welcomepage/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Welcome page with custom message which is the default page if the app is enabled.
Empty file added oioioi/welcomepage/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions oioioi/welcomepage/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class WelcomePageAppConfig(AppConfig):
default_auto_field = 'django.db.models.AutoField'
name = "oioioi.welcomepage"
37 changes: 37 additions & 0 deletions oioioi/welcomepage/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django import forms
from django.conf import settings
from django.forms import modelformset_factory

from oioioi.welcomepage.models import WelcomePageMessage


class WelcomePageMessageForm(forms.ModelForm):
class Meta(object):
model = WelcomePageMessage
fields = ['content', 'language']

language = forms.ChoiceField(
label="Language",
choices=settings.LANGUAGES,
widget=forms.HiddenInput(),
)

content = forms.CharField(
label="Content",
widget=forms.Textarea(attrs={
'rows': 10,
'style': 'white-space: pre;',
'class': 'monospace',
}),
)

WelcomePageMessageFormset = modelformset_factory(
WelcomePageMessage,
form=WelcomePageMessageForm,
extra=len(settings.LANGUAGES),
min_num=1,
max_num=len(settings.LANGUAGES),
validate_min=True,
validate_max=True,
can_delete=True,
)
26 changes: 26 additions & 0 deletions oioioi/welcomepage/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 4.2.16 on 2024-09-06 09:47

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='WelcomePageMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content', models.TextField(blank=True, verbose_name='message')),
('language', models.CharField(max_length=6, verbose_name='language code')),
],
options={
'verbose_name': 'welcome page message',
'verbose_name_plural': 'welcome page messages',
},
),
]
Empty file.
15 changes: 15 additions & 0 deletions oioioi/welcomepage/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _


class WelcomePageMessage(models.Model):
content = models.TextField(verbose_name=_("message"), blank=True)
language = models.CharField(max_length=6, verbose_name=_("language code"))

class Meta:
verbose_name = _("welcome page message")
verbose_name_plural = _("welcome page messages")

def render_content(self):
return mark_safe(self.content)
9 changes: 9 additions & 0 deletions oioioi/welcomepage/static/welcomepage/textfield-tab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$('textarea').on('keydown', function (e) {
if (e.key === 'Tab') {
e.preventDefault();
const start = this.selectionStart;
const end = this.selectionEnd;
this.value = this.value.substring(0, start) + '\t' + this.value.substring(end);
this.selectionStart = this.selectionEnd = start + 1;
}
});
31 changes: 31 additions & 0 deletions oioioi/welcomepage/templates/welcomepage/welcome-page-edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{% extends "base-with-menu.html" %}
{% load i18n static %}

{% block title %}Edit welcome page content{% endblock %}

{% block main-content %}
<h2>Edit welcome page content</h2>
<form style="max-width: initial;" method="POST">
{% csrf_token %}
{% include "ingredients/translation-formset.html" %}
<div class="form-group">
<a href="{% url 'delete_welcome_page' %}" class="btn btn-danger">
{% trans "Delete all messages" %}
</a>
</div>

<div class="form-group">
<button type="submit" class="btn btn-primary">
{% trans "Save" %}
</button>
</div>

</form>

<script type="text/javascript" src="{% static 'welcomepage/textfield-tab.js' %}"></script>
<script>
new TranslationFormset({
requiredFieldsSelector: '[id*="content"]'
});
</script>
{% endblock %}
20 changes: 20 additions & 0 deletions oioioi/welcomepage/templates/welcomepage/welcome-page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% extends "simple-centered.html" %}
{% load i18n %}

{% block title %}{{ title }}{% endblock %}

{% block content %}

{{ welcome_page_msg.render_content }}

<div class="text-center">
{% if show_edit_button %}
<div class="form-group">
<a href="{% url 'edit_welcome_page' %}" class="btn btn-primary mt-3">
{% trans "Edit message" %}
</a>
</div>
{% endif %}
</div>

{% endblock %}
60 changes: 60 additions & 0 deletions oioioi/welcomepage/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from oioioi.base.main_page import unregister_main_page_view
from oioioi.base.tests import TestCase
from django.urls import reverse

from oioioi.welcomepage.models import WelcomePageMessage


class TestWelcomePage(TestCase):
fixtures = ['test_users']

def test_button_visibility(self):
WelcomePageMessage.objects.create(language='en', content='Welcome to OIOIOI!')

self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Edit message')

self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'Edit message')

response = self.client.get(reverse('edit_welcome_page'))
self.assertEqual(response.status_code, 403)

def test_no_message(self):
self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 403)

def test_message(self):
self.assertTrue(self.client.login(username='test_user'))
msgs = {
'pl': 'Witaj na OIOIOI!',
'en': 'Welcome to OIOIOI!'
}
for lang, msg in msgs.items():
WelcomePageMessage.objects.create(language=lang, content=msg)

for lang, msg in msgs.items():
self.client.cookies['lang'] = lang
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, msg)

def test_one_language(self):
self.assertTrue(self.client.login(username='test_user'))
msg = 'Welcome to OIOIOI!'
WelcomePageMessage.objects.create(language='en', content=msg)

self.client.cookies['lang'] = 'en'
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, msg)

self.client.cookies['lang'] = 'pl'
response = self.client.get(reverse('welcome_page'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, msg)
11 changes: 11 additions & 0 deletions oioioi/welcomepage/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import re_path

from oioioi.welcomepage import views

app_name = 'welcomepage'

noncontest_patterns = [
re_path(r'^welcome/$', views.welcome_page_view, name='welcome_page'),
re_path(r'^edit_welcome_page/$', views.edit_welcome_page_view, name='edit_welcome_page'),
re_path(r'^delete_welcome_page/$', views.delete_welcome_page_view, name='delete_welcome_page'),
]
7 changes: 7 additions & 0 deletions oioioi/welcomepage/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from oioioi.base.permissions import make_request_condition
from oioioi.welcomepage.models import WelcomePageMessage


@make_request_condition
def any_welcome_messages(request):
return WelcomePageMessage.objects.exists()
77 changes: 77 additions & 0 deletions oioioi/welcomepage/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from django.urls import reverse
from django.conf import settings
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import get_language_from_request

from oioioi.base.admin import system_admin_menu_registry
from oioioi.base.main_page import register_main_page_view
from oioioi.base.permissions import enforce_condition, is_superuser
from oioioi.welcomepage.forms import WelcomePageMessageFormset
from oioioi.welcomepage.models import WelcomePageMessage
from oioioi.welcomepage.utils import any_welcome_messages


@register_main_page_view(order=110, condition=any_welcome_messages)
@enforce_condition(any_welcome_messages)
def welcome_page_view(request):
current_language = get_language_from_request(request)
try:
welcome_page_msg = WelcomePageMessage.objects.get(language=current_language)
except WelcomePageMessage.DoesNotExist:
welcome_page_msg = WelcomePageMessage.objects.first()
return TemplateResponse(
request,
'welcomepage/welcome-page.html',
{
'title': _('Welcome to %(site_name)s') % {'site_name': settings.SITE_NAME},
'welcome_page_msg': welcome_page_msg,
'show_edit_button': is_superuser(request),
},
)


system_admin_menu_registry.register(
'welcome_page',
_("Edit welcome page"),
lambda request: reverse('edit_welcome_page'),
order=80,
)
@enforce_condition(is_superuser)
def edit_welcome_page_view(request):
if request.method == 'POST':
formset = WelcomePageMessageFormset(request.POST)
if formset.is_valid():
instances = formset.save(commit=False)
for instance in instances:
instance.save()
for instance in formset.deleted_objects:
instance.delete()
return redirect('welcome_page')
else:
current_language = get_language_from_request(request)
instances = WelcomePageMessage.objects.all()
languages = [lang_short for lang_short, _ in settings.LANGUAGES]
for instance in instances:
languages.remove(instance.language)
formset = WelcomePageMessageFormset(
initial=[
{'language': lang, 'DELETE': lang != current_language}
for lang in languages
],
queryset=instances,
)
return TemplateResponse(
request,
'welcomepage/welcome-page-edit.html',
{
'formset': formset
},
)


@enforce_condition(is_superuser)
def delete_welcome_page_view(request):
WelcomePageMessage.objects.all().delete()
return redirect(reverse('index'))

0 comments on commit 0139795

Please sign in to comment.