From 3a610583794b3e190b0eed1c2d29d376b1526e29 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 09:36:45 +0200
Subject: [PATCH 1/7] Remove archive view h1 titles

---
 photologue/templates/photologue/gallery_archive.html      | 6 ------
 photologue/templates/photologue/gallery_archive_year.html | 5 -----
 2 files changed, 11 deletions(-)

diff --git a/photologue/templates/photologue/gallery_archive.html b/photologue/templates/photologue/gallery_archive.html
index 9e58de7..4cd683d 100644
--- a/photologue/templates/photologue/gallery_archive.html
+++ b/photologue/templates/photologue/gallery_archive.html
@@ -9,12 +9,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
 {% block title %}{% trans "Latest photo galleries" %}{% endblock %}
 
 {% block content %}
-<div class="row">
-    <div class="col-lg-12">
-        <h1>{% trans "Latest photo galleries" %}</h1>
-    </div>
-</div>
-
 <div class="row">
     <aside class="col-md-2">
         <h5>{% trans "Filter by year" %}</h5>
diff --git a/photologue/templates/photologue/gallery_archive_year.html b/photologue/templates/photologue/gallery_archive_year.html
index 794fd0d..8be8f21 100644
--- a/photologue/templates/photologue/gallery_archive_year.html
+++ b/photologue/templates/photologue/gallery_archive_year.html
@@ -9,11 +9,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
 {% block title %}{% blocktrans with show_year=year|date:"Y" %}Galleries for {{ show_year }}{% endblocktrans %}{% endblock %}
 
 {% block content %}
-    <div class="row">
-		<div class="col-lg-12">
-        	<h1>{% blocktrans with show_year=year|date:"Y" %}Galleries for {{ show_year }}{% endblocktrans %}</h1>
-    	</div>
-	</div>
 	<div class="row">
 		<aside class="col-md-2">
 		    <a href="{% url 'photologue:pl-gallery-archive' %}">⬅ {% trans "View all galleries" %}</a>
-- 
GitLab


From 3afbc2b7e1238cab290a98ccd5c7717925ece133 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 09:37:07 +0200
Subject: [PATCH 2/7] Do not 404 if no galleries

---
 photologue/views.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/photologue/views.py b/photologue/views.py
index 9bcc837..93b9c9b 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -27,6 +27,7 @@ from .models import Gallery, Photo, Tag
 class GalleryDateView(LoginRequiredMixin):
     model = Gallery
     date_field = "date_start"
+    allow_empty = True  # Do not 404 if no galleries
 
     def get_queryset(self):
         """Hide galleries with only private photos"""
-- 
GitLab


From f29719ef78e037b395eba56791aa7b9e6dc76794 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 09:39:30 +0200
Subject: [PATCH 3/7] Always list all years in archive views

---
 .../templates/photologue/gallery_archive.html |  2 +-
 .../photologue/gallery_archive_year.html      | 41 +++++++++++--------
 photologue/views.py                           |  6 +++
 3 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/photologue/templates/photologue/gallery_archive.html b/photologue/templates/photologue/gallery_archive.html
index 4cd683d..206dd58 100644
--- a/photologue/templates/photologue/gallery_archive.html
+++ b/photologue/templates/photologue/gallery_archive.html
@@ -32,4 +32,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
         {% endif %}
     </main>
 </div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/photologue/templates/photologue/gallery_archive_year.html b/photologue/templates/photologue/gallery_archive_year.html
index 8be8f21..54cdb95 100644
--- a/photologue/templates/photologue/gallery_archive_year.html
+++ b/photologue/templates/photologue/gallery_archive_year.html
@@ -9,22 +9,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
 {% block title %}{% blocktrans with show_year=year|date:"Y" %}Galleries for {{ show_year }}{% endblocktrans %}{% endblock %}
 
 {% block content %}
