Jonas Havelka
11 months ago
51 changed files with 723 additions and 112 deletions
@ -1,25 +0,0 @@ |
|||
.. Není odkázaná z menu, je to záměr |
|||
|
|||
Tabulka prerekvizit v různých distribucích |
|||
========= |
|||
|
|||
.. admonition:: Metodika |
|||
|
|||
Na čistém repozitáři (``git clean -fxd``) a čistém systému spouštíme |
|||
``make/init_local``. Když to spadne, tak do tabulky zapíšeme, co jsme |
|||
přiinstalovali. Protože větev ``makefiles`` aktuálně není mergenutá do |
|||
masteru, nefunguje synchronizace flatpages (a stejně nemáme SSH klíč), takže |
|||
tam ``make/init_local`` sestřelíme a vyzkoušíme, že ``make/test`` spustí |
|||
testy. |
|||
|
|||
.. Grafické tabulky (grid-tables, simple-tables) jsou strašný porod vyrábět, dlabu na to a cpu to do CSV… |
|||
|
|||
.. csv-table:: Prerekvizity v jednotlivých distribucích |
|||
:header: Distribuce / OS, Repozitář s Py3.9, venv, py knihovny, PostgreSQL knihovna, poznámky |
|||
|
|||
Ubuntu 22.10, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "Je potřeba zapnout zdroj ``universe`` a nainstalovat kompilátor C (``gcc``)?" |
|||
Linux Mint 21, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "" |
|||
Archlinux 2022.11.01, AUR, vestavěný, vestavěné, ``postgresql-libs``, "Je potřeba céčkový kompilátor (``gcc``)" |
|||
openSUSE Leap 15.4, oficiální (``python39``), předinstalovaný?, ``python39-devel``, ??FIXME!!, "Výchozí verze pythonu je 3.6 a ta je moc stará, potřeba instalovat ``gcc``. Nevím jak sehnat pg_config." |
|||
Debian 11, "oficiální, výchozí", ??, ??, ??, "Určitě to tam rozběhat jde, protože Gimli. Nejspíš bude relativně podobné Ubuntu." |
|||
|
@ -0,0 +1,97 @@ |
|||
Závislosti webu |
|||
@@@@@@@@@@@@@@@ |
|||
|
|||
Web ke svému běhu potřebuje různé další programy. Tahle stránka se snaží je pokrýt. |
|||
|
|||
Stránka je koncipována jako odrážkový seznam balíčků pro Ubuntu s případnými |
|||
komentáři, na konci stránky jsou uvedena :ref:`jména balíčků <Alternativní jména |
|||
balíčků>` v různých dalších distribucích. (Seznam mj. cílí na lokální |
|||
rozchození, proto popisuji Ubuntu a ne Debian. I tak se ale snažíme popsat web |
|||
v úplnosti.) |
|||
|
|||
.. I use Arch, btw. |
|||
|
|||
|
|||
Základ webu |
|||
=========== |
|||
|
|||
- ``python3`` – Ideálně Python 3.9, jenž je na Gimlim |
|||
- ``python3-pip`` pro instalaci dalších Pythoních balíčků podle ``requirements.txt`` |
|||
- ``python3-venv`` |
|||
- ``gcc`` – kompilace Pythoních knihoven ze zdrojových distribucí (sdist), možná (neotestováno) jde jako alternativu použít ``python3-wheel`` a stahovat bdists |
|||
- ``python3-dev`` – taktéž |
|||
- ``libpq-dev`` do třetice… |
|||
- ``ghostscript`` TODO konverze PDF v korekturovátku |
|||
- ``pdflatex`` FIXME! generování obálek a stvrzenek |
|||
- ``git`` – používán :ref:`Make skripty` |
|||
- ``locales`` pro české formáty |
|||
|
|||
Nasazení na produkci / testweb |
|||
============================== |
|||
|
|||
(nejsou nutně potřeba k provozu lokální instance) |
|||
|
|||
- ``rsync`` |
|||
- ``pg_utils`` FIXME |
|||
- ``htpasswd`` FIXME – aby testweb nepoužívali náhodní kolemjdoucí |
|||
- ``postgresql-server`` TODO |
|||
- ``acl`` pro nastavování práv přes ``setfacl`` |
|||
|
|||
Pro testweb je potřeba i všechno pro :ref:`dokumentaci <Dokumentace>`, vizte níž. |
|||
|
|||
Předpokládá se nasazení v uWSGI pod Nginxem a služba běžící pod systemd, nicméně to už je spíš záležitost infrastruktury a ne specifikum mamwebu. |
|||
|
|||
Dokumentace |
|||
=========== |
|||
|
|||
- ``make`` pro zbuildění |
|||
- Pythoní balíčky podle příslušné části ``requirements.txt`` |
|||
|
|||
Vývojové nástroje |
|||
================= |
|||
|
|||
(Nejsou nezbytně nutné, ale předpokládáme jejich užitečnost. Mohou se hodit i na produkci.) |
|||
|
|||
- ``psql`` TODO pro manuální dotazy do PostgreSQL |
|||
- ``sqlite3`` TODO totéž pro SQLite3 |
|||
- ``ssh`` |
|||
- ``graphviz`` pro vygenerování schématu |
|||
- ``rsync`` |
|||
- ``ipython3`` – hezčí interaktivní shell (stačí z ``requirements.txt``) |
|||
|
|||
Potenciální usnadnění života |
|||
============================ |
|||
|
|||
(Úplně zbytečné, ale sdílíme pozitivní zkušenosti :-)) |
|||
|
|||
- ``tea`` – CLI klient pro Giteu, aby člověk nepotřeboval otevírat web pro založení PR |
|||
|
|||
|
|||
Alternativní jména balíčků |
|||
========================== |
|||
|
|||
Různé distribuce balí SW různě, takže to, co je v jedné distribuci jeden |
|||
balíček může být v jiné rozděleno do víc. Pro usnadnění nasazení je tady |
|||
přehled známých alternativních jmen. |
|||
|
|||
TODO: tabulka není úplná. Pokud na něco narazíte, tak ji prosím doplňte. |
|||
|
|||
.. admonition:: Jak se pozná, že web funguje, pro účely tabulky? |
|||
|
|||
Na čistém repozitáři (``git clean -fxd``) a čistém systému spouštíme |
|||
``make/init_local``. Když to spadne, tak do tabulky zapíšeme, co jsme |
|||
přiinstalovali. Protože nefunguje synchronizace flatpages (nemáme SSH klíč), |
|||
``make/init_local`` sestřelíme při pokusu o synchronizaci a vyzkoušíme, že |
|||
``make/test`` spustí testy. |
|||
|
|||
.. Grafické tabulky (grid-tables, simple-tables) jsou strašný porod vyrábět, dlabu na to a cpu to do CSV… |
|||
|
|||
.. csv-table:: Prerekvizity v jednotlivých distribucích |
|||
:header: Distribuce / OS, Repozitář s Py3.9, venv, py knihovny, PostgreSQL knihovna, poznámky |
|||
|
|||
Ubuntu 22.10, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "Je potřeba zapnout zdroj ``universe`` a nainstalovat kompilátor C (``gcc``)?" |
|||
Linux Mint 21, ??, ``python3-venv``, ``python3-dev``, ``libpq-dev``, "" |
|||
Archlinux 2022.11.01, AUR, vestavěný, vestavěné, ``postgresql-libs``, "Je potřeba céčkový kompilátor (``gcc``)" |
|||
openSUSE Leap 15.4, oficiální (``python39``), předinstalovaný?, ``python39-devel``, ??FIXME!!, "Výchozí verze pythonu je 3.6 a ta je moc stará, potřeba instalovat ``gcc``. Nevím jak sehnat pg_config." |
|||
Debian 11, "oficiální, výchozí", ??, ??, ??, "Určitě to tam rozběhat jde, protože Gimli. Nejspíš bude relativně podobné Ubuntu." |
|||
|
@ -0,0 +1,15 @@ |
|||
from django import template |
|||
register = template.Library() |
|||
|
|||
from functools import cache |
|||
import seminar.models as m |
|||
|
|||
@register.filter |
|||
@cache |
|||
def barva_reseni(r: m.Reseni): |
|||
"""Vrátí nějakou barvu pro daný problém, ve tvaru '#RRGGBB' |
|||
|
|||
Efektivně hešujeme do barev.""" |
|||
|
|||
#TODO: ne všechny barvy jsou dobře rozlišitelné a vidět… |
|||
return f'#{hash(str(r.id)) & 0xffffff:06x}' |
@ -0,0 +1,63 @@ |
|||
from django.test import TestCase, RequestFactory |
|||
|
|||
from django.contrib.auth.models import User, Group |
|||
from django.contrib.admin.sites import AdminSite |
|||
from personalni.admin import OsobaAdmin |
|||
# Tohle bude peklo, až jednou ty modely fakt rozstřelíme… Možná vyrobit various.all_models, které půjdou importovat jako m? :-) |
|||
import seminar.models as m |
|||
|
|||
import logging |
|||
logger = logging.getLogger(__name__) |
|||
|
|||
class DelaniOrguTest(TestCase): |
|||
def setUp(self): |
|||
# Admin musí mít instanci |
|||
# Ref: https://www.argpar.se/posts/programming/testing-django-admin/ |
|||
adm_site = AdminSite() |
|||
self.admin = OsobaAdmin(m.Osoba, adm_site) |
|||
|
|||
from django.contrib.messages.storage.cookie import CookieStorage |
|||
self.request = RequestFactory().get('/admin') |
|||
self.request._messages = CookieStorage(self.request) |
|||
|
|||
self.org_group = Group.objects.get(name='org') |
|||
|
|||
novy_user = User.objects.create(username='osoba') |
|||
self.nova_osoba = m.Osoba.objects.create( |
|||
jmeno='Milada', |
|||
prijmeni='Von Kolej', |
|||
user = novy_user, |
|||
# Snad nic dalšího nepotřebujeme, kdyžtak se doplní… |
|||
) |
|||
stary_user = User.objects.create(username='stary_user') |
|||
stara_osoba = m.Osoba.objects.create(user=stary_user) |
|||
self.stary_org = m.Organizator.objects.create(osoba=stara_osoba) |
|||
|
|||
def test_pridani_orga(self): |
|||
# Nejdřív to není org… |
|||
self.assertFalse(m.Organizator.objects.filter(osoba=self.nova_osoba).exists()) |
|||
self.assertNotIn(self.org_group, self.nova_osoba.user.groups.all()) |
|||
self.assertFalse(self.nova_osoba.user.has_perm('auth.org')) |
|||
self.assertFalse(self.nova_osoba.user.is_staff) |
|||
|
|||
# Pak orga uděláme… |
|||
qs = m.Osoba.objects.filter(id=self.nova_osoba.id) |
|||
self.admin.udelej_orgem(self.request, qs) |
|||
|
|||
# A pak už to org má být. |
|||
self.nova_osoba.refresh_from_db() |
|||
self.assertTrue(self.nova_osoba.user.is_staff) |
|||
# FIXME: V db nejsou práva. Nový org je sice ve skupině "org", ale ta nemá právo "auth.org" |
|||
# Očekávané řešení: dodat fixture, která to přidá. |
|||
#self.assertTrue(self.nova_osoba.user.has_perm('auth.org')) |
|||
self.assertIn(self.org_group, self.nova_osoba.user.groups.all()) |
|||
self.assertTrue(m.Organizator.objects.filter(osoba=self.nova_osoba).exists()) |
|||
novy_org = m.Organizator.objects.get(osoba=self.nova_osoba) |
|||
self.assertIsNotNone(novy_org.organizuje_od) |
|||
|
|||
def test_pridani_stareho_orga(self): |
|||
self.admin.udelej_orgem(self.request, m.Osoba.objects.filter(id=self.stary_org.osoba.id)) # Ugly |
|||
# Když to spadne, tak jsem se to dozvěděl, takže už nepotřebuju nic kontrolovat. |
|||
# Jestli to funguje správně má řešit jiný test. |
|||
|
|||
|
@ -0,0 +1,40 @@ |
|||
# Generated by Django 4.2.7 on 2023-11-20 21:02 |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('contenttypes', '0002_remove_content_type_name'), |
|||
('seminar', '0113_resitel_zasilat_cislo_papirove'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='problem', |
|||
name='autor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autor_problemu_%(class)s', to='seminar.organizator', verbose_name='autor problému'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='problem', |
|||
name='garant', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='garant_problemu_%(class)s', to='seminar.organizator', verbose_name='garant zadaného problému'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='problem', |
|||
name='opravovatele', |
|||
field=models.ManyToManyField(blank=True, related_name='opravovatele_%(class)s', to='seminar.organizator', verbose_name='opravovatelé'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='problem', |
|||
name='polymorphic_ctype', |
|||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='treenode', |
|||
name='polymorphic_ctype', |
|||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'), |
|||
), |
|||
] |
@ -0,0 +1,8 @@ |
|||
from django.contrib import admin |
|||
|
|||
from .models import OdpovedUcastnika, SpravnaOdpoved |
|||
|
|||
# Register your models here. |
|||
|
|||
admin.site.register(OdpovedUcastnika) |
|||
admin.site.register(SpravnaOdpoved) |
@ -0,0 +1,5 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class SifrovackaConfig(AppConfig): |
|||
name = 'sifrovacka' |
@ -0,0 +1,18 @@ |
|||
from django.core.exceptions import ValidationError |
|||
from django.forms import ModelForm, Textarea |
|||
from .models import OdpovedUcastnika, SpravnaOdpoved |
|||
|
|||
|
|||
class SifrovackaForm(ModelForm): |
|||
class Meta: |
|||
model = OdpovedUcastnika |
|||
fields = ["sifra", "odpoved", ] |
|||
widgets = { |
|||
"odpoved": Textarea(attrs={'rows': 1, 'cols': 30}), |
|||
} |
|||
|
|||
def clean_sifra(self): |
|||
sifra = self.cleaned_data.get('sifra') |
|||
if SpravnaOdpoved.objects.filter(sifra=sifra).count() == 0: |
|||
raise ValidationError("Tohle číslo šifry v databázi nemáme. Zkontrolujte si ho prosím.") |
|||
return sifra |
@ -0,0 +1,34 @@ |
|||
# Generated by Django 3.2.22 on 2023-10-14 09:20 |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
initial = True |
|||
|
|||
dependencies = [ |
|||
('seminar', '0113_resitel_zasilat_cislo_papirove'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='SpravnaOdpoved', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('odpoved', models.TextField()), |
|||
('sifra', models.IntegerField()), |
|||
('skryty_text', models.TextField()), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name='OdpovedUcastnika', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('odpoved', models.TextField(verbose_name='Tajenka')), |
|||
('sifra', models.IntegerField(verbose_name='Číslo šifry')), |
|||
('resitel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='seminar.resitel')), |
|||
], |
|||
), |
|||
] |
@ -0,0 +1,28 @@ |
|||
# Generated by Django 3.2.22 on 2023-10-15 17:44 |
|||
|
|||
from django.db import migrations, models |
|||
import django.utils.timezone |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('sifrovacka', '0001_initial'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='odpoveducastnika', |
|||
options={'ordering': ['-timestamp']}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='odpoveducastnika', |
|||
name='timestamp', |
|||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Timestamp'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='odpoveducastnika', |
|||
name='odpoved', |
|||
field=models.TextField(verbose_name='Tajenka bez diakritiky'), |
|||
), |
|||
] |
@ -0,0 +1,18 @@ |
|||
# Generated by Django 3.2.22 on 2023-10-16 17:51 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('sifrovacka', '0002_auto_20231015_1944'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='odpoveducastnika', |
|||
name='uspech', |
|||
field=models.BooleanField(default=False, verbose_name='Úspěch'), |
|||
), |
|||
] |
@ -0,0 +1,27 @@ |
|||
from django.db import models |
|||
from django.utils import timezone |
|||
|
|||
from seminar.models.personalni import Resitel |
|||
|
|||
|
|||
# Create your models here. |
|||
|
|||
|
|||
class OdpovedUcastnika(models.Model): |
|||
class Meta: |
|||
ordering = ["-timestamp"] |
|||
|
|||
resitel = models.ForeignKey(Resitel, blank=False, null=False, on_delete=models.CASCADE) |
|||
odpoved = models.TextField("Tajenka bez diakritiky", blank=False, null=False,) |
|||
sifra = models.IntegerField("Číslo šifry", blank=False, null=False,) |
|||
timestamp = models.DateTimeField("Timestamp", blank=False, null=False, default=timezone.now) |
|||
uspech = models.BooleanField("Úspěch", blank=False, null=False, default=False) |
|||
|
|||
|
|||
class SpravnaOdpoved(models.Model): |
|||
odpoved = models.TextField(blank=False, null=False,) |
|||
sifra = models.IntegerField(blank=False, null=False,) |
|||
skryty_text = models.TextField(blank=False, null=False,) |
|||
|
|||
def __str__(self): |
|||
return f"{self.sifra}: {self.odpoved}" |
@ -0,0 +1,25 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% block content %} |
|||
|
|||
<h1>{% block nadpis1a %}Šifrovačka odpovědi{% endblock nadpis1a %}</h1> |
|||
|
|||
<table class="dosla_reseni"> |
|||
<tr> |
|||
<th>Timestamp</th> |
|||
<th>Řešitel</th> |
|||
<th>Šifra</th> |
|||
<th>Odpověď</th> |
|||
</tr> |
|||
|
|||
{% for u in object_list %} |
|||
<tr> |
|||
<td>{{ u.timestamp }}</td> |
|||
<td>{{ u.resitel }}</td> |
|||
<td>{{ u.sifra }}</td> |
|||
<td style="color: {% if u.uspech %}green{% else %}red{% endif %};">{{ u.odpoved }}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
|
|||
{% endblock content %} |
@ -0,0 +1,46 @@ |
|||
{% extends "base.html" %} |
|||
|
|||
{% block content %} |
|||
|
|||
<br> |
|||
|
|||
<h1>{% block nadpis1a %}M&Mí šifrovačka{% endblock nadpis1a %}</h1> |
|||
|
|||
<br> |
|||
|
|||
<h2>Zadat tajenku šifry:</h2> |
|||
|
|||
<form action="{% url 'sifrovacka' %}" method="post"> |
|||
<table class="form"> |
|||
{{form.non_field_errors}} |
|||
{% for field in form %} |
|||
<tr> |
|||
<td> |
|||
<label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}"> |
|||
{{ field.label }} |
|||
</label> |
|||
|
|||
</td> |
|||
|
|||
<td {% if field.help_text %} class="field-with-comment"{% endif %}> |
|||
{{ field }} |
|||
<span class="field-comment">{{ field.help_text|safe }}</span> |
|||
</td> |
|||
|
|||
</tr> |
|||
|
|||
|
|||
{% if field.errors %} |
|||
<tr> |
|||
<td colspan="2"><span class="field-error">{{ field.errors }}</span></td> |
|||
</tr> |
|||
{% endif %} |
|||
{% endfor %} |
|||
</table> |
|||
|
|||
{% csrf_token %} |
|||
|
|||
<input type="submit" value="Tak pravím!"> |
|||
</form> |
|||
|
|||
{% endblock content %} |
@ -0,0 +1,17 @@ |
|||
from django.urls import path |
|||
|
|||
from seminar.utils import org_required, resitel_or_org_required |
|||
from .views import SifrovackaView, SifrovackaListView |
|||
|
|||
urlpatterns = [ |
|||
path( |
|||
'', |
|||
resitel_or_org_required(SifrovackaView.as_view()), |
|||
name='sifrovacka' |
|||
), |
|||
path( |
|||
'odpovedi/', |
|||
org_required(SifrovackaListView.as_view()), |
|||
name='sifrovacka_odpovedi' |
|||
), |
|||
] |
@ -0,0 +1,33 @@ |
|||
from django.urls import reverse |
|||
from django.views.generic import FormView, ListView |
|||
|
|||
from seminar.views import formularOKView |
|||
from .forms import SifrovackaForm |
|||
from .models import OdpovedUcastnika, SpravnaOdpoved |
|||
from seminar.models.personalni import Resitel |
|||
|
|||
|
|||
# Create your views here. |
|||
|
|||
class SifrovackaView(FormView): |
|||
template_name = 'sifrovacka/sifrovacka.html' |
|||
form_class = SifrovackaForm |
|||
|
|||
def form_valid(self, form): |
|||
instance = form.save(commit=False) |
|||
resitel = Resitel.objects.get(osoba__user=self.request.user) |
|||
instance.resitel = resitel |
|||
instance.save() |
|||
sifra = SpravnaOdpoved.objects.filter(sifra=instance.sifra, odpoved__iexact=instance.odpoved.strip()).first() |
|||
if sifra is None: |
|||
return formularOKView(self.request, f'<h1>Bohužel vám hvězdy nebyly nakloněny. Rozumějte <i>máte to blbě</i>.</h1> <p><a href="{reverse("sifrovacka")}">Zkusit znovu.</a></p><br><br><br>') |
|||
|
|||
instance.uspech = True |
|||
instance.save() |
|||
|
|||
return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Odevzdat další.</a></p><br><br><br>') |
|||
|
|||
|
|||
class SifrovackaListView(ListView): |
|||
template_name = 'sifrovacka/odpovedi_list.html' |
|||
model = OdpovedUcastnika |
After Width: | Height: | Size: 32 KiB |
@ -0,0 +1,19 @@ |
|||
{#{% extends "error_base.html" %} Z toho nedědíme, protože se nemá přecházet na titulní stránku. #} |
|||
{% extends "base.html" %} |
|||
|
|||
{% load static %} |
|||
|
|||
{% block content %} |
|||
|
|||
<h2>{% block nadpis1a %}O-jo-jo-jo-joj{% endblock nadpis1a %}</h2> |
|||
|
|||
<p> |
|||
Problém se sušenkami či něčím podobným. Zkuste {% if url %}to prosím znovu: <a href="{{ url }}">{{ url }}</a>. Případně můžete {% endif %}přejít na <a href="/">titulní stránku</a>. |
|||
</p> |
|||
|
|||
<p>Pokud problém přetrvává obraťte se na nás přes e-mail: <a href="mailto:mam@matfyz.cz">mailto:mam@matfyz.cz</a> a pošlete nám následující popis chyby: <code>{{ reason }}</code></p> |
|||
|
|||
<img src="{% static 'various/img/zere_kostku.svg' %}"> |
|||
|
|||
|
|||
{% endblock %} |
@ -1,3 +1,13 @@ |
|||
from django.http import HttpResponseForbidden |
|||
from django.shortcuts import render |
|||
|
|||
# Create your views here. |
|||
|
|||
|
|||
def csrf_error(request, reason=""): |
|||
""" Jednoduchý „template view“ (třída to být nemůže) pro CSRF chyby """ |
|||
return render( |
|||
request, 'various/403_csrf.html', |
|||
{"url": request.META.get("HTTP_REFERER", None), "reason": reason}, |
|||
status=HttpResponseForbidden.status_code, |
|||
) |
|||
|
Loading…
Reference in new issue