From 1cdd1dce2687e2aef4dc859397fd76e6791d31db Mon Sep 17 00:00:00 2001 From: krek0 Date: Mon, 11 May 2026 15:49:46 +0200 Subject: [PATCH] Add configurable per-gallery public access --- photo21/templates/base.html | 2 + photo21/views.py | 22 ++++++- photologue/admin.py | 3 +- photologue/locale/fr/LC_MESSAGES/django.po | 20 +++---- ...lter_photo_options_gallery_public_token.py | 22 +++++++ photologue/models.py | 5 ++ .../lightgallery/plugins/admin/lg-admin.js | 1 - .../templates/photologue/gallery_detail.html | 30 +++++----- photologue/urls.py | 2 + photologue/views.py | 57 ++++++++++++++----- 10 files changed, 119 insertions(+), 45 deletions(-) create mode 100644 photologue/migrations/0009_alter_photo_options_gallery_public_token.py diff --git a/photo21/templates/base.html b/photo21/templates/base.html index 91f63cb..ccd17c2 100644 --- a/photo21/templates/base.html +++ b/photo21/templates/base.html @@ -37,6 +37,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% for photo in photos %} - + {{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %}{% if photo.owner.get_full_name %} - {{ photo.owner.get_full_name }}{% else %} - {{ photo.owner.username }}{% endif %}{% if photo.license %} - {{ photo.license }}{% endif %}{% if not photo.is_public %} - !PRIVATE!{% endif %} {% endfor %} diff --git a/photologue/urls.py b/photologue/urls.py index 53379e4..0acc12d 100644 --- a/photologue/urls.py +++ b/photologue/urls.py @@ -9,6 +9,7 @@ from .views import ( GalleryArchiveIndexView, GalleryDetailView, GalleryDownload, + GalleryPublicToggleView, GalleryUpload, GalleryYearArchiveView, PhotoDeleteView, @@ -44,4 +45,5 @@ urlpatterns = [ path("photo//report/", PhotoReportView.as_view(), name="pl-photo-report"), path("photo//uncensor/", PhotoUncensorView.as_view(), name="pl-photo-uncensor"), path("upload/", GalleryUpload.as_view(), name="pl-gallery-upload"), + path("gallery//toggle-public/", GalleryPublicToggleView.as_view(), name="pl-gallery-toggle-public"), ] diff --git a/photologue/views.py b/photologue/views.py index 0cf9ab3..6af85fd 100644 --- a/photologue/views.py +++ b/photologue/views.py @@ -4,23 +4,23 @@ import os 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.conf import settings 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.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 @@ -93,10 +93,18 @@ class PhotoDeleteView(LoginRequiredMixin, DeleteView): return reverse_lazy("photologue:pl-gallery", args=[slug]) -class PhotoReportView(LoginRequiredMixin, DetailView): +class PhotoReportView(DetailView): model = Photo template_name = "photologue/photo_confirm_report.html" + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + photo = self.get_object() + if not photo.galleries.filter(is_public=True).exists(): + from django.contrib.auth.views import redirect_to_login + return redirect_to_login(request.get_full_path()) + return super().dispatch(request, *args, **kwargs) + def post(self, request, *args, **kwargs): """ Make photo private on POST. @@ -115,9 +123,10 @@ class PhotoReportView(LoginRequiredMixin, DetailView): url = request.build_absolute_uri(url) # Send mail to managers + reporter = request.user.username if request.user.is_authenticated else "Anonymous (public link)" mail_admins( subject=f"Abuse report for photo id {photo.pk}", - message=f"{self.request.user.username} reported an abuse for `{photo.title}`: {url}#lg=1&slide={photo.pk}", + message=f"{reporter} reported an abuse for `{photo.title}`: {url}#lg=1&slide={photo.pk}", ) # Redirect to gallery @@ -150,13 +159,21 @@ class TagDetail(LoginRequiredMixin, DetailView): return context -class GalleryDetailView(LoginRequiredMixin, DetailView): +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: + self.object = self.get_object() + if not self.object.is_public: + 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) @@ -192,10 +209,18 @@ class GalleryDetailView(LoginRequiredMixin, DetailView): return context -class GalleryDownload(LoginRequiredMixin, DetailView): +class GalleryDownload(DetailView): ### IN FUTURE, PUT IT as Django Task model = Gallery + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + self.object = self.get_object() + if not self.object.is_public: + 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. @@ -218,13 +243,17 @@ class GalleryDownload(LoginRequiredMixin, DetailView): return redirect( (settings.MEDIA_URL + str(gallery_zip)).replace("\\", "/") ) # windows fix - # Return zip file - # response = HttpResponse( - # byte_data.getvalue(), content_type="application/x-zip-compressed" - # ) - # response["Content-Disposition"] = f"attachment; filename={gallery.slug}.zip" - # return response + +class GalleryPublicToggleView(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) + gallery.is_public = not gallery.is_public + gallery.save() + return redirect(reverse("photologue:pl-gallery", args=[slug])) class GalleryUpload(PermissionRequiredMixin, FormView):