diff --git a/galerie/admin.py b/galerie/admin.py index f68f1f3c..1eacbf60 100644 --- a/galerie/admin.py +++ b/galerie/admin.py @@ -1,4 +1,4 @@ -from galerie.models import Obrazek, Galerie +from galerie.models import Obrazek, Galerie, VZDY, ORG, NIKDY, UCASTNIK from django.contrib import admin from django.http import HttpResponseRedirect from django import forms @@ -8,8 +8,9 @@ from django.db import models def zverejnit_fotogalerii(modeladmin, request, queryset): '''zverejni vybranou fotogalerii i jeji vsechny podgalerie''' + queryset = queryset.filter(zobrazit=ORG) for galerie in queryset: - galerie.zobrazit = 0 + galerie.zobrazit = VZDY galerie.save() zverejnit_fotogalerii(modeladmin, request, Galerie.objects.filter(galerie_up = galerie)) @@ -18,8 +19,9 @@ def zverejnit_fotogalerii(modeladmin, request, queryset): def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset): '''zneverjni vybranou fotogalerii i jeji vsechny podgalerie''' + queryset = queryset.filter(zobrazit=VZDY) for galerie in queryset: - galerie.zobrazit = 1 + galerie.zobrazit = ORG galerie.save() prepnout_fotogalerii_do_org_rezimu(modeladmin, request, Galerie.objects.filter(galerie_up = galerie)) diff --git a/galerie/migrations/0014_alter_galerie_zobrazit.py b/galerie/migrations/0014_alter_galerie_zobrazit.py new file mode 100644 index 00000000..c53393f3 --- /dev/null +++ b/galerie/migrations/0014_alter_galerie_zobrazit.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.20 on 2025-04-23 18:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('galerie', '0013_post_split_soustredeni'), + ] + + operations = [ + migrations.AlterField( + model_name='galerie', + name='zobrazit', + field=models.IntegerField(choices=[(0, 'Vždy'), (1, 'Organizátorům'), (3, 'Účastníkům a orgům'), (2, 'Nikdy')], default=1, verbose_name='Zobrazit?'), + ), + ] diff --git a/galerie/migrations/0015_alter_galerie_galerie_up.py b/galerie/migrations/0015_alter_galerie_galerie_up.py new file mode 100644 index 00000000..2033c9af --- /dev/null +++ b/galerie/migrations/0015_alter_galerie_galerie_up.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.16 on 2025-04-30 18:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('galerie', '0014_alter_galerie_zobrazit'), + ] + + operations = [ + migrations.AlterField( + model_name='galerie', + name='galerie_up', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='galerie.galerie'), + ), + ] diff --git a/galerie/migrations/0016_alter_obrazek_galerie.py b/galerie/migrations/0016_alter_obrazek_galerie.py new file mode 100644 index 00000000..8c8e57af --- /dev/null +++ b/galerie/migrations/0016_alter_obrazek_galerie.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.16 on 2025-04-30 19:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('galerie', '0015_alter_galerie_galerie_up'), + ] + + operations = [ + migrations.AlterField( + model_name='obrazek', + name='galerie', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='galerie.galerie'), + ), + ] diff --git a/galerie/models.py b/galerie/models.py index f9efb2bc..12c0549d 100644 --- a/galerie/models.py +++ b/galerie/models.py @@ -10,9 +10,11 @@ from soustredeni.models import Soustredeni VZDY=0 ORG=1 NIKDY=2 +UCASTNIK=3 VIDITELNOST = ( (VZDY, 'Vždy'), (ORG, 'Organizátorům'), + (UCASTNIK, 'Účastníkům a orgům'), (NIKDY, 'Nikdy'), ) @@ -59,7 +61,7 @@ class Obrazek(models.Model): # ~~Rádoby~~ vtipný popisek od orgů popis = models.TextField('Popis', blank=True, null=True) datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True) - galerie = models.ForeignKey('Galerie', blank=True, null=True, on_delete=models.SET_NULL) + galerie = models.ForeignKey('Galerie', blank=True, null=True, on_delete=models.CASCADE) # Primární klíč k řazení pro overridování řazení podle názvu poradi = models.IntegerField('Pořadí', blank=True, null=True) @@ -98,7 +100,7 @@ class Galerie(models.Model): titulni_obrazek = models.ForeignKey(Obrazek, blank = True, null = True, related_name = "+", on_delete = models.SET_NULL) zobrazit = models.IntegerField('Zobrazit?', default = ORG, choices = VIDITELNOST) galerie_up = models.ForeignKey('Galerie', blank = True, null = True, - on_delete=models.SET_NULL) + on_delete=models.PROTECT) soustredeni = models.ForeignKey(Soustredeni, blank = True, null = True, on_delete=models.PROTECT) poradi = models.IntegerField('Pořadí', blank = True, null = False, default = 0) @@ -108,25 +110,3 @@ class Galerie(models.Model): class Meta: verbose_name = 'Galerie' verbose_name_plural = 'Galerie' - - #def link_na_preview(self): - #"""Odkaz na galerii, používá se v admin rozhranní. """ - #return 'Preview' % self.id - #link_na_preview.allow_tags = True - #link_na_preview.short_description = 'Zobrazit galerii' -# - #def je_publikovano(self): - #"""Vraci True, pokud je tato galerie publikovana. """ - #if self.zobrazit == VZDY: - #return True - #if self.zobrazit == PODLE_CLANKU: - #for clanek in self.clanek_set.all(): - #if clanek.je_publikovano(): - #return True - #return False -# - #@staticmethod - #def publikovane_galerie(): - #"""Vraci galerie, ktere uz maji byt publikovane.""" - #clanky = Blog.models.Clanek.publikovane_clanky() - #return Galerie.objects.filter(Q(zobrazit=VZDY) | (Q(clanek__in=clanky) & Q(zobrazit=PODLE_CLANKU))).distinct() diff --git a/galerie/static/css/galerie.css b/galerie/static/css/galerie.css index 46df8552..46e02367 100644 --- a/galerie/static/css/galerie.css +++ b/galerie/static/css/galerie.css @@ -136,6 +136,11 @@ top: 160px; } +.podgalerie_nahled.mam-org-only, .podgalerie_nahled.mam-resitel-only { + margin: 10px; + padding: 0; +} + /* Odkazy na předchozí a následující podgalerii */ .galerie_predchozi_nasledujici { diff --git a/galerie/templates/galerie/Galerie.html b/galerie/templates/galerie/Galerie.html index 61453531..2aeb6089 100644 --- a/galerie/templates/galerie/Galerie.html +++ b/galerie/templates/galerie/Galerie.html @@ -47,6 +47,7 @@ {% block content %} +

