# Copyright (C) 2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import os import zipfile from io import BytesIO from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.core.mail import mail_managers from django.db import IntegrityError from django.http import HttpResponse from django.urls import reverse_lazy from django.utils.text import slugify from django.views.generic.detail import DetailView from django.views.generic.edit import FormView from photologue.models import Gallery, Photo from photologue.views import GalleryArchiveIndexView, GalleryYearArchiveView from taggit.models import Tag from PIL import Image from .forms import UploadForm from .models import PhotoExtended class TagDetail(LoginRequiredMixin, DetailView): model = Tag def get_context_data(self, **kwargs): """ Insert the single object into the context dict. """ current_tag = self.get_object().slug context = super().get_context_data(**kwargs) context['galleries'] = Gallery.objects.on_site().is_public() \ .filter(extended__tags__slug=current_tag) \ .order_by('-extended__date_start') return context class CustomGalleryArchiveIndexView(GalleryArchiveIndexView): """ Override to use event date """ date_field = 'extended__date_start' uses_datetime_field = False # Fix related object access class CustomGalleryYearArchiveView(GalleryYearArchiveView): """ Override to use event date """ date_field = 'extended__date_start' uses_datetime_field = False # Fix related object access class CustomGalleryDetailView(DetailView): """ Custom gallery detail view to filter on photo owner """ queryset = Gallery.objects.on_site().is_public() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Query with extended and owner to reduce database lag context['photos'] = self.object.public().select_related('extended__owner') # List owners context['owners'] = [] for photo in context['photos']: if hasattr(photo, 'extended') and photo.extended.owner not in context['owners']: context['owners'].append(photo.extended.owner) # Filter on owner if 'owner' in self.kwargs: context['photos'] = context['photos'].filter(extended__owner__id=self.kwargs['owner']) return context class GalleryDownload(LoginRequiredMixin, DetailView): model = Gallery 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() byte_data = BytesIO() zip_file = zipfile.ZipFile(byte_data, "w") for photo in gallery.public(): filename = os.path.basename(os.path.normpath(photo.image.path)) zip_file.write(photo.image.path, filename) zip_file.close() # 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 GalleryUpload(FormView): """ Form to upload new photos in a gallery """ form_class = UploadForm template_name = "photologue/upload.html" success_url = reverse_lazy("gallery-upload") def form_valid(self, form): # Upload photos # We take files from the request to support multiple upload files = self.request.FILES.getlist('file_field') gallery = form.get_or_create_gallery() failed_upload = 0 for photo_file in files: # Check that we have a valid image print(photo_file, type(photo_file)) try: opened = Image.open(photo_file) opened.verify() except Exception: # Pillow doesn't recognize it as an image, skip it messages.error(self.request, f"{photo_file.name} was not recognized as an image") failed_upload += 1 continue title = f"{gallery.title} - {photo_file.name}" try: photo = Photo(title=title, slug=slugify(title)) photo.image.save(photo_file.name, photo_file) photo.save() photo.galleries.set([gallery]) PhotoExtended.objects.create(photo=photo, owner=self.request.user) except IntegrityError: messages.error(self.request, f"{photo_file.name} was not uploaded. Maybe the photo was already uploaded.") failed_upload += 1 # Notify user then managers if not failed_upload: messages.success(self.request, "All photos has been successfully uploaded.") else: n_success = len(files) - failed_upload messages.warning(self.request, f"Only {n_success} photos were successfully uploaded !") gallery_title = form.cleaned_data['gallery'] or form.cleaned_data.get('new_gallery_title', '') photos = ", ".join(f.name for f in files) mail_managers( subject="New photos upload", message=f"{self.request.user.username} has uploaded in `{gallery_title}`: {photos}", ) return super().form_valid(form)