Unify public and private gallery views, add author tabs and download
All checks were successful
Docker / build (release) Successful in 8s

This commit is contained in:
krek0 2026-05-07 17:22:06 +02:00
parent 317bad9068
commit 993421f52a
3 changed files with 36 additions and 13 deletions

View file

@ -69,18 +69,26 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if gallery.description %}<p>{{ gallery.description|linebreaksbr }}</p>{% endif %} {% if gallery.description %}<p>{{ gallery.description|linebreaksbr }}</p>{% endif %}
<div class="card"> <div class="card">
{% if not guest_mode %} {% if owners %}
<div class="card-header pb-0 border-bottom-0"> <div class="card-header pb-0 border-bottom-0">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"> <li class="nav-item">
{% if guest_mode %}
{% url 'photologue:pl-gallery-public' gallery.public_token as url %}
{% else %}
{% url 'photologue:pl-gallery' gallery.slug as url %} {% url 'photologue:pl-gallery' gallery.slug as url %}
{% endif %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"> <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
{% trans "All pictures" %} {% trans "All pictures" %}
</a> </a>
</li> </li>
{% for owner in owners %} {% for owner in owners %}
<li class="nav-item"> <li class="nav-item">
{% if guest_mode %}
{% url 'photologue:pl-gallery-public-owner' token=gallery.public_token owner=owner.id as url %}
{% else %}
{% url 'photologue:pl-gallery-owner' slug=gallery.slug owner=owner.id as url %} {% url 'photologue:pl-gallery-owner' slug=gallery.slug owner=owner.id as url %}
{% endif %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"> <a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
{% if owner.get_full_name %}{{ owner.get_full_name }}{% else %}{{ owner.username }}{% endif %} {% if owner.get_full_name %}{{ owner.get_full_name }}{% else %}{{ owner.username }}{% endif %}
</a> </a>
@ -101,9 +109,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
{% if not guest_mode %}
<div class="card-footer"> <div class="card-footer">
{% if guest_mode %}
<a href="{% url 'photologue:pl-gallery-public-download' gallery.public_token %}" class="btn btn-secondary btn-sm">
{% else %}
<a href="{% url 'photologue:pl-gallery-download' gallery.slug %}" class="btn btn-secondary btn-sm"> <a href="{% url 'photologue:pl-gallery-download' gallery.slug %}" class="btn btn-secondary btn-sm">
{% endif %}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/> <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/> <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
@ -111,6 +122,5 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans 'Download all gallery' %} {% trans 'Download all gallery' %}
</a> </a>
</div> </div>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -9,6 +9,7 @@ from .views import (
GalleryArchiveIndexView, GalleryArchiveIndexView,
GalleryDetailView, GalleryDetailView,
GalleryDownload, GalleryDownload,
GalleryPublicDownload,
GalleryPublicView, GalleryPublicView,
GalleryTokenView, GalleryTokenView,
GalleryUpload, GalleryUpload,
@ -47,5 +48,7 @@ urlpatterns = [
path("photo/<int:pk>/uncensor/", PhotoUncensorView.as_view(), name="pl-photo-uncensor"), path("photo/<int:pk>/uncensor/", PhotoUncensorView.as_view(), name="pl-photo-uncensor"),
path("upload/", GalleryUpload.as_view(), name="pl-gallery-upload"), path("upload/", GalleryUpload.as_view(), name="pl-gallery-upload"),
path("share/<uuid:token>/", GalleryPublicView.as_view(), name="pl-gallery-public"), path("share/<uuid:token>/", GalleryPublicView.as_view(), name="pl-gallery-public"),
path("share/<uuid:token>/<int:owner>/", GalleryPublicView.as_view(), name="pl-gallery-public-owner"),
path("share/<uuid:token>/download/", GalleryPublicDownload.as_view(), name="pl-gallery-public-download"),
path("gallery/<slug:slug>/token/", GalleryTokenView.as_view(), name="pl-gallery-token"), path("gallery/<slug:slug>/token/", GalleryTokenView.as_view(), name="pl-gallery-token"),
] ]

View file

@ -221,29 +221,39 @@ class GalleryDownload(LoginRequiredMixin, DetailView):
# return response # return response
class GalleryPublicView(DetailView): class GalleryPublicView(GalleryDetailView):
model = Gallery
template_name = "photologue/gallery_detail.html"
def get_object(self): def get_object(self):
return get_object_or_404(Gallery, public_token=self.kwargs["token"]) return get_object_or_404(Gallery, public_token=self.kwargs["token"])
def get(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated: if request.user.is_authenticated:
gallery = self.get_object() gallery = self.get_object()
return redirect("photologue:pl-gallery", slug=gallery.slug) return redirect("photologue:pl-gallery", slug=gallery.slug)
request.guest_mode = True # Bypass LoginRequiredMixin
return super().get(request, *args, **kwargs) return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["photos"] = self.object.photos.filter(is_public=True).select_related("owner")
context["owners"] = []
context["guest_mode"] = True context["guest_mode"] = True
context["photos"].update(view_count=F("view_count") + 1) context.pop("can_resolve_censorship", None)
context.pop("public_url", None)
return context return context
class GalleryPublicDownload(View):
def get(self, request, token):
gallery = get_object_or_404(Gallery, public_token=token)
buffer = BytesIO()
with zipfile.ZipFile(buffer, "w") as zf:
for photo in gallery.photos.filter(is_public=True):
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
class GalleryTokenView(LoginRequiredMixin, View): class GalleryTokenView(LoginRequiredMixin, View):
def post(self, request, slug): def post(self, request, slug):
if not request.user.is_staff: if not request.user.is_staff: