# 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 pathlib import Path from django.contrib import messages from django.contrib.auth.mixins import (LoginRequiredMixin, PermissionRequiredMixin) 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, Tag from PIL import Image from .forms import UploadForm 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.filter(is_public=True) \ .filter(tags__slug=current_tag) \ .order_by('-date_start') return context class CustomGalleryDetailView(LoginRequiredMixin, DetailView): """ Custom gallery detail view to filter on photo owner """ queryset = Gallery.objects.filter(is_public=True) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Query with owner to reduce database lag context['photos'] = self.object.public().select_related('owner') # List owners context['owners'] = [] for photo in context['photos']: if photo.owner not in context['owners']: context['owners'].append(photo.owner) # Filter on owner if 'owner' in self.kwargs: context['photos'] = context['photos'].filter(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(PermissionRequiredMixin, FormView): """ Form to upload new photos in a gallery """ form_class = UploadForm template_name = "photologue/upload.html" success_url = reverse_lazy("photologue:pl-gallery-upload") permission_required = 'photologue.add_gallery' 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() gallery_year = Path(str(gallery.date_start.year)) gallery_dir = gallery_year / gallery.slug failed_upload = 0 for photo_file in files: # Check that we have a valid image 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), owner=self.request.user, ) photo_name = str(gallery_dir / photo_file.name) photo.image.save(photo_name, photo_file) photo.save() photo.galleries.set([gallery]) 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)