Compare commits
8 Commits
6d5694a153
...
2bc64cdc5b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc64cdc5b | |||
| ace625a530 | |||
| b1e02bbf0a | |||
| ad74ae2c51 | |||
| e68c2eca63 | |||
| 8121a45ead | |||
| c2b6dfa3e4 | |||
| a7ee650729 |
@@ -30,11 +30,6 @@ docker-compose up
|
||||
|
||||
### Command Befehle
|
||||
|
||||
Erstellt die fehlenden Thumbs für die Alben in der Galerie:
|
||||
<code>
|
||||
python3 fet2020/manage.py create_thumbs
|
||||
</code>
|
||||
|
||||
Erstellt alle Searchindexes neu:
|
||||
<code>
|
||||
python3 fet2020/manage.py rebuild_index
|
||||
|
||||
@@ -74,6 +74,18 @@ def get_app_list(self, request, app_label=None):
|
||||
}
|
||||
app["models"].sort(key=lambda x: ordering[x["name"]])
|
||||
|
||||
elif app["app_label"] == "finance":
|
||||
ordering = {
|
||||
"Rechnungen": 1,
|
||||
"Wiref Formulare": 2,
|
||||
"Beschlüsse": 3,
|
||||
"Bankdaten": 4,
|
||||
}
|
||||
app["models"].sort(key=lambda x: ordering[x["name"]])
|
||||
|
||||
else:
|
||||
app["models"].sort(key=lambda x: x["name"])
|
||||
|
||||
return app_list
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ def get_ep_client():
|
||||
api_key = f.read()
|
||||
api_key = api_key.rstrip()
|
||||
|
||||
if api_key == "":
|
||||
if not api_key:
|
||||
logger.info("API Key is missing. Path: %s", api_key_path)
|
||||
return None
|
||||
|
||||
@@ -56,97 +56,114 @@ def get_ep_group(ep_group_name: str = "fet") -> dict[str, str]:
|
||||
return ep_group
|
||||
|
||||
|
||||
def ep_pad_exists(pad_id: str | None = None) -> bool | None:
|
||||
def ep_pad_exists(pad_id: str = "") -> bool:
|
||||
"""Check if pad exists.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pad_id : str | None
|
||||
pad_id : str
|
||||
Id of pad that is checked if it exists.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool | None
|
||||
bool
|
||||
- True: Pad exists.
|
||||
- False: Pad doesn't exist.
|
||||
- False: Pad not found.
|
||||
- None: There is no pad id or no connection to etherpad server.
|
||||
"""
|
||||
if pad_id is None:
|
||||
if not pad_id:
|
||||
return None
|
||||
|
||||
if (ep_c := get_ep_client()) is None:
|
||||
if not (ep_c := get_ep_client()):
|
||||
return None
|
||||
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
lists = ep_c.listPads(groupID=ep_group["groupID"])
|
||||
if any(pad_id in s for s in lists["padIDs"]):
|
||||
logger.info("Etherpad exists. Pad: %s", pad_id)
|
||||
logger.info("Etherpad '%s' exists.", pad_id)
|
||||
return True
|
||||
|
||||
logger.info("Etherpad doesn't exist. Pad: %s", pad_id)
|
||||
logger.info("Etherpad '%s' not found.", pad_id)
|
||||
return False
|
||||
|
||||
|
||||
def ep_create_new_pad(pad_id: str | None, text="helloworld"):
|
||||
def ep_create_new_pad(pad_id: str, text="helloworld") -> str:
|
||||
"""Create a new pad if it doesn't exist.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pad_id : str
|
||||
Id of the new pad to be created.
|
||||
text : str
|
||||
Text for new pad.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Id of the new pad.
|
||||
- None: No pad is created.
|
||||
"""
|
||||
Create a pad if it doesn't exist.
|
||||
|
||||
Return a pad_id if new pad is created. Otherwise None (when the padID exists already).
|
||||
"""
|
||||
if pad_id is None:
|
||||
if not pad_id:
|
||||
return None
|
||||
|
||||
if (ep_c := get_ep_client()) is None:
|
||||
if not (ep_c := get_ep_client()):
|
||||
return None
|
||||
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
if ep_pad_exists(pad_id):
|
||||
logger.info("Can't create new etherpad '%s' because it already exists.", pad_id)
|
||||
return None
|
||||
|
||||
if ep_pad_exists(pad_id) is False:
|
||||
ep_c.createGroupPad(groupID=ep_group["groupID"], padName=pad_id, text=text)
|
||||
logger.info("Create new etherpad. Pad: %s", pad_id)
|
||||
logger.info("Create new etherpad '%s'.", pad_id)
|
||||
return pad_id
|
||||
|
||||
|
||||
def ep_get_html(pad_id: str) -> str:
|
||||
if not pad_id:
|
||||
return None
|
||||
|
||||
|
||||
def ep_get_html(pad_id: str | None) -> str | None:
|
||||
if (ep_c := get_ep_client()) is None:
|
||||
if not (ep_c := get_ep_client()):
|
||||
return None
|
||||
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
if not ep_pad_exists(pad_id):
|
||||
return None
|
||||
|
||||
if ep_pad_exists(pad_id):
|
||||
return ep_c.getHTML(padID=ep_group["groupID"] + "$" + pad_id)["html"]
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def ep_set_html(pad_id: str | None, html: str):
|
||||
if (ep_c := get_ep_client()) is None:
|
||||
def ep_set_html(pad_id: str, html: str) -> bool:
|
||||
if not pad_id:
|
||||
return None
|
||||
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
if not (ep_c := get_ep_client()):
|
||||
return None
|
||||
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
if not ep_pad_exists(pad_id):
|
||||
return None
|
||||
|
||||
if ep_pad_exists(pad_id):
|
||||
ep_c.setHTML(padID=ep_group["groupID"] + "$" + pad_id, html=html)
|
||||
return True
|
||||
|
||||
|
||||
def ep_get_url(pad_id: str):
|
||||
if not pad_id:
|
||||
return None
|
||||
|
||||
|
||||
def ep_get_url(pad_id: str | None):
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
if ep_pad_exists(pad_id):
|
||||
return urljoin(
|
||||
settings.ETHERPAD_CLIENT["exturl"],
|
||||
"p/" + ep_group["groupID"] + "$" + str(pad_id),
|
||||
)
|
||||
if not ep_pad_exists(pad_id):
|
||||
return None
|
||||
|
||||
return "#"
|
||||
return urljoin(settings.ETHERPAD_CLIENT["exturl"], "p/" + ep_group["groupID"] + "$" + str(pad_id))
|
||||
|
||||
@@ -10,11 +10,11 @@ from .api import get_ep_client, get_ep_group
|
||||
|
||||
@ep_authenticated_user
|
||||
def _create_ep_session(request, expires):
|
||||
if (ep_c := get_ep_client()) is None:
|
||||
if not (ep_c := get_ep_client()):
|
||||
return None
|
||||
|
||||
if (ep_group := get_ep_group()) is None:
|
||||
return None, None
|
||||
if not (ep_group := get_ep_group()):
|
||||
return None
|
||||
|
||||
author = ep_c.createAuthorIfNotExistsFor(
|
||||
name=str(request.user),
|
||||
|
||||
@@ -394,6 +394,8 @@ TAGGIT_FORCE_LOWERCASE = True
|
||||
|
||||
|
||||
# THUMBNAIL
|
||||
THUMBNAIL_DEFAULT_STORAGE_ALIAS = "default"
|
||||
|
||||
THUMBNAIL_ALIASES = {
|
||||
"": {
|
||||
"avatar": {"size": (50, 50), "crop": True},
|
||||
|
||||
@@ -6,7 +6,7 @@ from posts.models import Event, FetMeeting, Post
|
||||
|
||||
|
||||
def index(request):
|
||||
posts = Post.articles.date_sorted_list()
|
||||
posts = Post.articles.date_sorted()
|
||||
# A maximum of 5 posts should be displayed on the startpage.
|
||||
post_count = 5
|
||||
|
||||
|
||||
@@ -346,28 +346,28 @@ class BillAdmin(admin.ModelAdmin):
|
||||
status=obj.get_status_display(),
|
||||
)
|
||||
|
||||
@admin.action(description="Als 'Abgerechnet' markieren.")
|
||||
@admin.action(description="Als 'Für Überweisung freigegeben' markieren.")
|
||||
def make_cleared(self, request, queryset):
|
||||
updated = queryset.update(status=Bill.Status.CLEARED)
|
||||
self.message_user(
|
||||
request,
|
||||
ngettext(
|
||||
"%d Rechnung wurde als 'Abgerechnet' markiert.",
|
||||
"%d Rechnungen wurden als 'Abgerechnet' markiert.",
|
||||
"%d Rechnung wurde als 'Für Überweisung freigegeben' markiert.",
|
||||
"%d Rechnungen wurden als 'Für Überweisung freigegeben' markiert.",
|
||||
updated,
|
||||
)
|
||||
% updated,
|
||||
messages.SUCCESS,
|
||||
)
|
||||
|
||||
@admin.action(description="Als 'Abgeschlossen' markieren.")
|
||||
@admin.action(description="Als 'Abgeschlossen / Überwiesen' markieren.")
|
||||
def make_finished(self, request, queryset):
|
||||
updated = queryset.update(status=Bill.Status.FINISHED)
|
||||
self.message_user(
|
||||
request,
|
||||
ngettext(
|
||||
"%d Rechnung wurde als 'Abgeschlossen' markiert.",
|
||||
"%d Rechnungen wurden als 'Abgeschlossen' markiert.",
|
||||
"%d Rechnung wurde als 'Abgeschlossen / Überwiesen' markiert.",
|
||||
"%d Rechnungen wurden als 'Abgeschlossen / Überwiesen' markiert.",
|
||||
updated,
|
||||
)
|
||||
% updated,
|
||||
|
||||
@@ -439,21 +439,19 @@ class ResolutionAdminForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs) # to get the self.fields set
|
||||
|
||||
resolution = kwargs.get("instance")
|
||||
bills = Bill.objects.filter(resolution=resolution)
|
||||
total = 0
|
||||
for elem in bills:
|
||||
budget = 0.0
|
||||
total = 0.0
|
||||
if (resolution := kwargs.get("instance")) is not None:
|
||||
for elem in Bill.objects.filter(resolution=resolution):
|
||||
total += elem.amount
|
||||
|
||||
budget = resolution.budget
|
||||
|
||||
self.fields["total"].disabled = True
|
||||
self.fields["total"].initial = total
|
||||
self.fields["total"].label = "Gesamtsumme (EUR)"
|
||||
self.fields["total"].required = False
|
||||
|
||||
budget = 0
|
||||
if resolution:
|
||||
budget = resolution.budget
|
||||
|
||||
self.fields["budget_remaining"].disabled = True
|
||||
self.fields["budget_remaining"].initial = budget - total
|
||||
self.fields["budget_remaining"].label = "Restbudget (EUR)"
|
||||
|
||||
@@ -154,7 +154,7 @@ class Bill(models.Model):
|
||||
|
||||
class Affiliation(models.TextChoices):
|
||||
VEREIN = "V", "Vereinsbudget"
|
||||
OFFICIAL = "B", "Offizielles Budget"
|
||||
OFFICIAL = "B", "Wiref-Budget"
|
||||
REPRESENTATION = "R", "Bundesvertretung"
|
||||
|
||||
affiliation = models.CharField(
|
||||
@@ -186,9 +186,9 @@ class Bill(models.Model):
|
||||
|
||||
class Status(models.TextChoices):
|
||||
SUBMITTED = "S", "Eingereicht"
|
||||
INCOMPLETED = "I", "Unvollständig"
|
||||
CLEARED = "C", "Abgerechnet"
|
||||
FINISHED = "F", "Abgeschlossen"
|
||||
INCOMPLETED = "I", "Unvollständig / Abgelehnt"
|
||||
CLEARED = "C", "Für Überweisung freigegeben"
|
||||
FINISHED = "F", "Abgeschlossen / Überwiesen"
|
||||
|
||||
status = models.CharField(
|
||||
max_length=1,
|
||||
|
||||
@@ -15,11 +15,7 @@ class AlbumAdmin(admin.ModelAdmin):
|
||||
"Der Ordner für die Fotos liegt am Server unter '/mnt/save/fotos/www'. "
|
||||
"Fette Schriften sind Pflichtfelder."
|
||||
)
|
||||
return super().add_view(
|
||||
request,
|
||||
form_url,
|
||||
extra_context=extra_context,
|
||||
)
|
||||
return super().add_view(request, form_url, extra_context=extra_context)
|
||||
|
||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||
extra_context = extra_context or {}
|
||||
@@ -27,12 +23,7 @@ class AlbumAdmin(admin.ModelAdmin):
|
||||
"Der Ordner für die Fotos liegt am Server unter '/mnt/save/fotos/www'. "
|
||||
"Fette Schriften sind Pflichtfelder."
|
||||
)
|
||||
return super().change_view(
|
||||
request,
|
||||
object_id,
|
||||
form_url,
|
||||
extra_context=extra_context,
|
||||
)
|
||||
return super().change_view(request, object_id, form_url, extra_context=extra_context)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
obj.author = request.user
|
||||
|
||||
6
fet2020/gallery/choices.py
Normal file
6
fet2020/gallery/choices.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Status(models.TextChoices):
|
||||
DRAFT = "10", "DRAFT"
|
||||
PUBLIC = "20", "PUBLIC"
|
||||
@@ -11,11 +11,7 @@ class AlbumAdminForm(forms.ModelForm):
|
||||
|
||||
widgets = {"description": CKEditorUploadingWidget(config_name="default")}
|
||||
|
||||
labels = {
|
||||
"slug": "Permalink",
|
||||
"event_place": "Event Ort",
|
||||
"description": "Beschreibung",
|
||||
}
|
||||
labels = {"slug": "Permalink", "event_place": "Event Ort", "description": "Beschreibung"}
|
||||
|
||||
help_texts = {
|
||||
"folder_name": "Füge den Ordnername (ohne Pfade) ein.",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import logging
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from gallery.utils import create_thumbs, get_folder_list
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
for folder in get_folder_list():
|
||||
logger.info("Folder '%s' in process.", folder)
|
||||
create_thumbs(folder)
|
||||
|
||||
logger.info("Command 'create thumbs' ended.")
|
||||
11
fet2020/gallery/managers.py
Normal file
11
fet2020/gallery/managers.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
from .choices import Status
|
||||
|
||||
|
||||
class AlbumManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().order_by("-event_date")
|
||||
|
||||
def public(self):
|
||||
return self.get_queryset().filter(status=Status.PUBLIC)
|
||||
@@ -1,45 +1,44 @@
|
||||
import logging
|
||||
from random import randint
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
|
||||
from .choices import Status
|
||||
from .managers import AlbumManager
|
||||
from .utils import get_image_list
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Album(models.Model):
|
||||
title = models.CharField(verbose_name="Titel", max_length=200)
|
||||
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||
|
||||
folder_name = models.CharField(verbose_name="Ordner Name", max_length=200)
|
||||
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, null=True, blank=True)
|
||||
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, blank=True, default="")
|
||||
|
||||
event_date = models.DateField(
|
||||
verbose_name="Event Datum",
|
||||
null=True,
|
||||
blank=True,
|
||||
default=timezone.now,
|
||||
verbose_name="Event Datum", null=True, blank=True, default=timezone.now
|
||||
)
|
||||
event_place = models.CharField(max_length=200, blank=True)
|
||||
|
||||
photographer = models.CharField(
|
||||
verbose_name="Fotograph(en)",
|
||||
max_length=200,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Fotograph(en)", max_length=200, blank=True, default=""
|
||||
)
|
||||
|
||||
DRAFT = "10"
|
||||
PUBLIC = "20"
|
||||
STATUS = (
|
||||
(DRAFT, "DRAFT"),
|
||||
(PUBLIC, "PUBLIC"),
|
||||
)
|
||||
status = models.CharField(max_length=2, choices=STATUS, default=DRAFT)
|
||||
# TextChoices
|
||||
Status = Status
|
||||
|
||||
description = models.TextField(null=True, blank=True)
|
||||
status = models.CharField(max_length=2, choices=Status.choices, default=Status.DRAFT)
|
||||
|
||||
description = models.TextField(blank=True, default="")
|
||||
|
||||
# Managers
|
||||
objects = models.Manager()
|
||||
objects = AlbumManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Album"
|
||||
@@ -57,14 +56,32 @@ class Album(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse("gallery:album", kwargs={"slug": self.slug})
|
||||
|
||||
def get_images(self):
|
||||
def clean(self):
|
||||
if not self.images:
|
||||
logger.info("No thumbnails are generated.")
|
||||
|
||||
@property
|
||||
def images(self) -> list:
|
||||
return get_image_list(self.folder_name)
|
||||
|
||||
def get_images_length_sub_3(self):
|
||||
return len(self.get_images()) - 3
|
||||
return len(self.images) - 3
|
||||
|
||||
def get_model_name(self):
|
||||
return self._meta.model_name
|
||||
|
||||
def get_thumbnail(self):
|
||||
return None
|
||||
@property
|
||||
def get_album_thumbnail(self):
|
||||
if img_list := self.images:
|
||||
return next(
|
||||
(
|
||||
img["thumb_url"]
|
||||
for img in img_list
|
||||
if self.thumbnail and self.thumbnail in img["title"]
|
||||
),
|
||||
img_list[randint(0, len(img_list) - 1)]["thumb_url"],
|
||||
)
|
||||
|
||||
self.status = Album.Status.DRAFT
|
||||
logger.info("Album '%s' is empty.", self.title)
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps, views
|
||||
from .views import AlbumDetailView, DraftAlbumDetailView
|
||||
|
||||
app_name = apps.GalleryConfig.name
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("<slug:slug>/", views.show_album, name="album"),
|
||||
path("draft/<slug:slug>/", views.show_draft_album, name="album_draft"),
|
||||
path("<slug:slug>/", AlbumDetailView.as_view(), name="album"),
|
||||
path("draft/<slug:slug>/", DraftAlbumDetailView.as_view(), name="album_draft"),
|
||||
]
|
||||
|
||||
@@ -1,80 +1,56 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.validators import get_available_image_extensions
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
gallery_path = settings.GALLERY["path"]
|
||||
gallery_thumb_path = settings.GALLERY["thumb_path"]
|
||||
gallery_path = Path(settings.MEDIA_ROOT) / settings.GALLERY["path"]
|
||||
gallery_path_url = Path(settings.MEDIA_URL) / settings.GALLERY["path"]
|
||||
gallery_thumb_path = Path(settings.MEDIA_ROOT) / settings.GALLERY["thumb_path"]
|
||||
gallery_thumb_path_url = Path(settings.MEDIA_URL) / settings.GALLERY["thumb_path"]
|
||||
logger = logging.getLogger(__name__)
|
||||
size = (320, 320)
|
||||
valid_images = [".jpg", ".png"]
|
||||
|
||||
|
||||
def get_image_list(folder_name):
|
||||
file_path = os.path.join(settings.MEDIA_ROOT + gallery_path, folder_name)
|
||||
def get_image_list(folder_name: str) -> list:
|
||||
image_path = Path(gallery_path) / folder_name
|
||||
thumb_path = Path(gallery_thumb_path) / folder_name
|
||||
img_list = []
|
||||
|
||||
if os.path.exists(file_path):
|
||||
for img in os.listdir(file_path):
|
||||
ext = os.path.splitext(img)[1]
|
||||
if ext.lower() not in valid_images:
|
||||
if not Path(image_path).exists():
|
||||
logger.info("Image path '%s' not found.", image_path)
|
||||
return img_list
|
||||
|
||||
Path(thumb_path).mkdir(exist_ok=True)
|
||||
|
||||
for _file in os.listdir(image_path):
|
||||
if Path(_file).suffix.lower()[1:] not in get_available_image_extensions():
|
||||
continue
|
||||
|
||||
thumb_path = os.path.join(settings.MEDIA_ROOT + gallery_thumb_path, folder_name)
|
||||
thumb_file_path = os.path.join(thumb_path, f"thumb_{img}")
|
||||
if os.path.exists(thumb_file_path):
|
||||
thumb_url = os.path.join(
|
||||
settings.MEDIA_URL + gallery_thumb_path,
|
||||
folder_name + "/" + f"thumb_{img}",
|
||||
)
|
||||
else:
|
||||
thumb_url = None
|
||||
thumb_file_path = Path(thumb_path) / f"thumb_{_file}"
|
||||
if not Path(thumb_file_path).exists():
|
||||
with Image.open(Path(image_path) / _file, "r") as im:
|
||||
if im._getexif() is not None:
|
||||
im = ImageOps.exif_transpose(im)
|
||||
|
||||
thumb = ImageOps.fit(im, size, Image.Resampling.LANCZOS)
|
||||
thumb.save(thumb_file_path)
|
||||
logger.info("Save thumb 'thumb_%s'.", _file)
|
||||
|
||||
img_dict = {
|
||||
"title": img,
|
||||
"image_url": os.path.join(
|
||||
settings.MEDIA_URL + gallery_path,
|
||||
folder_name + "/" + img,
|
||||
),
|
||||
"thumb_url": thumb_url,
|
||||
"title": _file,
|
||||
"image_url": Path(gallery_path_url) / folder_name / _file,
|
||||
"thumb_url": Path(gallery_thumb_path_url) / folder_name / f"thumb_{_file}",
|
||||
}
|
||||
|
||||
img_list.append(img_dict)
|
||||
|
||||
return img_list
|
||||
|
||||
|
||||
def get_folder_list():
|
||||
if os.path.exists(settings.MEDIA_ROOT + gallery_path):
|
||||
return next(os.walk(settings.MEDIA_ROOT + gallery_path))[1]
|
||||
if Path(gallery_path).exists():
|
||||
return next(os.walk(gallery_path))[1]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def create_thumbs(folder_path):
|
||||
file_path = os.path.join(settings.MEDIA_ROOT + gallery_path, folder_path)
|
||||
thumb_path = os.path.join(settings.MEDIA_ROOT + gallery_thumb_path, folder_path)
|
||||
|
||||
if os.path.exists(file_path):
|
||||
os.makedirs(thumb_path, exist_ok=True)
|
||||
|
||||
for f in os.listdir(file_path):
|
||||
ext = os.path.splitext(f)[1]
|
||||
if ext.lower() not in valid_images:
|
||||
continue
|
||||
|
||||
thumb_file_path = os.path.join(thumb_path, f"thumb_{f}")
|
||||
if os.path.exists(thumb_file_path):
|
||||
continue
|
||||
|
||||
image_path = os.path.join(file_path, f)
|
||||
logger.info("Edit picture '%s'.", f)
|
||||
|
||||
with Image.open(str(image_path), "r") as image:
|
||||
if image._getexif() is not None:
|
||||
image = ImageOps.exif_transpose(image)
|
||||
|
||||
thumb = ImageOps.fit(image, size, Image.ANTIALIAS)
|
||||
thumb.save(thumb_file_path)
|
||||
logger.info("Save thumb 'thumb_%s'.", f)
|
||||
|
||||
@@ -1,94 +1,65 @@
|
||||
import logging
|
||||
from collections import deque
|
||||
from random import randint
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from django.utils.text import slugify
|
||||
|
||||
from authentications.decorators import authenticated_user
|
||||
from django.views.generic.detail import DetailView
|
||||
|
||||
from .models import Album
|
||||
from .utils import create_thumbs, get_folder_list
|
||||
from .utils import get_folder_list
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def index(request):
|
||||
if request.user.is_authenticated:
|
||||
albums = deque(Album.objects.all().order_by("-event_date"))
|
||||
albums = deque(Album.objects.all())
|
||||
|
||||
# get albums that are in the server but not in the db.
|
||||
folders = get_folder_list()
|
||||
if folders:
|
||||
for folder in folders:
|
||||
# Get albums that are in the server but not in the db.
|
||||
for folder in get_folder_list():
|
||||
if not Album.objects.filter(folder_name=folder):
|
||||
albums.append(
|
||||
Album(
|
||||
title=folder,
|
||||
slug=slugify(folder),
|
||||
folder_name=folder,
|
||||
event_date=None,
|
||||
),
|
||||
Album(title=folder, slug=slugify(folder), folder_name=folder, event_date=None)
|
||||
)
|
||||
else:
|
||||
# show only PUBLIC albums.
|
||||
albums = deque(Album.objects.filter(status=Album.PUBLIC).order_by("-event_date"))
|
||||
|
||||
for album in list(albums):
|
||||
img_list = album.get_images()
|
||||
if img_list:
|
||||
if album.thumbnail:
|
||||
for img in img_list:
|
||||
if album.thumbnail in img["title"]:
|
||||
album.thumbnail = img["thumb_url"]
|
||||
break
|
||||
else:
|
||||
value = randint(0, len(img_list) - 1)
|
||||
album.thumbnail = img_list[value]["thumb_url"]
|
||||
else:
|
||||
value = randint(0, len(img_list) - 1)
|
||||
album.thumbnail = img_list[value]["thumb_url"]
|
||||
else:
|
||||
# empty album is temporarily set to DRAFT.
|
||||
album.thumbnail = settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
album.status = Album.DRAFT
|
||||
# Show only PUBLIC albums.
|
||||
albums = Album.objects.public()
|
||||
|
||||
context = {
|
||||
"albums": albums,
|
||||
}
|
||||
context = {"albums": albums}
|
||||
|
||||
return render(request, "gallery/index.html", context)
|
||||
|
||||
|
||||
def show_album(request, slug):
|
||||
album = Album.objects.filter(slug=slug).first()
|
||||
if not album:
|
||||
class AlbumDetailView(DetailView):
|
||||
model = Album
|
||||
template_name = "gallery/album.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
Album.objects.public()
|
||||
if not self.request.user.is_authenticated
|
||||
else Album.objects.all()
|
||||
)
|
||||
|
||||
|
||||
class DraftAlbumDetailView(LoginRequiredMixin, DetailView):
|
||||
model = Album
|
||||
template_name = "gallery/album.html"
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
slug = self.kwargs.get(self.slug_url_kwarg)
|
||||
|
||||
if not (album := Album.objects.filter(slug=slug).first()):
|
||||
for folder in get_folder_list():
|
||||
if slug == slugify(folder):
|
||||
album = Album(
|
||||
title=folder,
|
||||
slug=slugify(folder),
|
||||
folder_name=folder,
|
||||
event_date=None,
|
||||
title=folder, slug=slugify(folder), folder_name=folder, event_date=None
|
||||
)
|
||||
break
|
||||
else:
|
||||
raise Http404("wrong album slug")
|
||||
raise Http404("Album slug not found.")
|
||||
|
||||
img_list = album.get_images()
|
||||
if not img_list:
|
||||
# empty album is temporarily set to DRAFT.
|
||||
album.status = Album.DRAFT
|
||||
|
||||
create_thumbs(album.folder_name)
|
||||
|
||||
context = {
|
||||
"album": album,
|
||||
"images": img_list,
|
||||
}
|
||||
|
||||
return render(request, "gallery/album.html", context)
|
||||
|
||||
|
||||
@authenticated_user
|
||||
def show_draft_album(request, slug):
|
||||
return show_album(request, slug)
|
||||
return album
|
||||
|
||||
@@ -36,7 +36,7 @@ class JobOverviewInline(JobMemberInline):
|
||||
verbose_name_plural = "Tätigkeitsübersicht"
|
||||
|
||||
def get_queryset(self, request):
|
||||
return JobMember.members.get_all_jobs_sorted()
|
||||
return JobMember.members.get_all_jobs()
|
||||
|
||||
|
||||
class ActiveMemberInline(JobMemberInline):
|
||||
@@ -81,11 +81,8 @@ class MemberAdmin(admin.ModelAdmin):
|
||||
"nickname",
|
||||
"mailaccount",
|
||||
"role",
|
||||
"description",
|
||||
"image",
|
||||
"birthday",
|
||||
"phone",
|
||||
"address",
|
||||
"description",
|
||||
),
|
||||
},
|
||||
),
|
||||
|
||||
@@ -24,8 +24,9 @@ class ActiveMemberForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
member_qs = self.fields["member"].queryset.filter(role="A").order_by("firstname", "surname")
|
||||
self.fields["member"].queryset = member_qs
|
||||
self.fields["member"].queryset = (
|
||||
self.fields["member"].queryset.filter(role="A").order_by("firstname", "surname")
|
||||
)
|
||||
|
||||
|
||||
class InactiveMemberForm(forms.ModelForm):
|
||||
@@ -35,8 +36,9 @@ class InactiveMemberForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
member_qs = self.fields["member"].queryset.order_by("firstname", "surname")
|
||||
self.fields["member"].queryset = member_qs
|
||||
self.fields["member"].queryset = self.fields["member"].queryset.order_by(
|
||||
"firstname", "surname"
|
||||
)
|
||||
|
||||
|
||||
class MemberForm(forms.ModelForm):
|
||||
@@ -54,9 +56,6 @@ class MemberForm(forms.ModelForm):
|
||||
labels = {
|
||||
"description": "Beschreibung zu der Person",
|
||||
"image": "Porträt",
|
||||
"birthday": "Geburtstag",
|
||||
"phone": "Telefonnummer",
|
||||
"address": "Wohnadresse",
|
||||
}
|
||||
|
||||
widgets = {"description": CKEditorUploadingWidget(config_name="default")}
|
||||
|
||||
@@ -17,16 +17,13 @@ class ActiveJobMemberManager(models.Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
date_today = timezone.now().date()
|
||||
qs = (
|
||||
return (
|
||||
super()
|
||||
.get_queryset()
|
||||
.filter(Q(member__role="A") & (Q(job_end__gt=date_today) | Q(job_end__isnull=True)))
|
||||
.order_by("job_role", "member__firstname", "member__surname", "job_start")
|
||||
)
|
||||
|
||||
return qs.filter(
|
||||
Q(member__role="A") & (Q(job_end__gt=date_today) | Q(job_end__isnull=True)),
|
||||
)
|
||||
|
||||
|
||||
class InactiveJobMemberManager(models.Manager):
|
||||
"""return a list of inactive member."""
|
||||
@@ -40,39 +37,42 @@ class InactiveJobMemberManager(models.Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
date_today = timezone.now().date()
|
||||
qs = super().get_queryset().order_by("member__firstname", "member__surname", "-job_start")
|
||||
|
||||
return qs.filter(
|
||||
return (
|
||||
super()
|
||||
.get_queryset()
|
||||
.filter(
|
||||
Q(member__role="P")
|
||||
| (Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False)),
|
||||
| (Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False))
|
||||
)
|
||||
.order_by("member__firstname", "member__surname", "-job_start")
|
||||
)
|
||||
|
||||
|
||||
class JobMemberManager(models.Manager):
|
||||
def get_members(self, role):
|
||||
qs = self.get_queryset().order_by("member__firstname")
|
||||
|
||||
return qs.filter(Q(member__role=role))
|
||||
|
||||
def get_all_jobs_sorted(self):
|
||||
qs = self.get_queryset().order_by(
|
||||
F("job_end").desc(nulls_first=True),
|
||||
"-job_start",
|
||||
"job__name",
|
||||
def get_all_jobs(self):
|
||||
return self.get_queryset().order_by(
|
||||
F("job_end").desc(nulls_first=True), "-job_start", "job__name"
|
||||
)
|
||||
return qs
|
||||
|
||||
def get_active_jobs(self, member_id):
|
||||
date_today = timezone.now().date()
|
||||
qs = self.get_queryset().filter(member__id=member_id).order_by("-job_start", "job__name")
|
||||
|
||||
return qs.filter(Q(job_end__gt=date_today) | Q(job_end__isnull=True))
|
||||
return (
|
||||
self.get_queryset()
|
||||
.filter(Q(member__id=member_id) & (Q(job_end__gt=date_today) | Q(job_end__isnull=True)))
|
||||
.order_by("-job_start", "job__name")
|
||||
)
|
||||
|
||||
def get_inactive_jobs(self, member_id):
|
||||
date_today = timezone.now().date()
|
||||
qs = self.get_queryset().filter(member__id=member_id).order_by("-job_start", "job__name")
|
||||
|
||||
return qs.filter(Q(job_end__lt=date_today + timedelta(days=1)) & Q(job_end__isnull=False))
|
||||
return (
|
||||
self.get_queryset()
|
||||
.filter(
|
||||
Q(member__id=member_id)
|
||||
& Q(job_end__lt=date_today + timedelta(days=1))
|
||||
& Q(job_end__isnull=False)
|
||||
)
|
||||
.order_by("-job_start", "job__name")
|
||||
)
|
||||
|
||||
|
||||
class MemberManager(models.Manager):
|
||||
|
||||
@@ -26,7 +26,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='phone',
|
||||
field=models.CharField(blank=True, max_length=17, validators=[members.validators.PhoneNumberValidator()]),
|
||||
field=models.CharField(blank=True, max_length=17),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
|
||||
@@ -16,12 +16,12 @@ from .managers import (
|
||||
MemberManager,
|
||||
)
|
||||
from .validators import (
|
||||
PhoneNumberValidator,
|
||||
validate_domainonly_email,
|
||||
validate_file_size,
|
||||
validate_image_dimension,
|
||||
)
|
||||
|
||||
fet_logo_url = settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -55,23 +55,13 @@ class Member(models.Model):
|
||||
default=MemberRole.ACTIVE,
|
||||
)
|
||||
|
||||
description = models.TextField(null=True, blank=True)
|
||||
description = models.TextField(blank=True, default="")
|
||||
|
||||
image = ThumbnailerImageField(
|
||||
upload_to="uploads/members/image/",
|
||||
validators=[validate_file_size, validate_image_dimension],
|
||||
)
|
||||
|
||||
birthday = models.DateField(null=True, blank=True)
|
||||
|
||||
phone = models.CharField(
|
||||
max_length=17,
|
||||
blank=True,
|
||||
validators=[PhoneNumberValidator()],
|
||||
)
|
||||
|
||||
address = models.TextField(null=True, blank=True)
|
||||
|
||||
date_modified = models.DateTimeField(auto_now=True)
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@@ -88,7 +78,7 @@ class Member(models.Model):
|
||||
|
||||
# need to have 'View on site' link in admin app
|
||||
def get_absolute_url(self):
|
||||
return reverse("members:member", kwargs={"member_id": self.id})
|
||||
return reverse("members:member", kwargs={"pk": self.pk})
|
||||
|
||||
def clean(self):
|
||||
if not self.image:
|
||||
@@ -98,7 +88,7 @@ class Member(models.Model):
|
||||
try:
|
||||
user = User.objects.get(username=self.username.lower())
|
||||
except User.DoesNotExist as e:
|
||||
logger.info("Username does not exist. Error: %s", e)
|
||||
logger.info("Username not found. Error: %s", e)
|
||||
else:
|
||||
user.first_name = self.firstname
|
||||
user.save()
|
||||
@@ -108,35 +98,19 @@ class Member(models.Model):
|
||||
|
||||
@property
|
||||
def image_url(self):
|
||||
if self.image:
|
||||
return self.image.url
|
||||
|
||||
# return default image
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
return self.image.url if self.image else fet_logo_url
|
||||
|
||||
@property
|
||||
def avatar_url(self):
|
||||
if self.image:
|
||||
return self.image["avatar"].url
|
||||
|
||||
# return default image
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
return self.image["avatar"].url if self.image else fet_logo_url
|
||||
|
||||
@property
|
||||
def portrait_url(self):
|
||||
if self.image:
|
||||
return self.image["portrait"].url
|
||||
|
||||
# return default image
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
return self.image["portrait"].url if self.image else fet_logo_url
|
||||
|
||||
@property
|
||||
def thumb_url(self):
|
||||
if self.image:
|
||||
return self.image["thumb"].url
|
||||
|
||||
# return default image
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
return self.image["thumb"].url if self.image else fet_logo_url
|
||||
|
||||
|
||||
class JobGroup(models.Model):
|
||||
@@ -145,7 +119,7 @@ class JobGroup(models.Model):
|
||||
shortterm = models.CharField(max_length=128, unique=True, blank=True)
|
||||
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||
|
||||
description = models.TextField(null=True, blank=True)
|
||||
description = models.TextField(blank=True, default="")
|
||||
|
||||
# Managers
|
||||
objects = models.Manager()
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import apps, views
|
||||
from . import apps
|
||||
from .views import ActiveMemberListView, JobListView, MemberDetailView, MemberListView
|
||||
|
||||
app_name = apps.MembersConfig.name
|
||||
|
||||
urlpatterns = [
|
||||
path("members/", views.index, name="index"),
|
||||
path("members/<str:role>/", views.members, name="members"),
|
||||
path("member/<int:member_id>/", views.profile, name="member"),
|
||||
path("section/<slug:slug>/", views.jobs, name="jobs"),
|
||||
path("members/", ActiveMemberListView.as_view(), name="index"),
|
||||
path("members/<str:role>/", MemberListView.as_view(), name="members"),
|
||||
path("member/<int:pk>/", MemberDetailView.as_view(), name="member"),
|
||||
path("section/<slug:slug>/", JobListView.as_view(), name="jobs"),
|
||||
]
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
from django.core.validators import RegexValidator, ValidationError
|
||||
from django.utils.deconstruct import deconstructible
|
||||
|
||||
|
||||
@deconstructible
|
||||
class PhoneNumberValidator(RegexValidator):
|
||||
regex = r"^\+?1?\d{9,15}$"
|
||||
message = (
|
||||
"Telefonnummer muss in diesem Format +999999999999 eingegeben werden. Bis zu 15 Zahlen "
|
||||
"sind erlaubt."
|
||||
)
|
||||
from django.core.validators import ValidationError
|
||||
|
||||
|
||||
def validate_domainonly_email(value):
|
||||
|
||||
@@ -1,74 +1,57 @@
|
||||
import logging
|
||||
|
||||
from django.db.models import F
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.detail import DetailView
|
||||
|
||||
from .models import JobGroup, JobMember, Member
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ActiveMemberListView(ListView):
|
||||
allow_empty = False
|
||||
model = Member
|
||||
template_name = "members/members.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return Member.all_members.filter(role=Member.MemberRole.ACTIVE)
|
||||
|
||||
|
||||
def index(request):
|
||||
members = Member.all_members.filter(role=Member.MemberRole.ACTIVE)
|
||||
class JobListView(ListView):
|
||||
allow_empty = False
|
||||
model = JobMember
|
||||
template_name = "members/jobs.html"
|
||||
|
||||
context = {
|
||||
"members": members,
|
||||
}
|
||||
|
||||
return render(request, "members/members.html", context)
|
||||
|
||||
|
||||
def jobs(request, slug=None):
|
||||
try:
|
||||
description = JobGroup.objects.filter(slug=slug).values().first()["description"]
|
||||
except Exception:
|
||||
logger.info("Wrong job '%s'", slug)
|
||||
raise Http404("wrong job")
|
||||
|
||||
job_members = JobMember.active_member.get_all(slug=slug).order_by(
|
||||
F("job__order").asc(nulls_last=True),
|
||||
"job__name",
|
||||
def get_queryset(self):
|
||||
return JobMember.active_member.get_all(slug=self.kwargs["slug"]).order_by(
|
||||
F("job__order").asc(nulls_last=True), "job__name"
|
||||
)
|
||||
active_job_group = JobGroup.objects.filter(slug=slug).first()
|
||||
|
||||
context = {
|
||||
"description": description,
|
||||
"job_members": job_members,
|
||||
"active_job_group": active_job_group,
|
||||
}
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
return render(request, "members/jobs.html", context)
|
||||
context["active_job_group"] = JobGroup.objects.filter(slug=self.kwargs["slug"]).first()
|
||||
|
||||
return context
|
||||
|
||||
|
||||
def members(request, role=None):
|
||||
for elem in Member.MemberRole:
|
||||
if role == elem.label.lower():
|
||||
members = Member.all_members.filter(role=elem.value)
|
||||
break
|
||||
else:
|
||||
members = Member.all_members.all()
|
||||
class MemberListView(ListView):
|
||||
allow_empty = False
|
||||
model = Member
|
||||
template_name = "members/members.html"
|
||||
|
||||
context = {
|
||||
"members": members,
|
||||
}
|
||||
def get_queryset(self):
|
||||
if self.kwargs["role"].capitalize() in [r.label for r in Member.MemberRole]:
|
||||
return Member.all_members.filter(role=self.kwargs["role"].capitalize()[:1])
|
||||
|
||||
return render(request, "members/members.html", context)
|
||||
return Member.all_members.all() if self.kwargs["role"] in "all" else None
|
||||
|
||||
|
||||
def profile(request, member_id=None):
|
||||
member = Member.all_members.filter(id=member_id).first()
|
||||
if not member:
|
||||
logger.info("Wrong member id '%s'", member_id)
|
||||
raise Http404("no member")
|
||||
class MemberDetailView(DetailView):
|
||||
model = Member
|
||||
template_name = "members/member.html"
|
||||
|
||||
active_jobs = JobMember.members.get_active_jobs(member_id)
|
||||
inactive_jobs = JobMember.members.get_inactive_jobs(member_id)
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context = {
|
||||
"member": member,
|
||||
"active_jobs": active_jobs,
|
||||
"inactive_jobs": inactive_jobs,
|
||||
}
|
||||
context["active_jobs"] = JobMember.members.get_active_jobs(self.object.id)
|
||||
context["inactive_jobs"] = JobMember.members.get_inactive_jobs(self.object.id)
|
||||
|
||||
return render(request, "members/member.html", context)
|
||||
return context
|
||||
|
||||
@@ -10,8 +10,7 @@ from .models import Event, FetMeeting, FileUpload, News, Post
|
||||
def make_fetmeeting(self, request, queryset):
|
||||
qs = self.get_queryset(request).filter(id=request.POST["_selected_action"]).first()
|
||||
|
||||
agenda_key = ep_create_new_pad(qs.slug + "-agenda")
|
||||
if not agenda_key:
|
||||
if not (agenda_key := ep_create_new_pad(qs.slug + "-agenda")):
|
||||
self.message_user(
|
||||
request,
|
||||
"Das Agenda konnte nicht erstellt werden.",
|
||||
@@ -19,8 +18,7 @@ def make_fetmeeting(self, request, queryset):
|
||||
)
|
||||
return
|
||||
|
||||
protocol_key = ep_create_new_pad(qs.slug + "-protocol")
|
||||
if not protocol_key:
|
||||
if not (protocol_key := ep_create_new_pad(qs.slug + "-protocol")):
|
||||
self.message_user(
|
||||
request,
|
||||
"Das Protokoll konnte nicht erstellt werden.",
|
||||
@@ -79,7 +77,7 @@ class PostAdmin(admin.ModelAdmin):
|
||||
)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if obj.author is None:
|
||||
if not obj.author:
|
||||
obj.author = request.user
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
|
||||
13
fet2020/posts/choices.py
Normal file
13
fet2020/posts/choices.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class PostType(models.TextChoices):
|
||||
NEWS = "N", "News"
|
||||
EVENT = "E", "Event"
|
||||
FETMEETING = "F", "FetMeeting"
|
||||
|
||||
|
||||
class Status(models.TextChoices):
|
||||
DRAFT = "10", "DRAFT"
|
||||
ONLY_INTERN = "15", "ONLY_INTERN"
|
||||
PUBLIC = "20", "PUBLIC"
|
||||
@@ -2,7 +2,8 @@ import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Case, Q, When
|
||||
from django.utils import timezone
|
||||
|
||||
from .choices import PostType, Status
|
||||
|
||||
|
||||
class PublishedManager(models.Manager):
|
||||
@@ -10,33 +11,36 @@ class PublishedManager(models.Manager):
|
||||
"""
|
||||
publish all posts with status 'PUBLIC'
|
||||
"""
|
||||
qs = self.get_queryset().filter(status="20") if public else self.get_queryset()
|
||||
return qs
|
||||
return self.get_queryset().filter(status=Status.PUBLIC) if public else self.get_queryset()
|
||||
|
||||
def published_all(self, public=True):
|
||||
"""
|
||||
publish all posts with status 'PUBLIC' and 'ONLY_INTERN'
|
||||
"""
|
||||
qs = self.get_queryset().filter(~Q(status="10")) if public else self.get_queryset()
|
||||
return qs
|
||||
return (
|
||||
self.get_queryset().filter(~Q(status=Status.DRAFT)) if public else self.get_queryset()
|
||||
)
|
||||
|
||||
|
||||
class PostManager(PublishedManager, models.Manager):
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
qs = qs.annotate(
|
||||
qs = (
|
||||
super()
|
||||
.get_queryset()
|
||||
.annotate(
|
||||
date=Case(
|
||||
When(post_type="N", then="public_date"),
|
||||
When(post_type="E", then="event_start__date"),
|
||||
When(post_type="F", then="event_start__date"),
|
||||
When(post_type=PostType.NEWS, then="public_date"),
|
||||
When(post_type=PostType.EVENT, then="event_start__date"),
|
||||
When(post_type=PostType.FETMEETING, then="event_start__date"),
|
||||
),
|
||||
)
|
||||
)
|
||||
return qs.order_by("-date", "-id")
|
||||
|
||||
def date_sorted_list(self, public=True):
|
||||
def date_sorted(self, public=True):
|
||||
return self.published(public)
|
||||
|
||||
def date_filtered_list(
|
||||
def date_filter(
|
||||
self,
|
||||
public=True,
|
||||
year=None,
|
||||
@@ -46,7 +50,7 @@ class PostManager(PublishedManager, models.Manager):
|
||||
qs_filter = Q()
|
||||
|
||||
if fet_meeting_only:
|
||||
qs_filter &= Q(post_type="F")
|
||||
qs_filter &= Q(post_type=PostType.FETMEETING)
|
||||
|
||||
if year:
|
||||
qs_filter &= Q(date__year=year)
|
||||
@@ -63,16 +67,16 @@ class ArticleManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().filter(Q(post_type="N") | Q(post_type="E"))
|
||||
qs = super().get_queryset().filter(Q(post_type=PostType.NEWS) | Q(post_type=PostType.EVENT))
|
||||
qs = qs.annotate(
|
||||
date=Case(
|
||||
When(post_type="N", then="public_date"),
|
||||
When(post_type="E", then="event_start__date"),
|
||||
When(post_type=PostType.NEWS, then="public_date"),
|
||||
When(post_type=PostType.EVENT, then="event_start__date"),
|
||||
),
|
||||
)
|
||||
return qs.order_by("-date", "-id")
|
||||
|
||||
def date_sorted_list(self, public=True):
|
||||
def date_sorted(self, public=True):
|
||||
return self.published(public)
|
||||
|
||||
def pinned(self, public=True):
|
||||
@@ -95,10 +99,12 @@ class ArticleManager(PublishedManager, models.Manager):
|
||||
|
||||
return (
|
||||
self.published(public)
|
||||
.filter(is_pinned=True)
|
||||
.filter(
|
||||
(Q(post_type="N") & Q(public_date__gt=post_date))
|
||||
| (Q(post_type="E") & Q(event_end__date__gt=event_date)),
|
||||
Q(is_pinned=True)
|
||||
& (
|
||||
(Q(post_type=PostType.NEWS) & Q(public_date__gt=post_date))
|
||||
| (Q(post_type=PostType.EVENT) & Q(event_end__date__gt=event_date))
|
||||
)
|
||||
)
|
||||
.first()
|
||||
)
|
||||
@@ -110,10 +116,10 @@ class NewsManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().filter(post_type="N")
|
||||
qs = super().get_queryset().filter(post_type=PostType.NEWS)
|
||||
qs = qs.annotate(
|
||||
date=Case(
|
||||
When(post_type="N", then="public_date"),
|
||||
When(post_type=PostType.NEWS, then="public_date"),
|
||||
),
|
||||
)
|
||||
return qs.order_by("-date")
|
||||
@@ -125,17 +131,21 @@ class AllEventManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().filter(Q(post_type="E") | Q(post_type="F"))
|
||||
qs = (
|
||||
super()
|
||||
.get_queryset()
|
||||
.filter(Q(post_type=PostType.EVENT) | Q(post_type=PostType.FETMEETING))
|
||||
)
|
||||
qs = qs.annotate(
|
||||
date=Case(
|
||||
When(post_type="E", then="event_start__date"),
|
||||
When(post_type="F", then="event_start__date"),
|
||||
When(post_type=PostType.EVENT, then="event_start__date"),
|
||||
When(post_type=PostType.FETMEETING, then="event_start__date"),
|
||||
),
|
||||
)
|
||||
return qs.order_by("-date")
|
||||
|
||||
def future_events(self, public=True):
|
||||
date_today = timezone.now()
|
||||
date_today = datetime.date.today()
|
||||
qs = self.published(public).filter(event_start__gt=date_today)
|
||||
return qs.reverse()
|
||||
|
||||
@@ -147,21 +157,21 @@ class EventManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().filter(post_type="E")
|
||||
qs = super().get_queryset().filter(post_type=PostType.EVENT)
|
||||
qs = qs.annotate(
|
||||
date=Case(
|
||||
When(post_type="E", then="event_start__date"),
|
||||
When(post_type=PostType.EVENT, then="event_start__date"),
|
||||
),
|
||||
)
|
||||
return qs.order_by("-date")
|
||||
|
||||
def future_events(self, public=True):
|
||||
date_today = timezone.now()
|
||||
date_today = datetime.date.today()
|
||||
qs = self.published(public).filter(event_start__gt=date_today)
|
||||
return qs.reverse()
|
||||
|
||||
def past_events(self, public=True):
|
||||
date_today = timezone.now()
|
||||
date_today = datetime.date.today()
|
||||
qs = self.published(public).filter(event_start__lt=date_today)
|
||||
return qs
|
||||
|
||||
@@ -172,20 +182,20 @@ class FetMeetingManager(PublishedManager, models.Manager):
|
||||
"""
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().filter(post_type="F")
|
||||
qs = super().get_queryset().filter(post_type=PostType.FETMEETING)
|
||||
qs = qs.annotate(
|
||||
date=Case(
|
||||
When(post_type="F", then="event_start__date"),
|
||||
When(post_type=PostType.FETMEETING, then="event_start__date"),
|
||||
),
|
||||
)
|
||||
return qs.order_by("-date")
|
||||
|
||||
def future_events(self):
|
||||
date_today = timezone.now()
|
||||
date_today = datetime.date.today()
|
||||
qs = self.published().filter(event_start__gt=date_today)
|
||||
return qs.reverse()
|
||||
|
||||
def past_events(self):
|
||||
date_today = timezone.now()
|
||||
date_today = datetime.date.today()
|
||||
qs = self.published().filter(event_start__lt=date_today)
|
||||
return qs
|
||||
|
||||
@@ -8,18 +8,12 @@ from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from core.models import CustomFlatPage
|
||||
from documents.api import (
|
||||
ep_create_new_pad,
|
||||
ep_get_html,
|
||||
ep_get_url,
|
||||
ep_pad_exists,
|
||||
ep_set_html,
|
||||
)
|
||||
from documents.api import ep_create_new_pad, ep_get_html, ep_get_url, ep_pad_exists, ep_set_html
|
||||
|
||||
from .choices import PostType, Status
|
||||
from .managers import (
|
||||
AllEventManager,
|
||||
ArticleManager,
|
||||
@@ -30,58 +24,34 @@ from .managers import (
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
request_logger = logging.getLogger("django.request")
|
||||
|
||||
|
||||
def create_pad_for_post(slug, item="agenda"):
|
||||
"""
|
||||
Create a Etherpad pad.
|
||||
|
||||
Return a Etherpad key.
|
||||
"""
|
||||
logger.info(f"Pad-Type: {item}")
|
||||
logger.info("Pad-Type: %s", item)
|
||||
|
||||
pad_id = slug + "-" + item
|
||||
if ep_create_new_pad(pad_id):
|
||||
if not ep_create_new_pad(pad_id):
|
||||
return ""
|
||||
|
||||
# Set template into the newly created pad if it exists.
|
||||
if page := CustomFlatPage.objects.filter(title__iexact=item).first():
|
||||
ep_set_html(pad_id, page.content)
|
||||
logger.info(f"Template is set. Template: {page.title}")
|
||||
logger.info("Template '%s' is set.", page.title)
|
||||
|
||||
return pad_id
|
||||
|
||||
return "#"
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
# Titel des Posts
|
||||
title = models.CharField(max_length=200)
|
||||
|
||||
subtitle = models.CharField(max_length=500, null=True, blank=True)
|
||||
# Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
|
||||
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||
# Ein Haupt Bild für den Post
|
||||
image = models.ImageField(null=True, blank=True)
|
||||
|
||||
tags = TaggableManager(blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Category"
|
||||
verbose_name_plural = "Categories"
|
||||
|
||||
|
||||
class Post(models.Model):
|
||||
# legacy id is for the posts from the old website
|
||||
legacy_id = models.IntegerField(null=True, blank=True)
|
||||
|
||||
title = models.CharField(verbose_name="Titel", max_length=200)
|
||||
subtitle = models.CharField(max_length=500, null=True, blank=True)
|
||||
subtitle = models.CharField(max_length=500, blank=True, default="")
|
||||
tags = TaggableManager(blank=True)
|
||||
|
||||
# Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
|
||||
slug = models.SlugField(unique=True, blank=True)
|
||||
|
||||
body = models.TextField(null=True, blank=True)
|
||||
body = models.TextField(blank=True, default="")
|
||||
image = models.ImageField(null=True, blank=True)
|
||||
|
||||
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
|
||||
@@ -92,19 +62,10 @@ class Post(models.Model):
|
||||
default=timezone.now,
|
||||
)
|
||||
|
||||
__choices = [("N", _("News")), ("E", _("Event")), ("F", _("FetMeeting"))]
|
||||
post_type = models.CharField(max_length=1, choices=__choices, editable=True)
|
||||
|
||||
class Status(models.TextChoices):
|
||||
DRAFT = "10", _("DRAFT")
|
||||
ONLY_INTERN = "15", _("ONLY_INTERN")
|
||||
PUBLIC = "20", _("PUBLIC")
|
||||
post_type = models.CharField(max_length=1, choices=PostType.choices, editable=True)
|
||||
|
||||
status = models.CharField(
|
||||
verbose_name="Status",
|
||||
max_length=2,
|
||||
choices=Status.choices,
|
||||
default=Status.DRAFT,
|
||||
verbose_name="Status", max_length=2, choices=Status.choices, default=Status.DRAFT
|
||||
)
|
||||
|
||||
# post is pinned at main page
|
||||
@@ -113,22 +74,18 @@ class Post(models.Model):
|
||||
# addional infos for events
|
||||
event_start = models.DateTimeField(verbose_name="Event Start", null=True, blank=True)
|
||||
event_end = models.DateTimeField(verbose_name="Event Ende", null=True, blank=True)
|
||||
event_place = models.CharField(max_length=200, null=True, blank=True)
|
||||
event_place = models.CharField(max_length=200, blank=True, default="")
|
||||
|
||||
# protocol for fet meeting
|
||||
has_protocol = models.BooleanField(default=False)
|
||||
has_agenda = models.BooleanField(default=False)
|
||||
protocol_key = models.CharField(max_length=200, null=True, blank=True)
|
||||
agenda_key = models.CharField(max_length=200, null=True, blank=True)
|
||||
protocol_key = models.CharField(max_length=200, blank=True, default="")
|
||||
agenda_key = models.CharField(max_length=200, blank=True, default="")
|
||||
|
||||
# TimeStamps
|
||||
date_modified = models.DateTimeField(auto_now=True)
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
# useless (-_-)
|
||||
legacy_rubrik_id = models.IntegerField(null=True, blank=True)
|
||||
imported_from = models.CharField(max_length=200, null=True, blank=True)
|
||||
|
||||
# Managers
|
||||
objects = PostManager()
|
||||
articles = ArticleManager()
|
||||
@@ -153,48 +110,45 @@ class Post(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse("posts:post", kwargs={"slug": self.slug})
|
||||
|
||||
# "#" for backward compatibility
|
||||
_possible_empty_key_value = ["#", "", None]
|
||||
|
||||
@property
|
||||
def agenda_html(self) -> str | None:
|
||||
"Agenda HTML from Etherpad Pad"
|
||||
if self.agenda_key in [None, "#"]:
|
||||
if self.agenda_key in self._possible_empty_key_value:
|
||||
return None
|
||||
|
||||
return ep_get_html(self.agenda_key)
|
||||
|
||||
@agenda_html.setter
|
||||
def agenda_html(self, value: str) -> str | None:
|
||||
if self.agenda_key in self._possible_empty_key_value:
|
||||
self.create_agenda_key()
|
||||
|
||||
if not value or not self.agenda_key:
|
||||
return None
|
||||
|
||||
ep_set_html(self.agenda_key, value)
|
||||
logger.info("Set agenda etherpad '%s' for post '%s'.", self.agenda_key, self.slug)
|
||||
return value
|
||||
|
||||
@property
|
||||
def protocol_html(self) -> str | None:
|
||||
"Protocol HTML from Etherpad Pad"
|
||||
if self.protocol_key in [None, "#"]:
|
||||
if self.protocol_key in self._possible_empty_key_value:
|
||||
return None
|
||||
|
||||
return ep_get_html(self.protocol_key)
|
||||
|
||||
@agenda_html.setter
|
||||
def agenda_html(self, value: str) -> str | None:
|
||||
if self.agenda_key is None:
|
||||
self.create_agenda_key()
|
||||
|
||||
if value is None or self.agenda_key in [None, "#"]:
|
||||
return None
|
||||
|
||||
ep_set_html(self.agenda_key, value)
|
||||
request_logger.info("Set agenda etherpad. Post: %s. Key: %s", self.slug, self.agenda_key)
|
||||
return value
|
||||
|
||||
@protocol_html.setter
|
||||
def protocol_html(self, value: str) -> str | None:
|
||||
if self.protocol_key is None:
|
||||
if self.protocol_key in self._possible_empty_key_value:
|
||||
self.create_protocol_key()
|
||||
|
||||
if value is None or self.protocol_key in [None, "#"]:
|
||||
if not value or not self.protocol_key:
|
||||
return None
|
||||
|
||||
ep_set_html(self.protocol_key, value)
|
||||
request_logger.info(
|
||||
"Set protocol etherpad. Post: %s. Key: %s",
|
||||
self.slug,
|
||||
self.protocol_key,
|
||||
)
|
||||
logger.info("Set protocol etherpad '%s' for post '%s'.", self.protocol_key, self.slug)
|
||||
return value
|
||||
|
||||
_agenda_filename = None
|
||||
@@ -202,15 +156,15 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def agenda_url(self) -> str | None:
|
||||
if self.has_agenda is not True:
|
||||
if not self.has_agenda:
|
||||
self._agenda_url = None
|
||||
self._agenda_filename = None
|
||||
return self._agenda_url
|
||||
|
||||
if self._agenda_url is not None:
|
||||
if self._agenda_url:
|
||||
return self._agenda_url
|
||||
|
||||
if (url := ep_get_url(self.agenda_key)) not in [None, "#"]:
|
||||
if url := ep_get_url(self.agenda_key):
|
||||
self._agenda_url = url
|
||||
self._agenda_filename = self.slug + "-agenda.pdf"
|
||||
else:
|
||||
@@ -221,7 +175,7 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def agenda_filename(self) -> str | None:
|
||||
if self._agenda_filename is not None:
|
||||
if self._agenda_filename:
|
||||
return self._agenda_filename
|
||||
|
||||
if self.has_agenda and self.agenda_url:
|
||||
@@ -234,15 +188,15 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def protocol_url(self) -> str | None:
|
||||
if self.has_protocol is not True:
|
||||
if not self.has_protocol:
|
||||
self._protocol_url = None
|
||||
self._protocol_filename = None
|
||||
return self._protocol_url
|
||||
|
||||
if self._protocol_url is not None:
|
||||
if self._protocol_url:
|
||||
return self._protocol_url
|
||||
|
||||
if (url := ep_get_url(self.protocol_key)) not in [None, "#"]:
|
||||
if url := ep_get_url(self.protocol_key):
|
||||
self._protocol_url = url
|
||||
self._protocol_filename = self.slug + "-protokoll.pdf"
|
||||
else:
|
||||
@@ -253,7 +207,7 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def protocol_filename(self) -> str | None:
|
||||
if self._protocol_filename is not None:
|
||||
if self._protocol_filename:
|
||||
return self._protocol_filename
|
||||
|
||||
if self.has_protocol and self.protocol_url:
|
||||
@@ -290,14 +244,9 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def imageurl(self) -> str:
|
||||
"""
|
||||
returns the url to the image
|
||||
"""
|
||||
if self.image:
|
||||
return self.image.url
|
||||
|
||||
# return default image
|
||||
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
return (
|
||||
self.image.url if self.image else settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.event_end and self.event_end < self.event_start:
|
||||
@@ -307,7 +256,7 @@ class Post(models.Model):
|
||||
|
||||
@property
|
||||
def published(self):
|
||||
return self.status == self.Status.PUBLIC
|
||||
return self.status == Status.PUBLIC
|
||||
|
||||
|
||||
class News(Post):
|
||||
@@ -372,14 +321,17 @@ class FetMeeting(Event):
|
||||
if not self.slug:
|
||||
self.slug = self.__get_slug()
|
||||
|
||||
if ep_pad_exists(self.agenda_key) is not True or self.slug not in self.agenda_key:
|
||||
if not ep_pad_exists(self.agenda_key) or self.agenda_key in self._possible_empty_key_value:
|
||||
self.create_agenda_key()
|
||||
if self.agenda_key not in [None, "#"]:
|
||||
if self.agenda_key:
|
||||
self.has_agenda = True
|
||||
|
||||
if ep_pad_exists(self.protocol_key) is not True or self.slug not in self.protocol_key:
|
||||
if (
|
||||
not ep_pad_exists(self.protocol_key)
|
||||
or self.protocol_key in self._possible_empty_key_value
|
||||
):
|
||||
self.create_protocol_key()
|
||||
if self.protocol_key not in [None, "#"]:
|
||||
if self.protocol_key:
|
||||
self.has_protocol = True
|
||||
|
||||
if not self.post_type:
|
||||
@@ -393,7 +345,7 @@ class FetMeeting(Event):
|
||||
self.event_end = self.event_start + timedelta(hours=2)
|
||||
|
||||
# set FET Meeting always public
|
||||
self.status = self.Status.PUBLIC
|
||||
self.status = Status.PUBLIC
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class PostIndex(indexes.SearchIndex, indexes.Indexable):
|
||||
return Post
|
||||
|
||||
def index_queryset(self, using=None):
|
||||
return self.get_model().objects.date_sorted_list(public=False)
|
||||
return self.get_model().objects.date_sorted(public=False)
|
||||
|
||||
def prepare_date(self, obj):
|
||||
if obj.post_type == "N":
|
||||
|
||||
@@ -39,7 +39,7 @@ class PostTestCase(TestCase):
|
||||
post.event_start = timezone.now() - timedelta(1)
|
||||
post.save()
|
||||
|
||||
post_list = Post.objects.date_sorted_list(public=False)
|
||||
post_list = Post.objects.date_sorted(public=False)
|
||||
self.assertEqual(post_list[0].title, "zukünftiges FET Fest")
|
||||
self.assertEqual(post_list[1].title, "Informationen zur ÖH Wahl")
|
||||
self.assertEqual(post_list[2].title, "vergangenes FET Fest")
|
||||
|
||||
@@ -15,6 +15,7 @@ from documents.etherpadlib import add_ep_cookie
|
||||
from fet2020.utils import add_log_action
|
||||
from members.models import Member
|
||||
|
||||
from .choices import PostType
|
||||
from .forms import (
|
||||
EventUpdateForm,
|
||||
FetMeetingCreateForm,
|
||||
@@ -35,7 +36,7 @@ def index(request):
|
||||
if request.method == "POST":
|
||||
form = PostSearchForm(request.POST)
|
||||
if form.is_valid():
|
||||
posts = Post.objects.date_filtered_list(
|
||||
posts = Post.objects.date_filter(
|
||||
public_only,
|
||||
form.cleaned_data["year"],
|
||||
form.cleaned_data["month"],
|
||||
@@ -44,7 +45,7 @@ def index(request):
|
||||
else:
|
||||
# If no input, all posts are shown.
|
||||
form = PostSearchForm()
|
||||
posts = Post.objects.date_filtered_list(public_only)
|
||||
posts = Post.objects.date_filter(public_only)
|
||||
|
||||
context = {
|
||||
"formset": form,
|
||||
@@ -92,7 +93,7 @@ class PostDetailView(DetailView):
|
||||
try:
|
||||
response = add_ep_cookie(request, response)
|
||||
except Exception as e:
|
||||
logger.info("Etherpad Server doesn't work. Error: %s", e)
|
||||
logger.info("Etherpad Server not working. Error: %s", e)
|
||||
|
||||
return response
|
||||
|
||||
@@ -134,9 +135,9 @@ class PostDetailView(DetailView):
|
||||
|
||||
def get_template_names(self):
|
||||
template_name = "posts/news/detail.html"
|
||||
if self.object.post_type == "E":
|
||||
if self.object.post_type == PostType.EVENT:
|
||||
template_name = "posts/event/detail.html"
|
||||
elif self.object.post_type == "F":
|
||||
elif self.object.post_type == PostType.FETMEETING:
|
||||
template_name = "posts/fetmeeting/detail.html"
|
||||
|
||||
return template_name
|
||||
@@ -145,7 +146,7 @@ class PostDetailView(DetailView):
|
||||
"""
|
||||
Helper function for getting previous post
|
||||
"""
|
||||
posts = Post.objects.date_sorted_list(self.public_only).filter(
|
||||
posts = Post.objects.date_sorted(self.public_only).filter(
|
||||
post_type=self.object.post_type,
|
||||
)
|
||||
qs = posts.filter(
|
||||
@@ -165,7 +166,7 @@ class PostDetailView(DetailView):
|
||||
Helper function for getting next post
|
||||
"""
|
||||
posts = (
|
||||
Post.objects.date_sorted_list(self.public_only)
|
||||
Post.objects.date_sorted(self.public_only)
|
||||
.filter(post_type=self.object.post_type)
|
||||
.reverse()
|
||||
)
|
||||
@@ -187,9 +188,9 @@ class PostUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
def form_valid(self, form):
|
||||
model = "news"
|
||||
if self.object.post_type == "E":
|
||||
if self.object.post_type == PostType.EVENT:
|
||||
model = "event"
|
||||
elif self.object.post_type == "F":
|
||||
elif self.object.post_type == PostType.FETMEETING:
|
||||
model = "fetmeeting"
|
||||
add_log_action(self.request, form, "posts", model, False)
|
||||
|
||||
@@ -197,9 +198,9 @@ class PostUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
def get_form_class(self):
|
||||
form_class = NewsUpdateForm
|
||||
if self.object.post_type == "E":
|
||||
if self.object.post_type == PostType.EVENT:
|
||||
form_class = EventUpdateForm
|
||||
elif self.object.post_type == "F":
|
||||
elif self.object.post_type == PostType.FETMEETING:
|
||||
form_class = FetMeetingUpdateForm
|
||||
|
||||
return form_class
|
||||
@@ -229,9 +230,9 @@ class PostUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
def get_template_names(self):
|
||||
template_name = "posts/news/update.html"
|
||||
if self.object.post_type == "E":
|
||||
if self.object.post_type == PostType.EVENT:
|
||||
template_name = "posts/event/update.html"
|
||||
elif self.object.post_type == "F":
|
||||
elif self.object.post_type == PostType.FETMEETING:
|
||||
template_name = "posts/fetmeeting/update.html"
|
||||
|
||||
return template_name
|
||||
@@ -307,10 +308,8 @@ def show_pdf(request, html, filename):
|
||||
idx = html.index("<body>")
|
||||
html = html[:idx] + rendered + html[idx:]
|
||||
|
||||
pdf = render_to_pdf(html)
|
||||
|
||||
if not pdf:
|
||||
raise Http404("can't create pdf file")
|
||||
if not (pdf := render_to_pdf(html)):
|
||||
raise Http404("can't create pdf file.")
|
||||
|
||||
response = HttpResponse(pdf, content_type="application/pdf")
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<h1 class="page-title">Fotos</h1>
|
||||
|
||||
<section class="text-gray-800 dark:text-gray-200 my-2 sm:my-4">
|
||||
{% if album.status == album.DRAFT %}
|
||||
{% if album.status == album.Status.DRAFT %}
|
||||
<h2 class="text-gray-900 dark:text-gray-100 text-lg font-medium tracking-wide"><i class="fa-solid fa-eye-slash text-gray-600 dark:text-gray-300 mr-2" title="Album ist nicht öffentlich!"></i>{{ album.title }}</h2>
|
||||
{% else %}
|
||||
<h2 class="text-gray-900 dark:text-gray-100 text-lg font-medium tracking-wide">{{ album.title }}</h2>
|
||||
@@ -34,7 +34,7 @@
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if request.user.is_authenticated and album.id %}
|
||||
<a href="{% url 'admin:gallery_album_change' album.id %}" class="page-subtitle block btn-small btn-primary w-full sm:w-max sm:mr-0 sm:ml-auto">
|
||||
<i class="fa-regular fa-folder-open mr-1"></i>Album bearbeiten
|
||||
</a>
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
<div id="links" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7 justify-items-center gap-3">
|
||||
{% for image in images %}
|
||||
{% for image in object.images %}
|
||||
<a id="{{ image.title }}" href="{{ image.image_url }}" title="{{ image.title }}" class="block max-w-xs sm:max-w-none">
|
||||
{% if image.thumb_url %}
|
||||
<img src="{{ image.thumb_url }}" alt="{{ image.title }}" class="rounded-sm">
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 justify-items-center gap-4">
|
||||
{% for album in albums %}
|
||||
{% if request.user.is_authenticated and album.status == album.DRAFT %}
|
||||
{% if request.user.is_authenticated and album.status == album.Status.DRAFT %}
|
||||
<a href="{% url 'gallery:album_draft' album.slug %}" class="block max-w-xs sm:max-w-none">
|
||||
<img src="{{ album.thumbnail }}" class="rounded">
|
||||
<img src="{{ album.get_album_thumbnail }}" class="rounded">
|
||||
<h2 class="px-2 text-proprietary-dark dark:text-sky-300">{{ album.title }}</h2>
|
||||
<h3 class="px-2 text-sm text-proprietary dark:text-sky-400 font-normal"><i class="fa-solid fa-calendar-day mr-1"></i>{{ album.event_date }}</h3>
|
||||
</a>
|
||||
{% elif album.status == album.PUBLIC %}
|
||||
{% elif album.status == album.Status.PUBLIC %}
|
||||
<a href="{% url 'gallery:album' album.slug %}" class="block max-w-xs sm:max-w-none">
|
||||
<img src="{{ album.thumbnail }}" class="rounded">
|
||||
<img src="{{ album.get_album_thumbnail }}" class="rounded">
|
||||
<h2 class="px-2 text-proprietary-dark dark:text-sky-300">{{ album.title }}</h2>
|
||||
<h3 class="px-2 text-sm text-proprietary dark:text-sky-400 font-normal"><i class="fa-solid fa-calendar-day mr-1"></i>{{ album.event_date }}</h3>
|
||||
</a>
|
||||
|
||||
@@ -106,13 +106,13 @@
|
||||
{% endblock member_content %}
|
||||
{% endif %}
|
||||
|
||||
{% if members %}
|
||||
{% if member_list %}
|
||||
<!-- show all, active or pension members -->
|
||||
{% block members_content %}
|
||||
{% endblock members_content %}
|
||||
{% endif %}
|
||||
|
||||
{% if job_members %}
|
||||
{% if jobmember_list %}
|
||||
<!-- show job lists in a job group -->
|
||||
{% block jobs_content %}
|
||||
{% endblock jobs_content %}
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
{% block jobs_content %}
|
||||
<div class="db-page-content">
|
||||
<h2>{{ active_job_group.name }}</h2>
|
||||
{% if description %}
|
||||
{{ description|safe }}
|
||||
{% if active_job_group.description %}
|
||||
{{ active_job_group.description|safe }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% regroup job_members by job.name as all_jobmem_list %}
|
||||
{% regroup jobmember_list by job.name as jobmem_list %}
|
||||
|
||||
{% for jobmem in all_jobmem_list %}
|
||||
{% for jobmem in jobmem_list %}
|
||||
<article id="{{ jobmem.list.0.job.slug }}" class="members-article">
|
||||
<a href="#{{ jobmem.list.0.job.slug }}" class="title">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<article class="members-article">
|
||||
<div class="members-listing">
|
||||
{% for member in members %}
|
||||
{% for member in member_list %}
|
||||
<figure>
|
||||
<a href="{{ member.get_absolute_url }}">
|
||||
<img loading="lazy" src="{{ member.thumb_url }}" alt="Portraitfoto von {{ member.firstname }}" class="w-36 h-36 bg-white">
|
||||
|
||||
Reference in New Issue
Block a user