{% for g in cesta %} @@ -85,7 +86,7 @@ {# Popisek fotky #}
- {% if preview %} + {% if upravy_popisku %}
{% csrf_token %} @@ -135,4 +136,6 @@ {% endif %} + + {% endblock %} diff --git a/galerie/templates/galerie/GalerieNahled.html b/galerie/templates/galerie/GalerieNahled.html index 90a1d403..4592a69f 100644 --- a/galerie/templates/galerie/GalerieNahled.html +++ b/galerie/templates/galerie/GalerieNahled.html @@ -8,8 +8,11 @@ Galerie {{galerie.nazev}} {% block content %} - {% if galerie.zobrazit > 0 %} + {# FIXME: použít konstanty… #} + {% if galerie.zobrazit == 1 or galerie.zobrazit == 2 %}
+ {% elif galerie.zobrazit == 3 %} +
{% endif %}

@@ -47,34 +50,40 @@ Galerie {{galerie.nazev}} {% if podgalerie %} {% with 22 as max_delka_nazvu %}
- {% for galerie in podgalerie %} - max_delka_nazvu %} - title="{{ galerie.nazev }}" - {% endif %} - class="podgalerie_nahled"> - {% if galerie.titulni_obrazek %} - {% zmenseny_nahled galerie.titulni_obrazek.jako_bazmek %} + {% for pgalerie in podgalerie %} + max_delka_nazvu %} + title="{{ pgalerie.nazev }}" {% endif %} - {% endwith %} {% endif %} {% endif %} - {% if user.je_org and galerie.zobrazit > 0 %} + {% if user.je_org %}
- Vytvořit novou podgalerii + {% if galerie.zobrazit == 1 or galerie.zobrazit == 2 %} + Vytvořit novou podgalerii, upravit galerii v adminu + {% else %} + Jestli chceš změnit pořadí podgalerií nebo přidat novou, nastav zobrazení jen pro orgy v adminu. + {% endif %}
{% endif %} @@ -118,8 +127,10 @@ Galerie {{galerie.nazev}} {% endif %} {% endif %} - {% if galerie.zobrazit > 0 %} + {% if galerie.zobrazit == 1 or galerie.zobrazit == 2 %}
{# mam-org-only #} + {% elif galerie.zobrazit == 2 %} +

{# mam-resitel-only #} {% endif %} {% endblock content %} diff --git a/galerie/templates/galerie/base.html b/galerie/templates/galerie/base.html index 032f16b2..0c449914 100644 --- a/galerie/templates/galerie/base.html +++ b/galerie/templates/galerie/base.html @@ -2,5 +2,5 @@ {% load static %} {% block custom_css %} - + {% endblock %} diff --git a/galerie/utils.py b/galerie/utils.py new file mode 100644 index 00000000..352e197b --- /dev/null +++ b/galerie/utils.py @@ -0,0 +1,6 @@ +from galerie.models import Galerie + +def top_galerie(g: Galerie) -> Galerie: + while g.galerie_up is not None: + g = g.galerie_up + return g diff --git a/galerie/views.py b/galerie/views.py index cf9ade8b..fa2a15e9 100644 --- a/galerie/views.py +++ b/galerie/views.py @@ -1,22 +1,41 @@ import random -from django.http import HttpResponse, Http404 +from django.http import HttpResponse, Http404, HttpRequest from django.shortcuts import render, HttpResponseRedirect, get_object_or_404 from django.template import RequestContext from datetime import datetime -from galerie.models import Obrazek, Galerie +from galerie.utils import top_galerie +from personalni.utils import resitel_uzivatele + +from galerie.models import Obrazek, Galerie, VZDY, ORG, UCASTNIK, NIKDY from soustredeni.models import Soustredeni from galerie.forms import KomentarForm, NewGalerieForm -def zobrazit(galerie, request): - preview = False - if galerie.zobrazit >= 1: - if request.user.je_org: - preview = True; +import logging +logger = logging.getLogger(__name__) + +def galerie_ke_zobrazeni(soustredeni: Soustredeni | None, request: HttpRequest) -> tuple[int]: + if request.user.is_superuser: return (VZDY, ORG, UCASTNIK, NIKDY) + if request.user.je_org: return (VZDY, ORG, UCASTNIK) + if request.user.is_anonymous: return (VZDY,) + if soustredeni is None: return (VZDY,) + if (resitel := resitel_uzivatele(request.user)) is not None: + if resitel.soustredeni_set.contains(soustredeni): + return (VZDY, UCASTNIK) else: - raise Http404 - return preview + return (VZDY,) + logger.warning("Nepodařilo se zjistit, jaké galerie se mají zobrazit!") + return (VZDY,) + + +def zobrazit(galerie: Galerie, request: HttpRequest) -> bool: + soustredeni = top_galerie(galerie).soustredeni + return galerie.zobrazit in galerie_ke_zobrazeni(soustredeni, request) + +def dovolit_upravy_popisku(galerie: Galerie, request: HttpRequest) -> bool: + # FIXME: Dočasné: úpravy jen když je to v org-only stavu. (Odpovídá předchozímu chování) + return request.user.je_org and galerie.zobrazit in (ORG, NIKDY) def cesta_od_korene(g): @@ -31,19 +50,19 @@ def cesta_od_korene(g): def nahled(request, pk, soustredeni): """Zobrazeni nahledu vsech fotek ve skupine.""" galerie = get_object_or_404(Galerie, pk=pk) + soustredeni = top_galerie(galerie).soustredeni + # FIXME: přepsat model a použít přímo dolů… podgalerie = Galerie.objects.filter(galerie_up = galerie).order_by('poradi') - if not request.user.je_org: - podgalerie = podgalerie.filter(zobrazit__lt=1) + podgalerie = podgalerie.filter(zobrazit__in=galerie_ke_zobrazeni(soustredeni, request)) - obrazky = Obrazek.objects.filter(galerie = galerie).order_by('poradi', 'nazev') - preview = zobrazit(galerie, request) + obrazky = galerie.obrazek_set.all().order_by('poradi', 'nazev') + ma_se_zobrazit = zobrazit(galerie, request) + if not ma_se_zobrazit: raise Http404("Galerie sice existuje, ale my se tváříme, že ne :-D") sourozenci = [] if galerie.galerie_up: - sourozenci = galerie.galerie_up.galerie_set.all().order_by('poradi') - if not request.user.je_org: - sourozenci = sourozenci.filter(zobrazit__lt=1) + sourozenci = galerie.galerie_up.galerie_set.filter(zobrazit__in=galerie_ke_zobrazeni(soustredeni, request)).order_by('poradi') predchozi = None nasledujici = None @@ -62,7 +81,6 @@ def nahled(request, pk, soustredeni): {'galerie' : galerie, 'podgalerie' : podgalerie, 'obrazky' : obrazky, - 'preview' : preview, 'cesta': cesta, 'sourozenci': sourozenci, 'predchozi': predchozi, @@ -78,9 +96,41 @@ def detail(request, pk, fotka, soustredeni): NAHLEDU = 1 galerie = get_object_or_404(Galerie, pk=pk) - preview = zobrazit(galerie, request) + soustredeni = top_galerie(galerie).soustredeni + ma_se_zobrazit = zobrazit(galerie, request) + if not ma_se_zobrazit: raise Http404("Obrázek neukážu!") obrazek = get_object_or_404(Obrazek, pk=fotka) + + # Pořadí není povinné. FIXME: `nazev` je zavádějící… Ale tohle je kanonické pořadí obrázků v galerii… obrazky = galerie.obrazek_set.all().order_by('poradi', 'nazev') + obrazky = list(obrazky) + index_obrazku = obrazky.index(obrazek) + # Podle mě se nemůže stát, že by volání výš selhalo, kdyžtak shodí web. (původně to byl explicitně ošetřený stav dávající 404) + predchozi_obrazky = list(reversed(obrazky[:index_obrazku])) + nasledujici_obrazky = obrazky[index_obrazku+1:] + # Může jich být hodně… + predchozi_obrazky = predchozi_obrazky[:NAHLEDU] + nasledujici_obrazky = nasledujici_obrazky[:NAHLEDU] + # Předchozí obrázky chceme v normálním pořadí + predchozi_obrazky = list(reversed(predchozi_obrazky)) + + if galerie.galerie_up is not None: + sousedni_galerie = Galerie.objects.filter(galerie_up=galerie.galerie_up, zobrazit__in=galerie_ke_zobrazeni(soustredeni, request)).order_by('poradi') + sousedni_galerie = list(sousedni_galerie) + # Teoreticky se můžeme dívat na galerie.poradi, ale jednak už tenhle pattern stejně je výš a druhak je galerií málo, takže pomalost nevadí. + index_galerie = sousedni_galerie.index(galerie) + predchozi_galerie = sousedni_galerie[index_galerie-1] if index_galerie > 0 else None + nasledujici_galerie = sousedni_galerie[index_galerie+1] if index_galerie < len(sousedni_galerie) - 1 else None + else: + predchozi_galerie = None + nasledujici_galerie = None + + # Pokud je obrázků dost, tak další galerii nepotřebujeme + # (jo, mohli jsme si ušetřit práci, ale takhle je kód imho přehlednější a za pár ušetřených dotazů do DB to nestojí) + if len(predchozi_obrazky) >= NAHLEDU: + predchozi_galerie = None + if len(nasledujici_obrazky) >= NAHLEDU: + nasledujici_galerie = None # vytvoreni a obslouzeni formulare if request.method == 'POST': @@ -90,49 +140,6 @@ def detail(request, pk, fotka, soustredeni): obrazek.save() else: form = KomentarForm({'komentar': obrazek.popis}) - - # Poradi aktualniho obrazku v galerii/stitku. - for i in range(len(obrazky)): - if obrazky[i] == obrazek: - poradi = i - break - else: - # Obrazek neni v galerii/stitku. - raise Http404 - - - # Nacteni okolnich obrazku a galerii - # TODO vyjmout zjisteni predchozich a nasledujicich galerii - # a udelat z toho funkci, ktera se pouzije u nahledu - predchozi_galerie = None - nasledujici_galerie = None - obrazky_dalsi = obrazky[poradi+1:poradi+NAHLEDU+1] - if (poradi+1) > NAHLEDU: - obrazky_predchozi = obrazky[poradi-NAHLEDU:poradi] - else: - obrazky_predchozi = obrazky[0:poradi] - if galerie.poradi > 1: - predchozi_galerie = Galerie.objects.\ - filter(galerie_up=galerie.galerie_up).\ - filter(poradi=(galerie.poradi-1)) - if predchozi_galerie: - predchozi_galerie = predchozi_galerie[0] - else: - predchozi_galerie = None - if (poradi+1) == len(obrazky): # Tohle je poslední obrázek - if (galerie.poradi is not None - and galerie.galerie_up is not None): - nasledujici_galerie = Galerie.objects.\ - filter(galerie_up=galerie.galerie_up).\ - filter(poradi=(galerie.poradi+1)) - else: - nasledujici_galerie = None - if nasledujici_galerie: - nasledujici_galerie = nasledujici_galerie[0] - else: - nasledujici_galerie = None - - # Preskalovani obrazku do vybraneho prostoru. vyska = obrazek.obrazek_stredni.height @@ -151,9 +158,9 @@ def detail(request, pk, fotka, soustredeni): 'obrazek' : obrazek, 'vyska' : vyska, 'sirka' : sirka, - 'obrazky_predchozi' : obrazky_predchozi, - 'obrazky_dalsi' : obrazky_dalsi, - 'preview' : preview, + 'obrazky_predchozi' : predchozi_obrazky, + 'obrazky_dalsi' : nasledujici_obrazky, + 'upravy_popisku' : dovolit_upravy_popisku(galerie, request), 'form' : form, 'cesta': cesta_od_korene(galerie), }) @@ -179,7 +186,7 @@ def new_galerie(request, galerie, soustredeni): gal = Galerie() gal.nazev = form.cleaned_data['nazev'] #gal.popis = form.cleaned_data['popis'] # popis nepouzivame - gal.zobrazit = 1 # galerie je v procesu vytvareni + gal.zobrazit = ORG # galerie je v procesu vytvareni ''' pokud je to podgalerie pridej nadrazenou galerii a nadrazene soustredeni nechej volne, pokud je to hlavni galerie, tak nadrazena galerie neexistuje, diff --git a/odevzdavatko/static/css/odevzdavatko.css b/odevzdavatko/static/css/odevzdavatko.css new file mode 100644 index 00000000..9a61b259 --- /dev/null +++ b/odevzdavatko/static/css/odevzdavatko.css @@ -0,0 +1,10 @@ +.odevzdavatko-role { + font-size: 0.8em; + + .vyrazne { + color: var(--hlavni-oranzova); + } + .nevyrazne { + color: #aaa; + } +} diff --git a/odevzdavatko/templates/odevzdavatko/base.html b/odevzdavatko/templates/odevzdavatko/base.html new file mode 100644 index 00000000..e304e253 --- /dev/null +++ b/odevzdavatko/templates/odevzdavatko/base.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% load static %} + +{% block custom_css %} + +{% endblock %} diff --git a/odevzdavatko/templates/odevzdavatko/tabulka.html b/odevzdavatko/templates/odevzdavatko/tabulka.html index 41f91b14..4167c4b5 100644 --- a/odevzdavatko/templates/odevzdavatko/tabulka.html +++ b/odevzdavatko/templates/odevzdavatko/tabulka.html @@ -1,6 +1,7 @@ -{% extends "base.html" %} +{% extends "odevzdavatko/base.html" %} {% load barvy_reseni %} +{% load orgove %} {% block content %} @@ -27,7 +28,15 @@ Do data (včetně): {{ filtr.reseni_do }} {% for p in problemy %}
{% endfor %} diff --git a/odevzdavatko/templatetags/orgove.py b/odevzdavatko/templatetags/orgove.py new file mode 100644 index 00000000..c5380067 --- /dev/null +++ b/odevzdavatko/templatetags/orgove.py @@ -0,0 +1,27 @@ +from django import template +register = template.Library() + +from personalni.utils import organizator_cehokoliv + +# Jen typová anotace +from tvorba.models import Problem +from personalni.models import Osoba, Organizator, Resitel, Prijemce +from django.contrib.auth.models import AnonymousUser, User + +@register.filter +def ma_autora(p: Problem, o: Osoba | Organizator | User | AnonymousUser | Resitel | Prijemce) -> bool | None: + o = organizator_cehokoliv(o) + if o is None: return None + return p.autor == o + +@register.filter +def ma_garanta(p: Problem, o: Osoba | Organizator | User | AnonymousUser | Resitel | Prijemce) -> bool | None: + o = organizator_cehokoliv(o) + if o is None: return None + return p.garant == o + +@register.filter +def ma_opravovatele(p: Problem, o: Osoba | Organizator | User | AnonymousUser | Resitel | Prijemce) -> bool | None: + o = organizator_cehokoliv(o) + if o is None: return None + return p.opravovatele.contains(o) diff --git a/personalni/utils.py b/personalni/utils.py index 02e541da..4a8b9a38 100644 --- a/personalni/utils.py +++ b/personalni/utils.py @@ -3,7 +3,7 @@ import re from django.contrib.auth import get_user_model from django.contrib.auth.decorators import permission_required, user_passes_test -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import AnonymousUser, User from django.db import transaction import soustredeni.models @@ -182,3 +182,46 @@ def merge_osoby(cilova, zdrojova): cilova.save() input("Potvrdit transakci osob (^C pro zrušení) ") + +def osoba_uzivatele(u: User | AnonymousUser) -> Osoba | None: + if u.is_anonymous: return None + try: + return u.osoba + except User.osoba.RelatedObjectDoesNotExist: + return None + +def resitel_osoby(o: Osoba) -> Resitel | None: + try: + return o.resitel + except Osoba.resitel.RelatedObjectDoesNotExist: + return None + +def resitel_uzivatele(u: User | AnonymousUser) -> Resitel | None: + o = osoba_uzivatele(u) + if o is None: return None + return resitel_osoby(o) + +def resitel_cehokoliv(r: User | AnonymousUser | Osoba | Organizator | Resitel | Prijemce) -> Organizator | None: + if isinstance(r, User): r = resitel_uzivatele(r) + if isinstance(r, Osoba): r = resitel_osoby(r) + if isinstance(r, Resitel) or isinstance(r, Prijemce): r = resitel_osoby(r.osoba) + assert isinstance(r, Resitel) or r is None + return r + +def organizator_osoby(o: Osoba) -> Organizator | None: + try: + return o.org + except Osoba.org.RelatedObjectDoesNotExist: + return None + +def organizator_uzivatele(u: User | AnonymousUser) -> Organizator | None: + o = osoba_uzivatele(u) + if o is None: return None + return organizator_osoby(o) + +def organizator_cehokoliv(o: User | AnonymousUser | Osoba | Organizator | Resitel | Prijemce) -> Organizator | None: + if isinstance(o, User): o = organizator_uzivatele(o) + if isinstance(o, Osoba): o = organizator_osoby(o) + if isinstance(o, Resitel) or isinstance(o, Prijemce): o = organizator_osoby(o.osoba) + assert isinstance(o, Organizator) or o is None + return o
{# TODO: Přehled řešení k problému, odkázaný odsud? #} - {{ p }} + {{ p }} + + {% spaceless %} + A + G + O + {% endspaceless %} + +