-	<div class="row">
-		<aside class="col-md-2">
-		    <a href="{% url 'photologue:pl-gallery-archive' %}">⬅ {% trans "View all galleries" %}</a>
-		</aside>
-		<main class="col-md-10">
-			{% if object_list %}
-                <div class="row mb-2">
-				{% for gallery in object_list %}
-                    <div class="col-6 col-md-3 mb-2">
-                        {% include "photologue/includes/gallery_sample.html" %}
-                    </div>
-                {% endfor %}
+<div class="row">
+    <aside class="col-md-2">
+        <h5>{% trans "Filter by year" %}</h5>
+        <div class="list-group">
+            {% for date in date_list %}
+            <a class="list-group-item list-group-item-action{% if date == year %} active{% endif %}" href="{% url 'photologue:pl-gallery-archive-year' date.year %}">{{ date|date:"Y" }}</a>
+            {% endfor %}
+        </div>
+    </aside>
+    <main class="col-md-10">
+        {% if object_list %}
+            <div class="row mb-2">
+            {% for gallery in object_list %}
+                <div class="col-6 col-md-3 mb-2">
+                    {% include "photologue/includes/gallery_sample.html" %}
                 </div>
-			{% else %}
-			    <p>{% trans "No galleries were found." %}</p>
-			{% endif %}
-		</main>
-	</div>
-{% endblock %}
\ No newline at end of file
+            {% endfor %}
+            </div>
+        {% else %}
+            <p>{% trans "No galleries were found." %}</p>
+        {% endif %}
+    </main>
+</div>
+{% endblock %}
diff --git a/photologue/views.py b/photologue/views.py
index 93b9c9b..af1249d 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -37,6 +37,12 @@ class GalleryDateView(LoginRequiredMixin):
         else:
             return qs.filter(photos__is_public=True).distinct()
 
+    def get_context_data(self, **kwargs):
+        """Always show all years in archive"""
+        context = super().get_context_data(**kwargs)
+        context['date_list'] = self.get_queryset().dates(self.date_field, 'year', 'DESC')
+        return context
+
 
 class GalleryArchiveIndexView(GalleryDateView, ArchiveIndexView):
     pass
-- 
GitLab


From 73f80de7942ae5c79401786dbf0c24a2050280c4 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 10:03:50 +0200
Subject: [PATCH 4/7] Handle already existing pictures

---
 photologue/views.py | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/photologue/views.py b/photologue/views.py
index af1249d..e0cf0a6 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -193,6 +193,7 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
         gallery_year = Path(str(gallery.date_start.year))
         gallery_dir = gallery_year / gallery.slug
         failed_upload = 0
+        already_exists = 0
         for photo_file in files:
             # Check that we have a valid image
             try:
@@ -214,21 +215,21 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
                     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])
+
+                # Save to disk after successful database edit
+                photo.image.save(photo_name, photo_file)
             except IntegrityError:
-                messages.error(
-                    self.request,
-                    f"{photo_file.name} was not uploaded. Maybe the photo was already uploaded.",
-                )
-                failed_upload += 1
+                already_exists += 1
 
         # Notify user then managers
-        if not failed_upload:
-            messages.success(self.request, "All photos has been successfully uploaded.")
+        n_success = len(files) - failed_upload - already_exists
+        if already_exists:
+            messages.success(self.request, f"{n_success} photos has been successfully uploaded, {already_exists} photos were skipped as they already exist in this gallery.")
+        elif not failed_upload:
+            messages.success(self.request, f"All {n_success} photos has been successfully uploaded.")
         else:
-            n_success = len(files) - failed_upload
             messages.warning(
                 self.request, f"Only {n_success} photos were successfully uploaded !"
             )
-- 
GitLab


From fbbb7651b19d6cbdf95b992ed177b898ea3883f5 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 10:13:53 +0200
Subject: [PATCH 5/7] Better admin notification on uploads

---
 photo21/settings.py |  5 -----
 photologue/views.py | 34 +++++++++++++++++++---------------
 2 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/photo21/settings.py b/photo21/settings.py
index eaa3a79..0375594 100644
--- a/photo21/settings.py
+++ b/photo21/settings.py
@@ -42,11 +42,6 @@ ADMINS = [
     ("admin", "photos-admin@lists.crans.org"),
 ]
 
