Add possiblity to share gallerie with public link
All checks were successful
Docker / build (release) Successful in 8s

This commit is contained in:
krek0 2026-05-10 12:57:14 +02:00
parent ed28d4a9c4
commit 2a409b54f7
12 changed files with 206 additions and 48 deletions

View file

@ -3,24 +3,23 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import uuid
import zipfile
from io import BytesIO
from pathlib import Path
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.core.mail import mail_admins
from django.db import transaction
from django.http import HttpResponse
from django.http import JsonResponse
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.utils.text import slugify
from django.views import View
from django.views.generic.dates import ArchiveIndexView, YearArchiveView
from django.views.generic.detail import DetailView
from django.views.generic.edit import DeleteView, FormView
from django.conf import settings
from PIL import Image
from django.contrib.auth import get_user_model
@ -150,13 +149,30 @@ class TagDetail(LoginRequiredMixin, DetailView):
return context
class GalleryDetailView(LoginRequiredMixin, DetailView):
def _allowed_gallery_ids(request):
try:
ids = set(request.get_signed_cookie("public_galleries", default="").split(","))
except Exception:
ids = set()
ids.discard("")
return ids
class GalleryDetailView(DetailView):
"""
Gallery detail view to filter on photo owner
"""
model = Gallery
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
gallery = self.get_object()
if str(gallery.id) not in _allowed_gallery_ids(request):
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.get_full_path())
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -185,46 +201,71 @@ class GalleryDetailView(LoginRequiredMixin, DetailView):
if "owner" in self.kwargs:
context["photos"] = context["photos"].filter(owner__id=self.kwargs["owner"])
# Increment the photo view count
if self.object.public_token:
public_path = reverse("photologue:pl-gallery-public", args=[self.object.public_token])
context["public_url"] = self.request.build_absolute_uri(public_path)
context["photos"].update(view_count=F("view_count") + 1)
return context
class GalleryDownload(LoginRequiredMixin, DetailView):
### IN FUTURE, PUT IT as Django Task
class GalleryDownload(DetailView):
model = Gallery
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
gallery = self.get_object()
if str(gallery.id) not in _allowed_gallery_ids(request):
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.get_full_path())
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
"""
Download a zip file of the gallery on GET request.
"""
# Create zip file with pictures
gallery = self.get_object()
gallery_year = os.path.join("/photos/", str(gallery.date_start.year))
gallery_zip = os.path.join(gallery_year, (gallery.slug + ".zip"))
with open(settings.MEDIA_ROOT + gallery_zip, "wb") as zip_bytes:
zip_file = zipfile.ZipFile(zip_bytes, "w")
buffer = BytesIO()
with zipfile.ZipFile(buffer, "w") as zf:
for photo in gallery.photos.filter(is_public=True):
filename = os.path.basename(os.path.normpath(photo.image.path))
zip_file.write(photo.image.path, filename)
zip_file.close()
filename = os.path.basename(photo.image.name)
zf.write(photo.image.path, filename)
buffer.seek(0)
response = HttpResponse(buffer, content_type="application/zip")
response["Content-Disposition"] = f'attachment; filename="{gallery.slug}.zip"'
return response
# Return the path to it
return redirect(
(settings.MEDIA_URL + str(gallery_zip)).replace("\\", "/")
) # windows fix
# Return zip file
class GalleryPublicView(View):
def get(self, request, token):
gallery = get_object_or_404(Gallery, public_token=token)
response = redirect("photologue:pl-gallery", slug=gallery.slug)
if not request.user.is_authenticated:
try:
existing = set(request.get_signed_cookie("public_galleries", default="").split(","))
except Exception:
existing = set()
existing.discard("")
existing.add(str(gallery.id))
response.set_signed_cookie(
"public_galleries", ",".join(existing),
max_age=86400 * 30, httponly=True, samesite="Lax",
)
return response
# response = HttpResponse(
# byte_data.getvalue(), content_type="application/x-zip-compressed"
# )
# response["Content-Disposition"] = f"attachment; filename={gallery.slug}.zip"
# return response
class GalleryTokenView(LoginRequiredMixin, View):
def post(self, request, slug):
if not request.user.is_staff:
from django.core.exceptions import PermissionDenied
raise PermissionDenied
gallery = get_object_or_404(Gallery, slug=slug)
action = request.POST.get("action")
if action == "generate":
gallery.public_token = uuid.uuid4()
gallery.save()
elif action == "revoke":
gallery.public_token = None
gallery.save()
return redirect(reverse("photologue:pl-gallery", args=[slug]))
class GalleryUpload(PermissionRequiredMixin, FormView):