diff --git a/photo21/settings.py b/photo21/settings.py
index eaa3a790e8a493f529c7f11e2e2c4331eb694d79..0375594a0553aab623585722d11245196505a678 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/templates/photologue/gallery_archive.html b/photologue/templates/photologue/gallery_archive.html
index 9e58de7be56bb32021c68931feb28391d1455fdf..206dd585ef609e148216ed1045c3a3495d35e44e 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>
@@ -38,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 794fd0d564d7a257ffe504b2e93276a915f06541..54cdb9581d50aadc618e4961cba05db795c824c5 100644
--- a/photologue/templates/photologue/gallery_archive_year.html
+++ b/photologue/templates/photologue/gallery_archive_year.html
@@ -9,27 +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">
-		<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>
-		</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 9bcc837d32ab76e0ef5f523372d86d23f1a9408e..491480482885051ddb90cc88ab5e98b444da0deb 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
@@ -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"""
@@ -36,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
@@ -91,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}",
         )
@@ -182,10 +189,15 @@ 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
             try:
@@ -196,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}"
@@ -207,32 +218,32 @@ 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
+                continue
+
+            uploaded_photo_name.append(photo_file.name)
 
         # Notify user then managers
-        if not failed_upload:
-            messages.success(self.request, "All photos has been successfully uploaded.")
+        n_success = len(uploaded_photo_name)
+        if already_exists:
+            messages.success(self.request, f"{n_success} photo(s) uploaded, {already_exists} photo(s) skipped as they already exist in this gallery.")
         else:
-            n_success = len(files) - failed_upload
-            messages.warning(
-                self.request, f"Only {n_success} photos were successfully uploaded !"
+            messages.success(self.request, f"{n_success} photo(s) uploaded.")
+
+        # 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:
+            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_url}>:\n{photos}",
             )
 
-        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)