-# Managers receive uploads notification
-MANAGERS = [
-    ('admin', 'photos-admin@lists.crans.org'),
-]
-
 # Use secure cookies in production
 SESSION_COOKIE_SECURE = not DEBUG
 CSRF_COOKIE_SECURE = not DEBUG
diff --git a/photologue/views.py b/photologue/views.py
index e0cf0a6..05de677 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -9,7 +9,7 @@ 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.core.mail import mail_admins
 from django.db import IntegrityError
 from django.http import HttpResponse
 from django.shortcuts import redirect
@@ -189,10 +189,14 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
         # Upload photos
         # We take files from the request to support multiple upload
         files = self.request.FILES.getlist("file_field")
+
+        # Get or create gallery
         gallery = form.get_or_create_gallery()
         gallery_year = Path(str(gallery.date_start.year))
         gallery_dir = gallery_year / gallery.slug
-        failed_upload = 0
+
+        # Upload pictures
+        uploaded_photo_name = []
         already_exists = 0
         for photo_file in files:
             # Check that we have a valid image
@@ -204,7 +208,6 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
                 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}"
@@ -222,25 +225,26 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
                 photo.image.save(photo_name, photo_file)
             except IntegrityError:
                 already_exists += 1
+                continue
+
+            uploaded_photo_name.append(photo_file.name)
 
         # Notify user then managers
-        n_success = len(files) - failed_upload - already_exists
+        n_success = len(uploaded_photo_name)
         if already_exists:
-            messages.success(self.request, f"{n_success} photos has been successfully uploaded, {already_exists} photos were skipped as they already exist in this gallery.")
-        elif not failed_upload:
-            messages.success(self.request, f"All {n_success} photos has been successfully uploaded.")
+            messages.success(self.request, f"{n_success} photo(s) uploaded, {already_exists} photo(s) skipped as they already exist in this gallery.")
         else:
-            messages.warning(
-                self.request, f"Only {n_success} photos were successfully uploaded !"
-            )
+            messages.success(self.request, f"{n_success} photo(s) 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}",
-        )
+        if uploaded_photo_name:
+            # Notify administrators
+            photos = ", ".join(uploaded_photo_name)
+            mail_admins(
+                subject=f"New upload from {self.request.user.username}",
+                message=f"{self.request.user.username} has uploaded in `{gallery_title}`:\n{photos}",
+            )
 
         return super().form_valid(form)
-- 
GitLab


From 58d819a7196c52b6fd19625e9d115cba6300b986 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 10:17:29 +0200
Subject: [PATCH 6/7] Send abuse reports to admins

---
 photologue/views.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/photologue/views.py b/photologue/views.py
index 05de677..8baca75 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -98,7 +98,7 @@ class PhotoReportView(LoginRequiredMixin, DetailView):
         url = request.build_absolute_uri(url)
 
         # Send mail to managers
-        mail_managers(
+        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}",
         )
-- 
GitLab


From 7faff5368f375591a5a9a3aa0db3d6f731366921 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 17 Apr 2022 10:20:52 +0200
Subject: [PATCH 7/7] Use gallery URL in uploads notifications

---
 photologue/views.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/photologue/views.py b/photologue/views.py
index 8baca75..4914804 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -236,15 +236,14 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
         else:
             messages.success(self.request, f"{n_success} photo(s) uploaded.")
 
-        gallery_title = form.cleaned_data["gallery"] or form.cleaned_data.get(
-            "new_gallery_title", ""
-        )
+        # Notify administrators on new uploads
+        gallery_url = reverse_lazy("photologue:pl-gallery", args=[gallery.slug])
+        gallery_url = self.request.build_absolute_uri(gallery_url)
         if uploaded_photo_name:
-            # Notify administrators
             photos = ", ".join(uploaded_photo_name)
             mail_admins(
                 subject=f"New upload from {self.request.user.username}",
-                message=f"{self.request.user.username} has uploaded in `{gallery_title}`:\n{photos}",
+                message=f"{self.request.user.username} has uploaded in <{gallery_url}>:\n{photos}",
             )
 
         return super().form_valid(form)
-- 
GitLab