From 6ea1a7c77fc3540e85bc2fc3fc303eab2a54237a Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 10:19:08 +0100
Subject: [PATCH 1/9] Override logout template

---
 photo21/templates/account/logout.html | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 photo21/templates/account/logout.html

diff --git a/photo21/templates/account/logout.html b/photo21/templates/account/logout.html
new file mode 100644
index 0000000..7db6ac3
--- /dev/null
+++ b/photo21/templates/account/logout.html
@@ -0,0 +1,24 @@
+{% extends "account/base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-2.0-or-later
+{% endcomment %}
+{% load i18n %}
+{% block head_title %}{% trans "Sign Out" %}{% endblock %}
+
+{% block content %}
+<div class="card mx-auto">
+    <h3 class="card-header text-center">
+        {% trans "Sign Out" %}
+    </h3>
+    <div class="card-body">        
+            <p>{% trans 'Are you sure you want to sign out?' %}</p>
+            <form method="post" action="{% url 'account_logout' %}">
+                {% csrf_token %}
+                {% if redirect_field_value %}
+                <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
+                {% endif %}
+                <button type="submit" class="btn btn-primary">{% trans 'Sign Out' %}</button>
+            </form>
+    </div>
+</div>
+{% endblock %}
-- 
GitLab


From 27b1590a10f9af23b551278652f8c48975299848 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 10:20:32 +0100
Subject: [PATCH 2/9] Add extended fields to the Photo and Gallery

---
 photologue/admin.py                         | 13 ++-
 photologue/migrations/0014_merge_related.py | 96 +++++++++++++++++++++
 photologue/models.py                        | 47 ++++++++++
 3 files changed, 154 insertions(+), 2 deletions(-)
 create mode 100644 photologue/migrations/0014_merge_related.py

diff --git a/photologue/admin.py b/photologue/admin.py
index 0c04abf..ff797fb 100644
--- a/photologue/admin.py
+++ b/photologue/admin.py
@@ -1,6 +1,6 @@
 from django.contrib import admin
 
-from .models import Gallery, Photo
+from .models import Gallery, Photo, Tag
 
 
 class GalleryAdmin(admin.ModelAdmin):
@@ -9,7 +9,7 @@ class GalleryAdmin(admin.ModelAdmin):
     date_hierarchy = 'date_added'
     prepopulated_fields = {'slug': ('title',)}
     model = Gallery
-    autocomplete_fields = ['photos', ]
+    autocomplete_fields = ['photos', 'tags']
     search_fields = ['title', ]
 
 
@@ -22,3 +22,12 @@ class PhotoAdmin(admin.ModelAdmin):
     prepopulated_fields = {'slug': ('title',)}
     readonly_fields = ('date_taken',)
     model = Photo
+
+
+class TagAdmin(admin.ModelAdmin):
+    list_display = ('name',)
+    search_fields = ('name',)
+    model = Tag
+
+
+admin.site.register(Tag, TagAdmin)
diff --git a/photologue/migrations/0014_merge_related.py b/photologue/migrations/0014_merge_related.py
new file mode 100644
index 0000000..2608233
--- /dev/null
+++ b/photologue/migrations/0014_merge_related.py
@@ -0,0 +1,96 @@
+# Generated by Django 3.2.11 on 2022-01-30 08:32
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import taggit.managers
+from django.contrib.auth.models import User
+from django.contrib.contenttypes.models import ContentType
+from django.template.defaultfilters import slugify
+
+numens = User.objects.get(username="Numens").id
+
+def migrate_related(apps, schema_editor):
+    Gallery = apps.get_model('photologue', 'Gallery')
+    Tag = apps.get_model('photologue', 'Tag')
+    TaggedItems = apps.get_model('taggit', 'TaggedItem')
+    ct_ext = ContentType.objects.get(app_label="photologue_custom", model="galleryextended")
+    for gallery in Gallery.objects.all():
+        tagged_items = TaggedItems.objects.filter(
+            content_type_id=ct_ext.id,
+            object_id=gallery.extended.id
+        )
+        tags = [tag.tag for tag in tagged_items.all()]
+        gallery.date_start = gallery.extended.date_start
+        gallery.date_end = gallery.extended.date_end
+        gallery.save()
+        for tag in tags:
+            try:
+                new_tag, created = Tag.objects.get_or_create(
+                    name=tag.name.capitalize(),
+                    slug=slugify(tag.name),
+                )
+                if new_tag not in gallery.tags.all():
+                    gallery.tags.add(new_tag)
+                new_tag.save()
+                gallery.save()
+            except Exception:
+                continue
+
+    Photo = apps.get_model('photologue', 'Photo')
+    for photo in Photo.objects.all():
+        photo.owner = photo.extended.owner
+        photo.license = photo.extended.license
+        photo.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('photologue', '0013_alter_gallery_photos'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Tag',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=250, unique=True, verbose_name='name')),
+                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
+            ],
+            options={
+                'verbose_name': 'tag',
+                'verbose_name_plural': 'tags',
+                'ordering': ['name'],
+            },
+        ),
+        migrations.AddField(
+            model_name='gallery',
+            name='date_end',
+            field=models.DateField(blank=True, null=True, verbose_name='end date'),
+        ),
+        migrations.AddField(
+            model_name='gallery',
+            name='date_start',
+            field=models.DateField(default=django.utils.timezone.now, verbose_name='start date'),
+        ),
+        migrations.AddField(
+            model_name='photo',
+            name='license',
+            field=models.CharField(blank=True, max_length=255, verbose_name='license'),
+        ),
+        migrations.AddField(
+            model_name='photo',
+            name='owner',
+            field=models.ForeignKey(default=numens, on_delete=django.db.models.deletion.CASCADE, to='auth.user', verbose_name='owner'),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='gallery',
+            name='tags',
+            field=models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Tag', verbose_name='tags'),
+        ),
+        migrations.RunPython(migrate_related),
+    ]
diff --git a/photologue/models.py b/photologue/models.py
index f38fbc5..059ac44 100644
--- a/photologue/models.py
+++ b/photologue/models.py
@@ -145,8 +145,23 @@ class Gallery(models.Model):
                             unique=True,
                             max_length=250,
                             help_text=_('A "slug" is a unique URL-friendly title for an object.'))
+    date_start = models.DateField(
+        default=now,
+        verbose_name=_("start date"),
+    )
+    date_end = models.DateField(
+        blank=True,
+        null=True,
+        verbose_name=_("end date"),
+    )
     description = models.TextField(_('description'),
                                    blank=True)
+    tags = models.ManyToManyField(
+        'photologue.Tag',
+        related_name='galleries',
+        verbose_name=_('tags'),
+        blank=True,
+    )
     is_public = models.BooleanField(_('is public'),
                                     default=True,
                                     help_text=_('Public galleries will be displayed '
@@ -478,6 +493,16 @@ class Photo(ImageModel):
                                blank=True)
     date_added = models.DateTimeField(_('date added'),
                                       default=now)
+    owner = models.ForeignKey(
+        settings.AUTH_USER_MODEL,
+        on_delete=models.CASCADE,
+        verbose_name=_("owner"),
+    )
+    license = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_("license"),
+    )
     is_public = models.BooleanField(_('is public'),
                                     default=True,
                                     help_text=_('Public photographs will be displayed in the default views.'))
@@ -657,3 +682,25 @@ def init_size_method_map():
             {'base_name': '_get_size_url', 'size': size}
         size_method_map['get_%s_filename' % size] = \
             {'base_name': '_get_size_filename', 'size': size}
+
+
+class Tag(models.Model):
+    name = models.CharField(
+        max_length=250,
+        unique=True,
+        verbose_name=_('name'),
+    )
+    slug = models.SlugField(
+        unique=True,
+        max_length=250,
+        verbose_name=_('slug'),
+        help_text=_('A "slug" is a unique URL-friendly title for an object.'),
+    )
+
+    class Meta:
+        ordering = ['name']
+        verbose_name = _('tag')
+        verbose_name_plural = _('tags')
+
+    def __str__(self):
+        return self.name
-- 
GitLab


From 2a0f41deacf01aced64e7dacd4692b9bce8e77ce Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 10:21:05 +0100
Subject: [PATCH 3/9] Move urls.py in photologue

---
 photo21/urls.py                           | 2 +-
 {photologue_custom => photologue}/urls.py | 6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)
 rename {photologue_custom => photologue}/urls.py (73%)

diff --git a/photo21/urls.py b/photo21/urls.py
index 530068a..0538e1c 100644
--- a/photo21/urls.py
+++ b/photo21/urls.py
@@ -22,7 +22,7 @@ from .views import IndexView, MediaAccess
 
 urlpatterns = [
     path('', IndexView.as_view(), name='index'),
-    path('', include('photologue_custom.urls', namespace='photologue')),
+    path('', include('photologue.urls', namespace='photologue')),
     path('accounts/', include('allauth.urls')),
     path('i18n/', include('django.conf.urls.i18n')),
     path('admin/', admin.site.urls),
diff --git a/photologue_custom/urls.py b/photologue/urls.py
similarity index 73%
rename from photologue_custom/urls.py
rename to photologue/urls.py
index 037a354..4772067 100644
--- a/photologue_custom/urls.py
+++ b/photologue/urls.py
@@ -1,10 +1,8 @@
 from django.urls import path, re_path
-from photologue.views import GalleryArchiveIndexView, GalleryYearArchiveView, PhotoDetailView
+from photologue_custom.views import CustomGalleryDetailView, GalleryDownload, GalleryUpload, TagDetail
 
-from .views import CustomGalleryDetailView, GalleryDownload, GalleryUpload, TagDetail
+from .views import GalleryArchiveIndexView, GalleryYearArchiveView, PhotoDetailView
 
-# Rather than using photologue default router, we redefine our own router
-# with login and permission checks.
 app_name = 'photologue'
 urlpatterns = [
     path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'),
-- 
GitLab


From 3bc8a2b649dc8cc36a57625768c119a8d95ffcae Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 10:21:23 +0100
Subject: [PATCH 4/9] Remove padding around thumbnails

---
 photologue_custom/templates/photologue/gallery_detail.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/photologue_custom/templates/photologue/gallery_detail.html b/photologue_custom/templates/photologue/gallery_detail.html
index 21bc348..9649dc7 100644
--- a/photologue_custom/templates/photologue/gallery_detail.html
+++ b/photologue_custom/templates/photologue/gallery_detail.html
@@ -70,7 +70,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
     <div class="card-body row" id="lightgallery">
         {% for photo in photos %}
         <a class="col-6 col-md-3 mb-2 text-center" href="{{ photo.get_absolute_url }}" data-src="{{ photo.get_display_url }}" data-download-url="{{ photo.image.url }}" data-slide-name="{{ photo.id }}">
-            <img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.extended.owner.get_full_name }}{% if photo.extended.license %} - {{ photo.extended.license }}{% endif %}">
+            <img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail p-0" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.extended.owner.get_full_name }}{% if photo.extended.license %} - {{ photo.extended.license }}{% endif %}">
         </a>
         {% endfor %}
     </div>
-- 
GitLab


From 8beedb36264d75cf01d7dd34076a1ddf2b0438c1 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 10:53:58 +0100
Subject: [PATCH 5/9] Remove extended models

---
 README.md                                     |  3 +-
 photologue/admin.py                           | 12 +++-
 photologue/models.py                          |  2 +-
 photologue/views.py                           |  2 +-
 photologue_custom/admin.py                    | 47 --------------
 photologue_custom/forms.py                    | 24 ++-----
 .../management/commands/rename_media.py       |  2 +-
 .../migrations/0005_auto_20220130_0953.py     | 27 ++++++++
 photologue_custom/models.py                   | 64 -------------------
 .../templates/photologue/gallery_detail.html  |  8 +--
 .../photologue/includes/gallery_sample.html   |  2 +-
 .../{taggit => photologue}/tag_detail.html    |  0
 photologue_custom/views.py                    | 27 ++++----
 requirements.txt                              |  1 -
 14 files changed, 66 insertions(+), 155 deletions(-)
 delete mode 100644 photologue_custom/admin.py
 create mode 100644 photologue_custom/migrations/0005_auto_20220130_0953.py
 delete mode 100644 photologue_custom/models.py
 rename photologue_custom/templates/{taggit => photologue}/tag_detail.html (100%)

diff --git a/README.md b/README.md
index 52dccb3..9edddbc 100644
--- a/README.md
+++ b/README.md
@@ -79,8 +79,7 @@ production néccessite **une installation de Debian Bullseye ou plus récent**.
     ```
     $ sudo apt install nginx git gettext uwsgi uwsgi-plugin-python3 python3-venv \
         python3-certbot-nginx python3-django python3-django-crispy-forms \
-        python3-django-taggit python3-pil python3-exifread python3-django-allauth \
-        python3-psycopg2 python3-docutils
+        python3-pil python3-exifread python3-django-allauth python3-docutils
     ```
 
 2.  **Clonage du dépot dans `/var/www/photos/photo21`**
diff --git a/photologue/admin.py b/photologue/admin.py
index ff797fb..f398fc7 100644
--- a/photologue/admin.py
+++ b/photologue/admin.py
@@ -1,4 +1,5 @@
 from django.contrib import admin
+from django.utils.translation import gettext_lazy as _
 
 from .models import Gallery, Photo, Tag
 
@@ -15,14 +16,19 @@ class GalleryAdmin(admin.ModelAdmin):
 
 class PhotoAdmin(admin.ModelAdmin):
     list_display = ('title', 'date_taken', 'date_added',
-                    'is_public', 'view_count', 'admin_thumbnail')
-    list_filter = ['date_added', 'is_public']
+                    'is_public', 'view_count', 'admin_thumbnail', 'get_owner')
+    list_filter = ['date_added', 'is_public', 'owner']
     search_fields = ['title', 'slug', 'caption']
     list_per_page = 10
     prepopulated_fields = {'slug': ('title',)}
     readonly_fields = ('date_taken',)
     model = Photo
 
+    def get_owner(self, obj):
+        return obj.owner.username
+    get_owner.admin_order_field = 'owner'
+    get_owner.short_description = _('owner')
+
 
 class TagAdmin(admin.ModelAdmin):
     list_display = ('name',)
@@ -30,4 +36,6 @@ class TagAdmin(admin.ModelAdmin):
     model = Tag
 
 
+admin.site.register(Gallery, GalleryAdmin)
+admin.site.register(Photo, PhotoAdmin)
 admin.site.register(Tag, TagAdmin)
diff --git a/photologue/models.py b/photologue/models.py
index 059ac44..8b94e5c 100644
--- a/photologue/models.py
+++ b/photologue/models.py
@@ -178,7 +178,7 @@ class Gallery(models.Model):
         verbose_name_plural = _('galleries')
 
     def __str__(self):
-        return self.title
+        return f"{ self.title } ({self.date_start})"
 
     def get_absolute_url(self):
         return reverse('photologue:pl-gallery', args=[self.slug])
diff --git a/photologue/views.py b/photologue/views.py
index 93fc8b5..2741b50 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -7,7 +7,7 @@ from .models import Gallery, Photo
 
 class GalleryDateView(LoginRequiredMixin):
     queryset = Gallery.objects.filter(is_public=True)
-    date_field = 'extended__date_start'
+    date_field = 'date_start'
     uses_datetime_field = False  # Fix related object access
     allow_empty = True
 
diff --git a/photologue_custom/admin.py b/photologue_custom/admin.py
deleted file mode 100644
index e8f0130..0000000
--- a/photologue_custom/admin.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from django.contrib import admin
-from django.utils.translation import gettext_lazy as _
-from photologue.admin import GalleryAdmin as GalleryAdminDefault
-from photologue.admin import PhotoAdmin as PhotoAdminDefault
-from photologue.models import Gallery, Photo
-
-from .models import GalleryExtended, PhotoExtended
-
-
-class GalleryExtendedInline(admin.StackedInline):
-    model = GalleryExtended
-    can_delete = False
-
-
-class GalleryAdmin(GalleryAdminDefault):
-    """
-    Define our new one-to-one model as an inline of Photologue's Gallery
-    model.
-    """
-    inlines = [GalleryExtendedInline, ]
-
-
-class PhotoExtendedInline(admin.StackedInline):
-    model = PhotoExtended
-    can_delete = True
-
-
-class PhotoAdmin(PhotoAdminDefault):
-    """
-    Define our new one-to-one model as an inline of Photologue's Photo
-    model.
-    """
-    inlines = [PhotoExtendedInline, ]
-    list_display = ('title', 'date_taken', 'date_added',
-                    'is_public', 'view_count', 'admin_thumbnail', 'get_owner')
-    list_filter = ['date_added', 'is_public', 'extended__owner']
-
-    def get_owner(self, obj):
-        if not hasattr(obj, 'extended'):
-            return "No owner"
-        return obj.extended.owner.username
-    get_owner.admin_order_field = 'owner'
-    get_owner.short_description = _('owner')
-
-
-admin.site.register(Gallery, GalleryAdmin)
-admin.site.register(Photo, PhotoAdmin)
diff --git a/photologue_custom/forms.py b/photologue_custom/forms.py
index 11a146d..96f4498 100644
--- a/photologue_custom/forms.py
+++ b/photologue_custom/forms.py
@@ -5,19 +5,7 @@ from crispy_forms.layout import Div, Layout, Submit
 from django import forms
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
-from photologue.models import Gallery
-from taggit.models import Tag
-
-from .models import GalleryExtended
-
-
-class GalleryChoiceField(forms.ModelChoiceField):
-    def label_from_instance(self, obj):
-        """Show gallery event date."""
-        if hasattr(obj, 'extended'):
-            return f"{ obj.title } ({obj.extended.date_start})"
-        else:
-            return obj.title
+from photologue.models import Gallery, Tag
 
 
 class UploadForm(forms.Form):
@@ -29,7 +17,7 @@ class UploadForm(forms.Form):
             'class': 'mb-3',
         }),
     )
-    gallery = GalleryChoiceField(
+    gallery = forms.ModelChoiceField(
         Gallery.objects.all(),
         label=_('Gallery'),
         required=False,
@@ -100,12 +88,12 @@ class UploadForm(forms.Form):
         if not gallery:
             # Create new gallery
             title = self.cleaned_data.get('new_gallery_title')
-            gallery = Gallery.objects.create(title=title, slug=slugify(title))
-            ext = GalleryExtended.objects.create(
-                gallery=gallery,
+            gallery = Gallery.objects.create(
+                title=title,
+                slug=slugify(title),
                 date_start=self.cleaned_data['new_gallery_date_start'],
                 date_end=self.cleaned_data['new_gallery_date_end'],
             )
             for tag in self.cleaned_data['new_gallery_tags']:
-                ext.tags.add(tag)
+                gallery.tags.add(tag)
         return gallery
diff --git a/photologue_custom/management/commands/rename_media.py b/photologue_custom/management/commands/rename_media.py
index 62615e9..461870b 100644
--- a/photologue_custom/management/commands/rename_media.py
+++ b/photologue_custom/management/commands/rename_media.py
@@ -16,7 +16,7 @@ class Command(BaseCommand):
         media_dir = Path(settings.MEDIA_ROOT)
         for gallery in Gallery.objects.all():
             # Create gallery directory
-            gallery_year = str(gallery.extended.date_start.year)
+            gallery_year = str(gallery.date_start.year)
             gallery_dir = Path('photos') / gallery_year / gallery.slug
             gallery_path = media_dir / gallery_dir
             if not gallery_path.exists():
diff --git a/photologue_custom/migrations/0005_auto_20220130_0953.py b/photologue_custom/migrations/0005_auto_20220130_0953.py
new file mode 100644
index 0000000..ef769ec
--- /dev/null
+++ b/photologue_custom/migrations/0005_auto_20220130_0953.py
@@ -0,0 +1,27 @@
+# Generated by Django 3.2.11 on 2022-01-30 09:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('photologue_custom', '0004_photoextended_license'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='photoextended',
+            name='owner',
+        ),
+        migrations.RemoveField(
+            model_name='photoextended',
+            name='photo',
+        ),
+        migrations.DeleteModel(
+            name='GalleryExtended',
+        ),
+        migrations.DeleteModel(
+            name='PhotoExtended',
+        ),
+    ]
diff --git a/photologue_custom/models.py b/photologue_custom/models.py
deleted file mode 100644
index 6275e64..0000000
--- a/photologue_custom/models.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from django.db import models
-from django.conf import settings
-from taggit.managers import TaggableManager
-from photologue.models import Gallery, Photo
-from django.utils.translation import gettext_lazy as _
-
-
-class GalleryExtended(models.Model):
-    # Extend Photologue Gallery model.
-    gallery = models.OneToOneField(
-        Gallery,
-        related_name='extended',
-        on_delete=models.CASCADE,
-    )
-
-    # Add tags
-    tags = TaggableManager(blank=True)
-
-    # Add start and end dates fields to GalleryExtend
-    date_start = models.DateField(
-        blank=True,
-        null=True,
-        verbose_name=_("start date"),
-    )
-    date_end = models.DateField(
-        blank=True,
-        null=True,
-        verbose_name=_("end date"),
-    )
-
-    class Meta:
-        verbose_name = 'Extra fields'
-        verbose_name_plural = 'Extra fields'
-
-    def __str__(self):
-        return self.gallery.title
-
-
-class PhotoExtended(models.Model):
-    # Extend Photologue Photo model.
-    photo = models.OneToOneField(
-        Photo,
-        related_name='extended',
-        on_delete=models.CASCADE,
-    )
-
-    # Add a owner field to PhotoExtended
-    owner = models.ForeignKey(
-        settings.AUTH_USER_MODEL,
-        on_delete=models.CASCADE,
-        verbose_name=_("owner"),
-    )
-    license = models.CharField(
-        max_length=255,
-        blank=True,
-        verbose_name=_("license"),
-    )
-
-    class Meta:
-        verbose_name = 'Extra fields'
-        verbose_name_plural = 'Extra fields'
-
-    def __str__(self):
-        return str(self.photo)
diff --git a/photologue_custom/templates/photologue/gallery_detail.html b/photologue_custom/templates/photologue/gallery_detail.html
index 9649dc7..1df68af 100644
--- a/photologue_custom/templates/photologue/gallery_detail.html
+++ b/photologue_custom/templates/photologue/gallery_detail.html
@@ -38,10 +38,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
     </a>
     {% endif %}
 </h1>
-{% if gallery.extended.date_start %}<p class="text-muted small">{{ gallery.extended.date_start }}{% if gallery.extended.date_end and gallery.extended.date_end != gallery.extended.date_start %} {% trans "to" %} {{ gallery.extended.date_end }}{% endif %}</p>{% endif %}
-{% if gallery.extended.tags.all %}
+{% if gallery.date_start %}<p class="text-muted small">{{ gallery.date_start }}{% if gallery.date_end and gallery.date_end != gallery.date_start %} {% trans "to" %} {{ gallery.date_end }}{% endif %}</p>{% endif %}
+{% if gallery.tags.all %}
 <p class="text-muted">
-    Tags : {% for tag in gallery.extended.tags.all %}
+    Tags : {% for tag in gallery.tags.all %}
     <a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'photologue:tag-detail' tag.slug %}">{{ tag }}</a>
     {% endfor %}
 </p>
@@ -70,7 +70,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
     <div class="card-body row" id="lightgallery">
         {% for photo in photos %}
         <a class="col-6 col-md-3 mb-2 text-center" href="{{ photo.get_absolute_url }}" data-src="{{ photo.get_display_url }}" data-download-url="{{ photo.image.url }}" data-slide-name="{{ photo.id }}">
-            <img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail p-0" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.extended.owner.get_full_name }}{% if photo.extended.license %} - {{ photo.extended.license }}{% endif %}">
+            <img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail p-0" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.owner.get_full_name }}{% if photo.license %} - {{ photo.license }}{% endif %}">
         </a>
         {% endfor %}
     </div>
diff --git a/photologue_custom/templates/photologue/includes/gallery_sample.html b/photologue_custom/templates/photologue/includes/gallery_sample.html
index abe2bfd..8282074 100644
--- a/photologue_custom/templates/photologue/includes/gallery_sample.html
+++ b/photologue_custom/templates/photologue/includes/gallery_sample.html
@@ -6,7 +6,7 @@
     {% endfor %}
     <div class="card-body">
         <h5 class="card-title">{{ gallery.title }}</h5>
-        {% if gallery.extended.date_start %}<p class="card-text text-muted small mb-0">{{ gallery.extended.date_start }}{% if gallery.extended.date_end and gallery.extended.date_end != gallery.extended.date_start %} - {{ gallery.extended.date_end }}{% endif %}</p>{% endif %}
+        {% if gallery.date_start %}<p class="card-text text-muted small mb-0">{{ gallery.date_start }}{% if gallery.date_end and gallery.date_end != gallery.date_start %} - {{ gallery.date_end }}{% endif %}</p>{% endif %}
         <a href="{{ gallery.get_absolute_url }}" class="stretched-link"></a>
     </div>
 </div>
diff --git a/photologue_custom/templates/taggit/tag_detail.html b/photologue_custom/templates/photologue/tag_detail.html
similarity index 100%
rename from photologue_custom/templates/taggit/tag_detail.html
rename to photologue_custom/templates/photologue/tag_detail.html
diff --git a/photologue_custom/views.py b/photologue_custom/views.py
index 8a683f0..38ebc7f 100644
--- a/photologue_custom/views.py
+++ b/photologue_custom/views.py
@@ -16,12 +16,10 @@ 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.models import Gallery, Photo, Tag
 from PIL import Image
-from taggit.models import Tag
 
 from .forms import UploadForm
-from .models import PhotoExtended
 
 
 class TagDetail(LoginRequiredMixin, DetailView):
@@ -34,8 +32,8 @@ class TagDetail(LoginRequiredMixin, DetailView):
         current_tag = self.get_object().slug
         context = super().get_context_data(**kwargs)
         context['galleries'] = Gallery.objects.filter(is_public=True) \
-            .filter(extended__tags__slug=current_tag) \
-            .order_by('-extended__date_start')
+            .filter(tags__slug=current_tag) \
+            .order_by('-date_start')
         return context
 
 
@@ -48,18 +46,18 @@ class CustomGalleryDetailView(LoginRequiredMixin, DetailView):
     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')
+        # 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 hasattr(photo, 'extended') and photo.extended.owner not in context['owners']:
-                context['owners'].append(photo.extended.owner)
+            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(extended__owner__id=self.kwargs['owner'])
+            context['photos'] = context['photos'].filter(owner__id=self.kwargs['owner'])
 
         return context
 
@@ -100,7 +98,7 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
         # 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.extended.date_start.year))
+        gallery_year = Path(str(gallery.date_start.year))
         gallery_dir = gallery_year / gallery.slug
         failed_upload = 0
         for photo_file in files:
@@ -116,12 +114,15 @@ class GalleryUpload(PermissionRequiredMixin, FormView):
 
             title = f"{gallery.title} - {photo_file.name}"
             try:
-                photo = Photo(title=title, slug=slugify(title))
+                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])
-                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
diff --git a/requirements.txt b/requirements.txt
index 9f86e8d..cf9dcaf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,5 @@
 django-allauth>=0.44
 django-crispy-forms~=1.7
-django-taggit>=1.5.0
 Django>=2.2.20
 ExifRead>=2.1.2
 git+https://gitlab.crans.org/bde/allauth-note-kfet.git
-- 
GitLab


From d865a6eb1f99004e94da9093009afced4b436a85 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 11:09:55 +0100
Subject: [PATCH 6/9] Merge photologue_custom into photologue

---
 photo21/locale/de/LC_MESSAGES/django.po       |  20 +-
 photo21/locale/es/LC_MESSAGES/django.po       |  20 +-
 photo21/locale/fr/LC_MESSAGES/django.po       |  67 +---
 photo21/settings.py                           |   1 -
 {photologue_custom => photologue}/forms.py    |   3 +-
 photologue/locale/de/LC_MESSAGES/django.po    | 194 +++++------
 photologue/locale/es/LC_MESSAGES/django.po    | 194 +++++------
 photologue/locale/fr/LC_MESSAGES/django.po    | 307 +++++++++---------
 .../management/commands/duplicate.py          |   4 +-
 .../management/commands/rename_media.py       |   4 +-
 photologue/migrations/0014_merge_related.py   |   4 +-
 .../static/lightgallery/css/lg-thumbnail.css  |   0
 .../static/lightgallery/css/lg-zoom.css       |   0
 .../static/lightgallery/css/lightgallery.css  |   0
 .../static/lightgallery/fonts/lg.svg          |   0
 .../static/lightgallery/fonts/lg.ttf          | Bin
 .../static/lightgallery/fonts/lg.woff         | Bin
 .../static/lightgallery/fonts/lg.woff2        | Bin
 .../static/lightgallery/images/loading.gif    | Bin
 .../static/lightgallery/lightgallery.min.js   |   0
 .../lightgallery/plugins/admin/lg-admin.js    |   0
 .../lightgallery/plugins/hash/lg-hash.min.js  |   0
 .../plugins/thumbnail/lg-thumbnail.min.js     |   0
 .../lightgallery/plugins/zoom/lg-zoom.min.js  |   0
 .../admin/photologue/photo/change_list.html   |   0
 .../templates/photologue/gallery_archive.html |   0
 .../photologue/gallery_archive_year.html      |   0
 .../templates/photologue/gallery_detail.html  |   0
 .../photologue/includes/gallery_sample.html   |   0
 .../templates/photologue/photo_detail.html    |   0
 .../templates/photologue/tag_detail.html      |   0
 .../templates/photologue/upload.html          |   0
 photologue/urls.py                            |   5 +-
 photologue/views.py                           | 144 +++++++-
 photologue_custom/views.py                    | 144 --------
 tox.ini                                       |   4 +-
 36 files changed, 549 insertions(+), 566 deletions(-)
 rename {photologue_custom => photologue}/forms.py (98%)
 rename {photologue_custom => photologue}/management/commands/duplicate.py (100%)
 rename {photologue_custom => photologue}/management/commands/rename_media.py (100%)
 rename {photologue_custom => photologue}/static/lightgallery/css/lg-thumbnail.css (100%)
 rename {photologue_custom => photologue}/static/lightgallery/css/lg-zoom.css (100%)
 rename {photologue_custom => photologue}/static/lightgallery/css/lightgallery.css (100%)
 rename {photologue_custom => photologue}/static/lightgallery/fonts/lg.svg (100%)
 rename {photologue_custom => photologue}/static/lightgallery/fonts/lg.ttf (100%)
 rename {photologue_custom => photologue}/static/lightgallery/fonts/lg.woff (100%)
 rename {photologue_custom => photologue}/static/lightgallery/fonts/lg.woff2 (100%)
 rename {photologue_custom => photologue}/static/lightgallery/images/loading.gif (100%)
 rename {photologue_custom => photologue}/static/lightgallery/lightgallery.min.js (100%)
 rename {photologue_custom => photologue}/static/lightgallery/plugins/admin/lg-admin.js (100%)
 rename {photologue_custom => photologue}/static/lightgallery/plugins/hash/lg-hash.min.js (100%)
 rename {photologue_custom => photologue}/static/lightgallery/plugins/thumbnail/lg-thumbnail.min.js (100%)
 rename {photologue_custom => photologue}/static/lightgallery/plugins/zoom/lg-zoom.min.js (100%)
 rename {photologue_custom => photologue}/templates/admin/photologue/photo/change_list.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/gallery_archive.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/gallery_archive_year.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/gallery_detail.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/includes/gallery_sample.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/photo_detail.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/tag_detail.html (100%)
 rename {photologue_custom => photologue}/templates/photologue/upload.html (100%)
 delete mode 100644 photologue_custom/views.py

diff --git a/photo21/locale/de/LC_MESSAGES/django.po b/photo21/locale/de/LC_MESSAGES/django.po
index 1cff806..d45572b 100644
--- a/photo21/locale/de/LC_MESSAGES/django.po
+++ b/photo21/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-29 21:58+0000\n"
+"POT-Creation-Date: 2022-01-30 09:55+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -40,19 +40,19 @@ msgstr ""
 msgid "hash"
 msgstr ""
 
-#: photo21/settings.py:162
+#: photo21/settings.py:161
 msgid "German"
 msgstr ""
 
-#: photo21/settings.py:163
+#: photo21/settings.py:162
 msgid "English"
 msgstr ""
 
-#: photo21/settings.py:164
+#: photo21/settings.py:163
 msgid "Spanish"
 msgstr ""
 
-#: photo21/settings.py:165
+#: photo21/settings.py:164
 msgid "French"
 msgstr ""
 
@@ -199,6 +199,16 @@ msgstr ""
 msgid "If any problem, please contact the server owners at"
 msgstr ""
 
+#: photo21/templates/account/logout.html:6
+#: photo21/templates/account/logout.html:11
+#: photo21/templates/account/logout.html:20
+msgid "Sign Out"
+msgstr ""
+
+#: photo21/templates/account/logout.html:14
+msgid "Are you sure you want to sign out?"
+msgstr ""
+
 #: photo21/templates/account/signup.html:6
 msgid "Signup"
 msgstr ""
diff --git a/photo21/locale/es/LC_MESSAGES/django.po b/photo21/locale/es/LC_MESSAGES/django.po
index a9413d9..f05fed8 100644
--- a/photo21/locale/es/LC_MESSAGES/django.po
+++ b/photo21/locale/es/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-29 21:58+0000\n"
+"POT-Creation-Date: 2022-01-30 09:55+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -39,19 +39,19 @@ msgstr ""
 msgid "hash"
 msgstr ""
 
-#: photo21/settings.py:162
+#: photo21/settings.py:161
 msgid "German"
 msgstr ""
 
-#: photo21/settings.py:163
+#: photo21/settings.py:162
 msgid "English"
 msgstr ""
 
-#: photo21/settings.py:164
+#: photo21/settings.py:163
 msgid "Spanish"
 msgstr ""
 
-#: photo21/settings.py:165
+#: photo21/settings.py:164
 msgid "French"
 msgstr ""
 
@@ -198,6 +198,16 @@ msgstr ""
 msgid "If any problem, please contact the server owners at"
 msgstr ""
 
+#: photo21/templates/account/logout.html:6
+#: photo21/templates/account/logout.html:11
+#: photo21/templates/account/logout.html:20
+msgid "Sign Out"
+msgstr ""
+
+#: photo21/templates/account/logout.html:14
+msgid "Are you sure you want to sign out?"
+msgstr ""
+
 #: photo21/templates/account/signup.html:6
 msgid "Signup"
 msgstr ""
diff --git a/photo21/locale/fr/LC_MESSAGES/django.po b/photo21/locale/fr/LC_MESSAGES/django.po
index ac7970b..7d7d518 100644
--- a/photo21/locale/fr/LC_MESSAGES/django.po
+++ b/photo21/locale/fr/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-30 07:09+0000\n"
+"POT-Creation-Date: 2022-01-30 10:06+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -42,19 +42,19 @@ msgstr ""
 msgid "hash"
 msgstr ""
 
-#: photo21/settings.py:162
+#: photo21/settings.py:160
 msgid "German"
 msgstr ""
 
-#: photo21/settings.py:163
+#: photo21/settings.py:161
 msgid "English"
 msgstr ""
 
-#: photo21/settings.py:164
+#: photo21/settings.py:162
 msgid "Spanish"
 msgstr ""
 
-#: photo21/settings.py:165
+#: photo21/settings.py:163
 msgid "French"
 msgstr ""
 
@@ -206,6 +206,18 @@ msgstr ""
 msgid "If any problem, please contact the server owners at"
 msgstr "En cas de problème, contactez les administrateurs à"
 
+#: photo21/templates/account/logout.html:6
+#: photo21/templates/account/logout.html:11
+#: photo21/templates/account/logout.html:20
+#, fuzzy
+#| msgid "Sign up"
+msgid "Sign Out"
+msgstr "Inscription"
+
+#: photo21/templates/account/logout.html:14
+msgid "Are you sure you want to sign out?"
+msgstr ""
+
 #: photo21/templates/account/signup.html:6
 msgid "Signup"
 msgstr ""
@@ -333,48 +345,3 @@ msgstr ""
 #: photo21/templates/socialaccount/connections.html:54
 msgid "Add a 3rd Party Account"
 msgstr ""
-
-#~ msgid "owner"
-#~ msgstr "propriétaire"
-
-#~ msgid "Gallery"
-#~ msgstr "Galerie"
-
-#~ msgid "-- Create a new gallery --"
-#~ msgstr "-- Créer une nouvelle galerie --"
-
-#~ msgid "New gallery title"
-#~ msgstr "Titre de la nouvelle galerie"
-
-#~ msgid "New gallery event start date"
-#~ msgstr "Date de début de l'évènement de la nouvelle galerie"
-
-#~ msgid "New gallery event end date"
-#~ msgstr "Date de fin de l'évènement de la nouvelle galerie"
-
-#~ msgid "New gallery tags"
-#~ msgstr "Tags de la nouvelle galerie"
-
-#~ msgid "start date"
-#~ msgstr "date de début"
-
-#~ msgid "end date"
-#~ msgstr "date de fin"
-
-#~ msgid "license"
-#~ msgstr "licence"
-
-#~ msgid "to"
-#~ msgstr "au"
-
-#~ msgid "All pictures"
-#~ msgstr "Toutes les photos"
-
-#~ msgid "Download all gallery"
-#~ msgstr "Télécharger toute la galerie"
-
-#~ msgid "Drag and drop photos here"
-#~ msgstr "Glissez et déposez les photos ici"
-
-#~ msgid "Owner will be"
-#~ msgstr "Le propriétaire sera"
diff --git a/photo21/settings.py b/photo21/settings.py
index 5c61f42..9b2844b 100644
--- a/photo21/settings.py
+++ b/photo21/settings.py
@@ -62,7 +62,6 @@ INSTALLED_APPS = [
     'allauth.socialaccount',
     'allauth_note_kfet',
     'crispy_forms',
-    'photologue_custom',
     'photologue',
     'taggit',
 ]
diff --git a/photologue_custom/forms.py b/photologue/forms.py
similarity index 98%
rename from photologue_custom/forms.py
rename to photologue/forms.py
index 96f4498..8ef1f1c 100644
--- a/photologue_custom/forms.py
+++ b/photologue/forms.py
@@ -5,7 +5,8 @@ from crispy_forms.layout import Div, Layout, Submit
 from django import forms
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
-from photologue.models import Gallery, Tag
+
+from .models import Gallery, Tag
 
 
 class UploadForm(forms.Form):
diff --git a/photologue/locale/de/LC_MESSAGES/django.po b/photologue/locale/de/LC_MESSAGES/django.po
index f9a29ed..50c106d 100644
--- a/photologue/locale/de/LC_MESSAGES/django.po
+++ b/photologue/locale/de/LC_MESSAGES/django.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Photologue\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-30 07:09+0000\n"
+"POT-Creation-Date: 2022-01-30 09:55+0000\n"
 "PO-Revision-Date: 2017-12-03 14:47+0000\n"
 "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
 "Language-Team: German (http://www.transifex.com/richardbarran/django-"
@@ -22,75 +22,79 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: photologue/models.py:86
+#: photologue/admin.py:30 photologue/models.py:499
+msgid "owner"
+msgstr ""
+
+#: photologue/models.py:84
 msgid "Very Low"
 msgstr "Sehr niedrig"
 
-#: photologue/models.py:87
+#: photologue/models.py:85
 msgid "Low"
 msgstr "Niedrig"
 
-#: photologue/models.py:88
+#: photologue/models.py:86
 msgid "Medium-Low"
 msgstr "Mittel-niedrig"
 
-#: photologue/models.py:89
+#: photologue/models.py:87
 msgid "Medium"
 msgstr "Mittel"
 
-#: photologue/models.py:90
+#: photologue/models.py:88
 msgid "Medium-High"
 msgstr "Mittel-hoch"
 
-#: photologue/models.py:91
+#: photologue/models.py:89
 msgid "High"
 msgstr "Hoch"
 
-#: photologue/models.py:92
+#: photologue/models.py:90
 msgid "Very High"
 msgstr "Sehr hoch"
 
-#: photologue/models.py:97
+#: photologue/models.py:95
 msgid "Top"
 msgstr "Oben"
 
-#: photologue/models.py:98
+#: photologue/models.py:96
 msgid "Right"
 msgstr "Rechts"
 
-#: photologue/models.py:99
+#: photologue/models.py:97
 msgid "Bottom"
 msgstr "Unten"
 
-#: photologue/models.py:100
+#: photologue/models.py:98
 msgid "Left"
 msgstr "Links"
 
-#: photologue/models.py:101
+#: photologue/models.py:99
 msgid "Center (Default)"
 msgstr "Mitte (Standard)"
 
-#: photologue/models.py:105
+#: photologue/models.py:103
 msgid "Flip left to right"
 msgstr "Horizontal spiegeln"
 
-#: photologue/models.py:106
+#: photologue/models.py:104
 msgid "Flip top to bottom"
 msgstr "Vertikal spiegeln"
 
-#: photologue/models.py:107
+#: photologue/models.py:105
 msgid "Rotate 90 degrees counter-clockwise"
 msgstr "Um 90° nach links drehen"
 
-#: photologue/models.py:108
+#: photologue/models.py:106
 msgid "Rotate 90 degrees clockwise"
 msgstr "Um 90° nach rechts drehen"
 
-#: photologue/models.py:109
+#: photologue/models.py:107
 msgid "Rotate 180 degrees"
 msgstr "Um 180° drehen"
 
-#: photologue/models.py:119
+#: photologue/models.py:117
 #, python-format
 msgid ""
 "Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
@@ -101,106 +105,122 @@ msgstr ""
 "\". Bildfilter werden nach der Reihe angewendet. Folgende Filter sind "
 "verfügbar: %s."
 
-#: photologue/models.py:141
+#: photologue/models.py:139
 msgid "date published"
 msgstr "Veröffentlichungsdatum"
 
-#: photologue/models.py:143 photologue/models.py:474
+#: photologue/models.py:141 photologue/models.py:485
 msgid "title"
 msgstr "Titel"
 
-#: photologue/models.py:146
+#: photologue/models.py:144
 msgid "title slug"
 msgstr "Kurztitel"
 
-#: photologue/models.py:149 photologue/models.py:480
+#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
 msgid "A \"slug\" is a unique URL-friendly title for an object."
 msgstr ""
 "Ein Kurztitel (\"slug\") ist ein eindeutiger, URL-geeigneter Titel für ein "
 "Objekt."
 
 #: photologue/models.py:150
+msgid "start date"
+msgstr ""
+
+#: photologue/models.py:155
+msgid "end date"
+msgstr ""
+
+#: photologue/models.py:157
 msgid "description"
 msgstr "Beschreibung"
 
-#: photologue/models.py:152 photologue/models.py:485
+#: photologue/models.py:162 photologue/models.py:703
+msgid "tags"
+msgstr ""
+
+#: photologue/models.py:165 photologue/models.py:506
 msgid "is public"
 msgstr "ist öffentlich"
 
-#: photologue/models.py:154
+#: photologue/models.py:167
 msgid "Public galleries will be displayed in the default views."
 msgstr "Öffentliche Galerien werden in den Standard-Views angezeigt."
 
-#: photologue/models.py:158 photologue/models.py:495
+#: photologue/models.py:171 photologue/models.py:514
 msgid "photos"
 msgstr "Fotos"
 
-#: photologue/models.py:166
+#: photologue/models.py:177
 msgid "gallery"
 msgstr "Galerie"
 
-#: photologue/models.py:167
+#: photologue/models.py:178
 msgid "galleries"
 msgstr "Galerien"
 
-#: photologue/models.py:202
+#: photologue/models.py:213
 msgid "count"
 msgstr "Anzahl"
 
-#: photologue/models.py:210
+#: photologue/models.py:221
 msgid "image"
 msgstr "Bild"
 
-#: photologue/models.py:213
+#: photologue/models.py:224
 msgid "date taken"
 msgstr "Aufnahmedatum"
 
-#: photologue/models.py:216
+#: photologue/models.py:227
 msgid "Date image was taken; is obtained from the image EXIF data."
 msgstr ""
 "Datum, an dem das Foto geschossen wurde; ausgelesen aus den EXIF-Daten."
 
-#: photologue/models.py:217
+#: photologue/models.py:228
 msgid "view count"
 msgstr "Anzahl an Aufrufen"
 
-#: photologue/models.py:220
+#: photologue/models.py:231
 msgid "crop from"
 msgstr "Beschneiden von"
 
-#: photologue/models.py:243
+#: photologue/models.py:254
 msgid "An \"admin_thumbnail\" photo size has not been defined."
 msgstr "Es ist keine Fotogröße \"admin_thumbnail\" definiert."
 
-#: photologue/models.py:250
+#: photologue/models.py:261
 msgid "Thumbnail"
 msgstr "Vorschaubild"
 
-#: photologue/models.py:477
+#: photologue/models.py:488 photologue/models.py:696
 msgid "slug"
 msgstr "Kurztitel"
 
-#: photologue/models.py:481
+#: photologue/models.py:492
 msgid "caption"
 msgstr "Bildunterschrift"
 
-#: photologue/models.py:483
+#: photologue/models.py:494
 msgid "date added"
 msgstr "Datum des Eintrags"
 
-#: photologue/models.py:487
+#: photologue/models.py:504
+msgid "license"
+msgstr ""
+
+#: photologue/models.py:508
 msgid "Public photographs will be displayed in the default views."
 msgstr "Öffentliche Fotos werden in den Standard-Views angezeigt."
 
-#: photologue/models.py:494
+#: photologue/models.py:513
 msgid "photo"
 msgstr "Foto"
 
-#: photologue/models.py:556
+#: photologue/models.py:575 photologue/models.py:691
 msgid "name"
 msgstr "Name"
 
-#: photologue/models.py:560
+#: photologue/models.py:579
 msgid ""
 "Photo size name should contain only letters, numbers and underscores. "
 "Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
@@ -209,41 +229,41 @@ msgstr ""
 "enthalten. Beispiele: \"thumbnail\", \"display\", \"small\", "
 "\"main_page_widget\"."
 
-#: photologue/models.py:567
+#: photologue/models.py:586
 msgid "width"
 msgstr "Breite"
 
-#: photologue/models.py:570
+#: photologue/models.py:589
 msgid ""
 "If width is set to \"0\" the image will be scaled to the supplied height."
 msgstr ""
 "Wenn die Breite auf \"0\" gesetzt ist, wird das Bild proportional auf die "
 "angebene Höhe skaliert."
 
-#: photologue/models.py:571
+#: photologue/models.py:590
 msgid "height"
 msgstr "Höhe"
 
-#: photologue/models.py:574
+#: photologue/models.py:593
 msgid ""
 "If height is set to \"0\" the image will be scaled to the supplied width"
 msgstr ""
 "Wenn die Höhe auf \"0\" gesetzt ist, wird das Bild proportional auf die "
 "angebene Breite skaliert."
 
-#: photologue/models.py:575
+#: photologue/models.py:594
 msgid "quality"
 msgstr "Qualität"
 
-#: photologue/models.py:578
+#: photologue/models.py:597
 msgid "JPEG image quality."
 msgstr "JPEG-Bildqualität"
 
-#: photologue/models.py:579
+#: photologue/models.py:598
 msgid "upscale images?"
 msgstr "Bilder hochskalieren?"
 
-#: photologue/models.py:581
+#: photologue/models.py:600
 msgid ""
 "If selected the image will be scaled up if necessary to fit the supplied "
 "dimensions. Cropped sizes will be upscaled regardless of this setting."
@@ -252,32 +272,32 @@ msgstr ""
 "Beschnittene Größen werden unabhängig von dieser Einstellung bei Bedarf "
 "hochskaliert."
 
-#: photologue/models.py:585
+#: photologue/models.py:604
 msgid "crop to fit?"
 msgstr "Zuschneiden?"
 
-#: photologue/models.py:587
+#: photologue/models.py:606
 msgid ""
 "If selected the image will be scaled and cropped to fit the supplied "
 "dimensions."
 msgstr ""
 "Soll das Bild auf das angegebene Format skaliert und beschnitten werden?"
 
-#: photologue/models.py:589
+#: photologue/models.py:608
 msgid "pre-cache?"
 msgstr "Vorausspeichern?"
 
-#: photologue/models.py:591
+#: photologue/models.py:610
 msgid "If selected this photo size will be pre-cached as photos are added."
 msgstr ""
 "Soll diese Bildgröße im Voraus gespeichert (pre-cached) werden, wenn Fotos "
 "hinzugefügt werden?"
 
-#: photologue/models.py:592
+#: photologue/models.py:611
 msgid "increment view count?"
 msgstr "Bildzähler?"
 
-#: photologue/models.py:594
+#: photologue/models.py:613
 msgid ""
 "If selected the image's \"view_count\" will be incremented when this photo "
 "size is displayed."
@@ -285,32 +305,32 @@ msgstr ""
 "Soll der Ansichts-Zähler (view-count) hochgezählt werden, wenn ein Foto "
 "dieser Größe angezeigt wird?"
 
-#: photologue/models.py:599
+#: photologue/models.py:618
 msgid "photo size"
 msgstr "Foto-Größe"
 
-#: photologue/models.py:600
+#: photologue/models.py:619
 msgid "photo sizes"
 msgstr "Foto-Größen"
 
-#: photologue/models.py:617
+#: photologue/models.py:636
 msgid "Can only crop photos if both width and height dimensions are set."
 msgstr ""
 "Fotos können nur zugeschnitten werden, wenn Breite und Höhe angegeben sind."
 
-#: photologue_custom/admin.py:43 photologue_custom/models.py:51
-msgid "owner"
+#: photologue/models.py:702
+msgid "tag"
 msgstr ""
 
-#: photologue_custom/forms.py:34
+#: photologue_custom/forms.py:22
 msgid "Gallery"
 msgstr "Galerie"
 
-#: photologue_custom/forms.py:36
+#: photologue_custom/forms.py:24
 msgid "-- Create a new gallery --"
 msgstr ""
 
-#: photologue_custom/forms.py:37
+#: photologue_custom/forms.py:25
 msgid ""
 "Select a gallery to add these images to. Leave this empty to create a new "
 "gallery from the supplied title."
@@ -318,59 +338,43 @@ msgstr ""
 "Wähle eine Galerie aus, zu der diese Bilder hinzugefügt werden sollen. Lasse "
 "dieses Feld leer, um eine neue Galerie mit dem angegeben Titel zu erzeugen."
 
-#: photologue_custom/forms.py:41
-#, fuzzy
-#| msgid "View all galleries"
+#: photologue_custom/forms.py:29
 msgid "New gallery title"
-msgstr "Zeige alle Galerien."
+msgstr ""
 
-#: photologue_custom/forms.py:46
+#: photologue_custom/forms.py:34
 msgid "New gallery event start date"
 msgstr ""
 
-#: photologue_custom/forms.py:51
+#: photologue_custom/forms.py:39
 msgid "New gallery event end date"
 msgstr ""
 
-#: photologue_custom/forms.py:57
-#, fuzzy
-#| msgid "gallery"
+#: photologue_custom/forms.py:45
 msgid "New gallery tags"
-msgstr "Galerie"
+msgstr ""
 
-#: photologue_custom/forms.py:59
+#: photologue_custom/forms.py:47
 msgid ""
 "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
 msgstr ""
 
-#: photologue_custom/forms.py:76
+#: photologue_custom/forms.py:64
 #: photologue_custom/templates/photologue/upload.html:6
 #: photologue_custom/templates/photologue/upload.html:73
 msgid "Upload"
 msgstr "Hochladen"
 
-#: photologue_custom/forms.py:82
+#: photologue_custom/forms.py:70
 msgid "A gallery with that title already exists."
 msgstr "Es existiert bereits eine Gallerie mit diesem Titel."
 
-#: photologue_custom/forms.py:91
+#: photologue_custom/forms.py:79
 msgid "Select an existing gallery, or enter a title for a new gallery."
 msgstr ""
 "Wähle eine existierende Galerie aus oder gib einen Titel für eine neue "
 "Galerie ein."
 
-#: photologue_custom/models.py:23
-msgid "start date"
-msgstr ""
-
-#: photologue_custom/models.py:28
-msgid "end date"
-msgstr ""
-
-#: photologue_custom/models.py:56
-msgid "license"
-msgstr ""
-
 #: photologue_custom/templates/photologue/gallery_archive.html:7
 #: photologue_custom/templates/photologue/gallery_archive.html:12
 msgid "Latest photo galleries"
@@ -403,16 +407,12 @@ msgid "to"
 msgstr ""
 
 #: photologue_custom/templates/photologue/gallery_detail.html:57
-#, fuzzy
-#| msgid "All photos"
 msgid "All pictures"
-msgstr "Alle Fotos"
+msgstr ""
 
 #: photologue_custom/templates/photologue/gallery_detail.html:78
-#, fuzzy
-#| msgid "View all galleries"
 msgid "Download all gallery"
-msgstr "Zeige alle Galerien."
+msgstr ""
 
 #: photologue_custom/templates/photologue/photo_detail.html:13
 msgid "Published"
diff --git a/photologue/locale/es/LC_MESSAGES/django.po b/photologue/locale/es/LC_MESSAGES/django.po
index 8a9ca71..e8f687f 100644
--- a/photologue/locale/es/LC_MESSAGES/django.po
+++ b/photologue/locale/es/LC_MESSAGES/django.po
@@ -12,7 +12,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Photologue\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-30 07:09+0000\n"
+"POT-Creation-Date: 2022-01-30 09:55+0000\n"
 "PO-Revision-Date: 2017-12-03 14:46+0000\n"
 "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
 "Language-Team: Spanish (Spain) (http://www.transifex.com/richardbarran/"
@@ -23,75 +23,79 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: photologue/models.py:86
+#: photologue/admin.py:30 photologue/models.py:499
+msgid "owner"
+msgstr ""
+
+#: photologue/models.py:84
 msgid "Very Low"
 msgstr "Muy bajo"
 
-#: photologue/models.py:87
+#: photologue/models.py:85
 msgid "Low"
 msgstr "Bajo"
 
-#: photologue/models.py:88
+#: photologue/models.py:86
 msgid "Medium-Low"
 msgstr "Medio-bajo"
 
-#: photologue/models.py:89
+#: photologue/models.py:87
 msgid "Medium"
 msgstr "Medio"
 
-#: photologue/models.py:90
+#: photologue/models.py:88
 msgid "Medium-High"
 msgstr "Medio-alto"
 
-#: photologue/models.py:91
+#: photologue/models.py:89
 msgid "High"
 msgstr "Alto"
 
-#: photologue/models.py:92
+#: photologue/models.py:90
 msgid "Very High"
 msgstr "Muy alto"
 
-#: photologue/models.py:97
+#: photologue/models.py:95
 msgid "Top"
 msgstr "Arriba"
 
-#: photologue/models.py:98
+#: photologue/models.py:96
 msgid "Right"
 msgstr "Derecha"
 
-#: photologue/models.py:99
+#: photologue/models.py:97
 msgid "Bottom"
 msgstr "Abajo"
 
-#: photologue/models.py:100
+#: photologue/models.py:98
 msgid "Left"
 msgstr "Izquierda"
 
-#: photologue/models.py:101
+#: photologue/models.py:99
 msgid "Center (Default)"
 msgstr "Centro (por defecto)"
 
-#: photologue/models.py:105
+#: photologue/models.py:103
 msgid "Flip left to right"
 msgstr "Voltear de izquerda a derecha"
 
-#: photologue/models.py:106
+#: photologue/models.py:104
 msgid "Flip top to bottom"
 msgstr "Voltear de arriba a abajo"
 
-#: photologue/models.py:107
+#: photologue/models.py:105
 msgid "Rotate 90 degrees counter-clockwise"
 msgstr "Rotar 90 grados en sentido horario"
 
-#: photologue/models.py:108
+#: photologue/models.py:106
 msgid "Rotate 90 degrees clockwise"
 msgstr "Rotar 90 grados en sentido antihorario"
 
-#: photologue/models.py:109
+#: photologue/models.py:107
 msgid "Rotate 180 degrees"
 msgstr "Rotar 180 grados"
 
-#: photologue/models.py:119
+#: photologue/models.py:117
 #, python-format
 msgid ""
 "Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
@@ -102,103 +106,119 @@ msgstr ""
 ">FILTRO_DOS->FILTRO_TRES\". Los filtros de imagen se aplicarán en orden. Los "
 "siguientes filtros están disponibles: %s."
 
-#: photologue/models.py:141
+#: photologue/models.py:139
 msgid "date published"
 msgstr "fecha de publicación"
 
-#: photologue/models.py:143 photologue/models.py:474
+#: photologue/models.py:141 photologue/models.py:485
 msgid "title"
 msgstr "título"
 
-#: photologue/models.py:146
+#: photologue/models.py:144
 msgid "title slug"
 msgstr "título slug"
 
-#: photologue/models.py:149 photologue/models.py:480
+#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
 msgid "A \"slug\" is a unique URL-friendly title for an object."
 msgstr "Un \"slug\" es un único título URL-amigable para un objeto."
 
 #: photologue/models.py:150
+msgid "start date"
+msgstr ""
+
+#: photologue/models.py:155
+msgid "end date"
+msgstr ""
+
+#: photologue/models.py:157
 msgid "description"
 msgstr "descripción"
 
-#: photologue/models.py:152 photologue/models.py:485
+#: photologue/models.py:162 photologue/models.py:703
+msgid "tags"
+msgstr ""
+
+#: photologue/models.py:165 photologue/models.py:506
 msgid "is public"
 msgstr "es público"
 
-#: photologue/models.py:154
+#: photologue/models.py:167
 msgid "Public galleries will be displayed in the default views."
 msgstr "Las galerías públicas serán mostradas en las vistas por defecto."
 
-#: photologue/models.py:158 photologue/models.py:495
+#: photologue/models.py:171 photologue/models.py:514
 msgid "photos"
 msgstr "fotos"
 
-#: photologue/models.py:166
+#: photologue/models.py:177
 msgid "gallery"
 msgstr "galería"
 
-#: photologue/models.py:167
+#: photologue/models.py:178
 msgid "galleries"
 msgstr "galerías"
 
-#: photologue/models.py:202
+#: photologue/models.py:213
 msgid "count"
 msgstr "contar"
 
-#: photologue/models.py:210
+#: photologue/models.py:221
 msgid "image"
 msgstr "imagen"
 
-#: photologue/models.py:213
+#: photologue/models.py:224
 msgid "date taken"
 msgstr "fecha en la que se tomó"
 
-#: photologue/models.py:216
+#: photologue/models.py:227
 msgid "Date image was taken; is obtained from the image EXIF data."
 msgstr "La fecha de la imagen fue obtenida por información EXIF de la imagen."
 
-#: photologue/models.py:217
+#: photologue/models.py:228
 msgid "view count"
 msgstr "Contador de visitas"
 
-#: photologue/models.py:220
+#: photologue/models.py:231
 msgid "crop from"
 msgstr "Recortar desde"
 
-#: photologue/models.py:243
+#: photologue/models.py:254
 msgid "An \"admin_thumbnail\" photo size has not been defined."
 msgstr "El tamaño de foto de \"miniatura de admin\" no ha sido definido."
 
-#: photologue/models.py:250
+#: photologue/models.py:261
 msgid "Thumbnail"
 msgstr "Miniatura"
 
-#: photologue/models.py:477
+#: photologue/models.py:488 photologue/models.py:696
 msgid "slug"
 msgstr "slug"
 
-#: photologue/models.py:481
+#: photologue/models.py:492
 msgid "caption"
 msgstr "pie de foto"
 
-#: photologue/models.py:483
+#: photologue/models.py:494
 msgid "date added"
 msgstr "fecha añadida"
 
-#: photologue/models.py:487
+#: photologue/models.py:504
+msgid "license"
+msgstr ""
+
+#: photologue/models.py:508
 msgid "Public photographs will be displayed in the default views."
 msgstr "Las fotos públicas serán mostradas en las vistas por defecto."
 
-#: photologue/models.py:494
+#: photologue/models.py:513
 msgid "photo"
 msgstr "foto"
 
-#: photologue/models.py:556
+#: photologue/models.py:575 photologue/models.py:691
 msgid "name"
 msgstr "nombre"
 
-#: photologue/models.py:560
+#: photologue/models.py:579
 msgid ""
 "Photo size name should contain only letters, numbers and underscores. "
 "Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
@@ -206,41 +226,41 @@ msgstr ""
 "El nombre del tamaño solo puede contener letras, números y subrayados. Por "
 "ejemplo:\"miniaturas\", \"muestra\", \"muestra_principal\"."
 
-#: photologue/models.py:567
+#: photologue/models.py:586
 msgid "width"
 msgstr "anchura"
 
-#: photologue/models.py:570
+#: photologue/models.py:589
 msgid ""
 "If width is set to \"0\" the image will be scaled to the supplied height."
 msgstr ""
 "Si la anchura se establece a \"0\" la imagen será escalada hasta la altura "
 "proporcionada"
 
-#: photologue/models.py:571
+#: photologue/models.py:590
 msgid "height"
 msgstr "altura"
 
-#: photologue/models.py:574
+#: photologue/models.py:593
 msgid ""
 "If height is set to \"0\" the image will be scaled to the supplied width"
 msgstr ""
 "Si la altura se establece a \"0\" la imagen será escalada hasta la anchura "
 "proporcionada"
 
-#: photologue/models.py:575
+#: photologue/models.py:594
 msgid "quality"
 msgstr "calidad"
 
-#: photologue/models.py:578
+#: photologue/models.py:597
 msgid "JPEG image quality."
 msgstr "Calidad de imagen JPEG."
 
-#: photologue/models.py:579
+#: photologue/models.py:598
 msgid "upscale images?"
 msgstr "¿Aumentar imágenes?"
 
-#: photologue/models.py:581
+#: photologue/models.py:600
 msgid ""
 "If selected the image will be scaled up if necessary to fit the supplied "
 "dimensions. Cropped sizes will be upscaled regardless of this setting."
@@ -249,11 +269,11 @@ msgstr ""
 "las dimensiones proporcionadas. Los tamaños recortados serán aumentados de "
 "acuerdo a esta opción."
 
-#: photologue/models.py:585
+#: photologue/models.py:604
 msgid "crop to fit?"
 msgstr "¿Recortar hasta ajustar?"
 
-#: photologue/models.py:587
+#: photologue/models.py:606
 msgid ""
 "If selected the image will be scaled and cropped to fit the supplied "
 "dimensions."
@@ -261,21 +281,21 @@ msgstr ""
 "Si se selecciona la imagen será escalada y recortada para ajustarse a las "
 "dimensiones proporcionadas."
 
-#: photologue/models.py:589
+#: photologue/models.py:608
 msgid "pre-cache?"
 msgstr "¿pre-cachear?"
 
-#: photologue/models.py:591
+#: photologue/models.py:610
 msgid "If selected this photo size will be pre-cached as photos are added."
 msgstr ""
 "Si se selecciona, este tamaño de foto será pre-cacheado cuando se añadan "
 "nuevas fotos."
 
-#: photologue/models.py:592
+#: photologue/models.py:611
 msgid "increment view count?"
 msgstr "¿incrementar contador de visualizaciones?"
 
-#: photologue/models.py:594
+#: photologue/models.py:613
 msgid ""
 "If selected the image's \"view_count\" will be incremented when this photo "
 "size is displayed."
@@ -283,31 +303,31 @@ msgstr ""
 "Si se selecciona el \"contador de visualizaciones\" se incrementará cuando "
 "esta foto sea visualizada."
 
-#: photologue/models.py:599
+#: photologue/models.py:618
 msgid "photo size"
 msgstr "tamaño de foto"
 
-#: photologue/models.py:600
+#: photologue/models.py:619
 msgid "photo sizes"
 msgstr "tamaños de foto"
 
-#: photologue/models.py:617
+#: photologue/models.py:636
 msgid "Can only crop photos if both width and height dimensions are set."
 msgstr "Solo puede recortar las fotos si ancho y alto están establecidos."
 
-#: photologue_custom/admin.py:43 photologue_custom/models.py:51
-msgid "owner"
+#: photologue/models.py:702
+msgid "tag"
 msgstr ""
 
-#: photologue_custom/forms.py:34
+#: photologue_custom/forms.py:22
 msgid "Gallery"
 msgstr "Galería"
 
-#: photologue_custom/forms.py:36
+#: photologue_custom/forms.py:24
 msgid "-- Create a new gallery --"
 msgstr ""
 
-#: photologue_custom/forms.py:37
+#: photologue_custom/forms.py:25
 msgid ""
 "Select a gallery to add these images to. Leave this empty to create a new "
 "gallery from the supplied title."
@@ -315,58 +335,42 @@ msgstr ""
 "Seleccione una galería para agregarle estas imágenes. Déjelo vacío para "
 "crear una nueva galería a partir de este título."
 
-#: photologue_custom/forms.py:41
-#, fuzzy
-#| msgid "View all galleries"
+#: photologue_custom/forms.py:29
 msgid "New gallery title"
-msgstr "Ver todas las galerías"
+msgstr ""
 
-#: photologue_custom/forms.py:46
+#: photologue_custom/forms.py:34
 msgid "New gallery event start date"
 msgstr ""
 
-#: photologue_custom/forms.py:51
+#: photologue_custom/forms.py:39
 msgid "New gallery event end date"
 msgstr ""
 
-#: photologue_custom/forms.py:57
-#, fuzzy
-#| msgid "gallery"
+#: photologue_custom/forms.py:45
 msgid "New gallery tags"
-msgstr "galería"
+msgstr ""
 
-#: photologue_custom/forms.py:59
+#: photologue_custom/forms.py:47
 msgid ""
 "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
 msgstr ""
 
-#: photologue_custom/forms.py:76
+#: photologue_custom/forms.py:64
 #: photologue_custom/templates/photologue/upload.html:6
 #: photologue_custom/templates/photologue/upload.html:73
 msgid "Upload"
 msgstr "Subir"
 
-#: photologue_custom/forms.py:82
+#: photologue_custom/forms.py:70
 msgid "A gallery with that title already exists."
 msgstr "Ya existe una galería con ese título."
 
-#: photologue_custom/forms.py:91
+#: photologue_custom/forms.py:79
 msgid "Select an existing gallery, or enter a title for a new gallery."
 msgstr ""
 "Seleccione una galería existente o ingrese un nuevo nombre para la galería."
 
-#: photologue_custom/models.py:23
-msgid "start date"
-msgstr ""
-
-#: photologue_custom/models.py:28
-msgid "end date"
-msgstr ""
-
-#: photologue_custom/models.py:56
-msgid "license"
-msgstr ""
-
 #: photologue_custom/templates/photologue/gallery_archive.html:7
 #: photologue_custom/templates/photologue/gallery_archive.html:12
 msgid "Latest photo galleries"
@@ -399,16 +403,12 @@ msgid "to"
 msgstr ""
 
 #: photologue_custom/templates/photologue/gallery_detail.html:57
-#, fuzzy
-#| msgid "All photos"
 msgid "All pictures"
-msgstr "Todas las fotos"
+msgstr ""
 
 #: photologue_custom/templates/photologue/gallery_detail.html:78
-#, fuzzy
-#| msgid "View all galleries"
 msgid "Download all gallery"
-msgstr "Ver todas las galerías"
+msgstr ""
 
 #: photologue_custom/templates/photologue/photo_detail.html:13
 msgid "Published"
diff --git a/photologue/locale/fr/LC_MESSAGES/django.po b/photologue/locale/fr/LC_MESSAGES/django.po
index f3b69fc..a996627 100644
--- a/photologue/locale/fr/LC_MESSAGES/django.po
+++ b/photologue/locale/fr/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Photologue\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-01-30 07:09+0000\n"
+"POT-Creation-Date: 2022-01-30 10:06+0000\n"
 "PO-Revision-Date: 2017-12-03 14:47+0000\n"
 "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
 "Language-Team: French (http://www.transifex.com/richardbarran/django-"
@@ -21,75 +21,131 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: photologue/models.py:86
+#: photologue/admin.py:30 photologue/models.py:499
+msgid "owner"
+msgstr "propriétaire"
+
+#: photologue/forms.py:23
+msgid "Gallery"
+msgstr "Galerie"
+
+#: photologue/forms.py:25
+msgid "-- Create a new gallery --"
+msgstr "-- Créer une nouvelle galerie --"
+
+#: photologue/forms.py:26
+msgid ""
+"Select a gallery to add these images to. Leave this empty to create a new "
+"gallery from the supplied title."
+msgstr ""
+"Sélectionner une galerie à laquelle ajouter ces images. Laisser ce champ "
+"vide pour créer une nouvelle galerie à partir du titre indiqué."
+
+#: photologue/forms.py:30
+msgid "New gallery title"
+msgstr "Titre de la nouvelle galerie"
+
+#: photologue/forms.py:35
+msgid "New gallery event start date"
+msgstr "Date de début de l'évènement de la nouvelle galerie"
+
+#: photologue/forms.py:40
+msgid "New gallery event end date"
+msgstr "Date de fin de l'évènement de la nouvelle galerie"
+
+#: photologue/forms.py:46
+msgid "New gallery tags"
+msgstr "Balises de la nouvelle galerie"
+
+#: photologue/forms.py:48
+msgid ""
+"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
+msgstr ""
+
+#: photologue/forms.py:65 photologue/templates/photologue/upload.html:6
+#: photologue/templates/photologue/upload.html:73
+msgid "Upload"
+msgstr "Télécharger"
+
+#: photologue/forms.py:71
+msgid "A gallery with that title already exists."
+msgstr "Une galerie portant ce nom existe déjà."
+
+#: photologue/forms.py:80
+msgid "Select an existing gallery, or enter a title for a new gallery."
+msgstr ""
+"Sélectionner une galerie existante ou entrer un titre pour une nouvelle "
+"galerie."
+
+#: photologue/models.py:84
 msgid "Very Low"
 msgstr "Très Bas"
 
-#: photologue/models.py:87
+#: photologue/models.py:85
 msgid "Low"
 msgstr "Bas"
 
-#: photologue/models.py:88
+#: photologue/models.py:86
 msgid "Medium-Low"
 msgstr "Moyen-Bas"
 
-#: photologue/models.py:89
+#: photologue/models.py:87
 msgid "Medium"
 msgstr "Moyen"
 
-#: photologue/models.py:90
+#: photologue/models.py:88
 msgid "Medium-High"
 msgstr "Moyen-Haut"
 
-#: photologue/models.py:91
+#: photologue/models.py:89
 msgid "High"
 msgstr "Haut"
 
-#: photologue/models.py:92
+#: photologue/models.py:90
 msgid "Very High"
 msgstr "Très Haut"
 
-#: photologue/models.py:97
+#: photologue/models.py:95
 msgid "Top"
 msgstr "Sommet"
 
-#: photologue/models.py:98
+#: photologue/models.py:96
 msgid "Right"
 msgstr "Droite"
 
-#: photologue/models.py:99
+#: photologue/models.py:97
 msgid "Bottom"
 msgstr "Bas"
 
-#: photologue/models.py:100
+#: photologue/models.py:98
 msgid "Left"
 msgstr "Gauche"
 
-#: photologue/models.py:101
+#: photologue/models.py:99
 msgid "Center (Default)"
 msgstr "Centré (par défaut)"
 
-#: photologue/models.py:105
+#: photologue/models.py:103
 msgid "Flip left to right"
 msgstr "Inversion de gauche à droite"
 
-#: photologue/models.py:106
+#: photologue/models.py:104
 msgid "Flip top to bottom"
 msgstr "Inversion de haut en bas"
 
-#: photologue/models.py:107
+#: photologue/models.py:105
 msgid "Rotate 90 degrees counter-clockwise"
 msgstr "Rotation de 90 degrés dans le sens anti-horloger"
 
-#: photologue/models.py:108
+#: photologue/models.py:106
 msgid "Rotate 90 degrees clockwise"
 msgstr "Rotation de 90 degrés dans le sens horloger"
 
-#: photologue/models.py:109
+#: photologue/models.py:107
 msgid "Rotate 180 degrees"
 msgstr "Rotation de 180 degrés"
 
-#: photologue/models.py:119
+#: photologue/models.py:117
 #, python-format
 msgid ""
 "Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
@@ -100,107 +156,123 @@ msgstr ""
 ">FILTRE_DEUX->FILTRE_TROIS\". Les filtres d'image seront appliqués dans "
 "l'ordre. Les filtres suivants sont disponibles: %s."
 
-#: photologue/models.py:141
+#: photologue/models.py:139
 msgid "date published"
 msgstr "date de publication"
 
-#: photologue/models.py:143 photologue/models.py:474
+#: photologue/models.py:141 photologue/models.py:485
 msgid "title"
 msgstr "titre"
 
-#: photologue/models.py:146
+#: photologue/models.py:144
 msgid "title slug"
 msgstr "version abrégée du titre"
 
-#: photologue/models.py:149 photologue/models.py:480
+#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
 msgid "A \"slug\" is a unique URL-friendly title for an object."
 msgstr ""
 "Un \"slug\" est un titre abrégé et unique, compatible avec les URL, pour un "
 "objet."
 
 #: photologue/models.py:150
+msgid "start date"
+msgstr "date de début"
+
+#: photologue/models.py:155
+msgid "end date"
+msgstr "date de fin"
+
+#: photologue/models.py:157
 msgid "description"
 msgstr "description"
 
-#: photologue/models.py:152 photologue/models.py:485
+#: photologue/models.py:162 photologue/models.py:703
+msgid "tags"
+msgstr "balises"
+
+#: photologue/models.py:165 photologue/models.py:506
 msgid "is public"
 msgstr "est public"
 
-#: photologue/models.py:154
+#: photologue/models.py:167
 msgid "Public galleries will be displayed in the default views."
 msgstr "Les galeries publiques seront affichée dans les vues par défaut."
 
-#: photologue/models.py:158 photologue/models.py:495
+#: photologue/models.py:171 photologue/models.py:514
 msgid "photos"
 msgstr "photos"
 
-#: photologue/models.py:166
+#: photologue/models.py:177
 msgid "gallery"
 msgstr "galerie"
 
-#: photologue/models.py:167
+#: photologue/models.py:178
 msgid "galleries"
 msgstr "galleries"
 
-#: photologue/models.py:202
+#: photologue/models.py:213
 msgid "count"
 msgstr "nombre"
 
-#: photologue/models.py:210
+#: photologue/models.py:221
 msgid "image"
 msgstr "image"
 
-#: photologue/models.py:213
+#: photologue/models.py:224
 msgid "date taken"
 msgstr "date de prise de vue"
 
-#: photologue/models.py:216
+#: photologue/models.py:227
 msgid "Date image was taken; is obtained from the image EXIF data."
 msgstr ""
 "La date à laquelle l'image a été prise ; obtenue à partir des données EXIF "
 "de l'image."
 
-#: photologue/models.py:217
+#: photologue/models.py:228
 msgid "view count"
 msgstr "nombre"
 
-#: photologue/models.py:220
+#: photologue/models.py:231
 msgid "crop from"
 msgstr "découper à partir de"
 
-#: photologue/models.py:243
+#: photologue/models.py:254
 msgid "An \"admin_thumbnail\" photo size has not been defined."
 msgstr "Une taille de photo \"admin_thumbnail\" n'a pas encore été définie."
 
-#: photologue/models.py:250
+#: photologue/models.py:261
 msgid "Thumbnail"
 msgstr "Miniature"
 
-#: photologue/models.py:477
+#: photologue/models.py:488 photologue/models.py:696
 msgid "slug"
 msgstr "libellé court"
 
-#: photologue/models.py:481
+#: photologue/models.py:492
 msgid "caption"
 msgstr "légende"
 
-#: photologue/models.py:483
+#: photologue/models.py:494
 msgid "date added"
 msgstr "date d'ajout"
 
-#: photologue/models.py:487
+#: photologue/models.py:504
+msgid "license"
+msgstr "licence"
+
+#: photologue/models.py:508
 msgid "Public photographs will be displayed in the default views."
 msgstr "Les photographies publique seront affichées dans les vues par défaut."
 
-#: photologue/models.py:494
+#: photologue/models.py:513
 msgid "photo"
 msgstr "photo"
 
-#: photologue/models.py:556
+#: photologue/models.py:575 photologue/models.py:691
 msgid "name"
 msgstr "nom"
 
-#: photologue/models.py:560
+#: photologue/models.py:579
 msgid ""
 "Photo size name should contain only letters, numbers and underscores. "
 "Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
@@ -209,41 +281,41 @@ msgstr ""
 "chiffres et des caractères de soulignement. Exemples: \"miniature\", "
 "\"affichage\", \"petit\", \"widget_page_principale\"."
 
-#: photologue/models.py:567
+#: photologue/models.py:586
 msgid "width"
 msgstr "largeur"
 
-#: photologue/models.py:570
+#: photologue/models.py:589
 msgid ""
 "If width is set to \"0\" the image will be scaled to the supplied height."
 msgstr ""
 "Si la largeur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
 "la hauteur fournie."
 
-#: photologue/models.py:571
+#: photologue/models.py:590
 msgid "height"
 msgstr "hauteur"
 
-#: photologue/models.py:574
+#: photologue/models.py:593
 msgid ""
 "If height is set to \"0\" the image will be scaled to the supplied width"
 msgstr ""
 "Si la hauteur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
 "la largeur fournie."
 
-#: photologue/models.py:575
+#: photologue/models.py:594
 msgid "quality"
 msgstr "qualité"
 
-#: photologue/models.py:578
+#: photologue/models.py:597
 msgid "JPEG image quality."
 msgstr "Qualité JPEG de l'image."
 
-#: photologue/models.py:579
+#: photologue/models.py:598
 msgid "upscale images?"
 msgstr "agrandir les images ?"
 
-#: photologue/models.py:581
+#: photologue/models.py:600
 msgid ""
 "If selected the image will be scaled up if necessary to fit the supplied "
 "dimensions. Cropped sizes will be upscaled regardless of this setting."
@@ -252,11 +324,11 @@ msgstr ""
 "dimensions fournies. Les dimensions ajustées seront agrandies sans prendre "
 "en compte ce paramètre."
 
-#: photologue/models.py:585
+#: photologue/models.py:604
 msgid "crop to fit?"
 msgstr "découper pour adapter à la taille ?"
 
-#: photologue/models.py:587
+#: photologue/models.py:606
 msgid ""
 "If selected the image will be scaled and cropped to fit the supplied "
 "dimensions."
@@ -264,21 +336,21 @@ msgstr ""
 "Si sélectionné l'image sera redimensionnée et recadrée pour coïncider avec "
 "les dimensions fournies."
 
-#: photologue/models.py:589
+#: photologue/models.py:608
 msgid "pre-cache?"
 msgstr "mise en cache ?"
 
-#: photologue/models.py:591
+#: photologue/models.py:610
 msgid "If selected this photo size will be pre-cached as photos are added."
 msgstr ""
 "Si sélectionné cette taille de photo sera mise en cache au moment au les "
 "photos sont ajoutées."
 
-#: photologue/models.py:592
+#: photologue/models.py:611
 msgid "increment view count?"
 msgstr "incrémenter le nombre d'affichages ?"
 
-#: photologue/models.py:594
+#: photologue/models.py:613
 msgid ""
 "If selected the image's \"view_count\" will be incremented when this photo "
 "size is displayed."
@@ -286,148 +358,75 @@ msgstr ""
 "Si sélectionné le \"view_count\" (nombre d'affichage) de l'image sera "
 "incrémenté quand cette taille de photo sera affichée."
 
-#: photologue/models.py:599
+#: photologue/models.py:618
 msgid "photo size"
 msgstr "taille de la photo"
 
-#: photologue/models.py:600
+#: photologue/models.py:619
 msgid "photo sizes"
 msgstr "tailles des photos"
 
-#: photologue/models.py:617
+#: photologue/models.py:636
 msgid "Can only crop photos if both width and height dimensions are set."
 msgstr ""
 "La hauteur et la largeur doivent être toutes les deux définies pour "
 "retailler des photos."
 
-#: photologue_custom/admin.py:43 photologue_custom/models.py:51
-msgid "owner"
-msgstr ""
-
-#: photologue_custom/forms.py:34
-msgid "Gallery"
-msgstr "Galerie"
-
-#: photologue_custom/forms.py:36
-msgid "-- Create a new gallery --"
-msgstr ""
-
-#: photologue_custom/forms.py:37
-msgid ""
-"Select a gallery to add these images to. Leave this empty to create a new "
-"gallery from the supplied title."
-msgstr ""
-"Sélectionner une galerie à laquelle ajouter ces images. Laisser ce champ "
-"vide pour créer une nouvelle galerie à partir du titre indiqué."
-
-#: photologue_custom/forms.py:41
-#, fuzzy
-#| msgid "View all galleries"
-msgid "New gallery title"
-msgstr "Afficher toutes les galeries"
-
-#: photologue_custom/forms.py:46
-msgid "New gallery event start date"
-msgstr ""
-
-#: photologue_custom/forms.py:51
-msgid "New gallery event end date"
-msgstr ""
-
-#: photologue_custom/forms.py:57
-#, fuzzy
-#| msgid "gallery uploads"
-msgid "New gallery tags"
-msgstr "gallery uploads"
-
-#: photologue_custom/forms.py:59
-msgid ""
-"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
+#: photologue/models.py:702
+msgid "tag"
 msgstr ""
 
-#: photologue_custom/forms.py:76
-#: photologue_custom/templates/photologue/upload.html:6
-#: photologue_custom/templates/photologue/upload.html:73
-msgid "Upload"
-msgstr "Télécharger"
-
-#: photologue_custom/forms.py:82
-msgid "A gallery with that title already exists."
-msgstr "Une galerie portant ce nom existe déjà."
-
-#: photologue_custom/forms.py:91
-msgid "Select an existing gallery, or enter a title for a new gallery."
-msgstr ""
-"Sélectionner une galerie existante ou entrer un titre pour une nouvelle "
-"galerie."
-
-#: photologue_custom/models.py:23
-msgid "start date"
-msgstr ""
-
-#: photologue_custom/models.py:28
-msgid "end date"
-msgstr ""
-
-#: photologue_custom/models.py:56
-msgid "license"
-msgstr ""
-
-#: photologue_custom/templates/photologue/gallery_archive.html:7
-#: photologue_custom/templates/photologue/gallery_archive.html:12
+#: photologue/templates/photologue/gallery_archive.html:7
+#: photologue/templates/photologue/gallery_archive.html:12
 msgid "Latest photo galleries"
 msgstr "Dernières galeries de photos"
 
-#: photologue_custom/templates/photologue/gallery_archive.html:18
+#: photologue/templates/photologue/gallery_archive.html:18
 msgid "Filter by year"
 msgstr "Filtrer par année"
 
-#: photologue_custom/templates/photologue/gallery_archive.html:35
+#: photologue/templates/photologue/gallery_archive.html:35
 msgid "No galleries were found"
 msgstr "Aucune galerie trouvée"
 
-#: photologue_custom/templates/photologue/gallery_archive_year.html:7
-#: photologue_custom/templates/photologue/gallery_archive_year.html:12
+#: photologue/templates/photologue/gallery_archive_year.html:7
+#: photologue/templates/photologue/gallery_archive_year.html:12
 #, python-format
 msgid "Galleries for %(show_year)s"
 msgstr "Galeries de %(show_year)s"
 
-#: photologue_custom/templates/photologue/gallery_archive_year.html:17
+#: photologue/templates/photologue/gallery_archive_year.html:17
 msgid "View all galleries"
 msgstr "Afficher toutes les galeries"
 
-#: photologue_custom/templates/photologue/gallery_archive_year.html:29
+#: photologue/templates/photologue/gallery_archive_year.html:29
 msgid "No galleries were found."
 msgstr "Aucune galerie trouvée."
 
-#: photologue_custom/templates/photologue/gallery_detail.html:41
+#: photologue/templates/photologue/gallery_detail.html:41
 msgid "to"
-msgstr ""
+msgstr "au"
 
-#: photologue_custom/templates/photologue/gallery_detail.html:57
-#, fuzzy
-#| msgid "All photos"
+#: photologue/templates/photologue/gallery_detail.html:57
 msgid "All pictures"
 msgstr "Toutes les photos"
 
-#: photologue_custom/templates/photologue/gallery_detail.html:78
-#, fuzzy
-#| msgid "View all galleries"
+#: photologue/templates/photologue/gallery_detail.html:78
 msgid "Download all gallery"
-msgstr "Afficher toutes les galeries"
+msgstr "Télécharger toute la galerie"
 
-#: photologue_custom/templates/photologue/photo_detail.html:13
+#: photologue/templates/photologue/photo_detail.html:13
 msgid "Published"
 msgstr "Publiée le"
 
-#: photologue_custom/templates/photologue/photo_detail.html:25
+#: photologue/templates/photologue/photo_detail.html:25
 msgid "This photo is found in the following galleries"
 msgstr "Cette photo se trouve dans les galeries suivantes"
 
-#: photologue_custom/templates/photologue/upload.html:78
+#: photologue/templates/photologue/upload.html:78
 msgid "Drag and drop photos here"
-msgstr ""
+msgstr "Glissez et déposez les photos ici"
 
-#: photologue_custom/templates/photologue/upload.html:82
+#: photologue/templates/photologue/upload.html:82
 msgid "Owner will be"
-msgstr ""
+msgstr "Le propriétaire sera"
diff --git a/photologue_custom/management/commands/duplicate.py b/photologue/management/commands/duplicate.py
similarity index 100%
rename from photologue_custom/management/commands/duplicate.py
rename to photologue/management/commands/duplicate.py
index 97a902a..56ce185 100644
--- a/photologue_custom/management/commands/duplicate.py
+++ b/photologue/management/commands/duplicate.py
@@ -1,8 +1,8 @@
+import hashlib
+
 from django.core.management.base import BaseCommand, CommandError
 from photologue.models import Gallery
 
-import hashlib
-
 
 class Command(BaseCommand):
     help = 'List all duplicate for chosen galleries'
diff --git a/photologue_custom/management/commands/rename_media.py b/photologue/management/commands/rename_media.py
similarity index 100%
rename from photologue_custom/management/commands/rename_media.py
rename to photologue/management/commands/rename_media.py
index 461870b..1f8767e 100644
--- a/photologue_custom/management/commands/rename_media.py
+++ b/photologue/management/commands/rename_media.py
@@ -1,9 +1,9 @@
-from pathlib import Path
 import os
+from pathlib import Path
 
+from django.conf import settings
 from django.core.management.base import BaseCommand
 from photologue.models import Gallery
-from django.conf import settings
 
 
 class Command(BaseCommand):
diff --git a/photologue/migrations/0014_merge_related.py b/photologue/migrations/0014_merge_related.py
index 2608233..8dfa458 100644
--- a/photologue/migrations/0014_merge_related.py
+++ b/photologue/migrations/0014_merge_related.py
@@ -1,12 +1,12 @@
 # Generated by Django 3.2.11 on 2022-01-30 08:32
 
-from django.conf import settings
-from django.db import migrations, models
 import django.db.models.deletion
 import django.utils.timezone
 import taggit.managers
+from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.models import ContentType
+from django.db import migrations, models
 from django.template.defaultfilters import slugify
 
 numens = User.objects.get(username="Numens").id
diff --git a/photologue_custom/static/lightgallery/css/lg-thumbnail.css b/photologue/static/lightgallery/css/lg-thumbnail.css
similarity index 100%
rename from photologue_custom/static/lightgallery/css/lg-thumbnail.css
rename to photologue/static/lightgallery/css/lg-thumbnail.css
diff --git a/photologue_custom/static/lightgallery/css/lg-zoom.css b/photologue/static/lightgallery/css/lg-zoom.css
similarity index 100%
rename from photologue_custom/static/lightgallery/css/lg-zoom.css
rename to photologue/static/lightgallery/css/lg-zoom.css
diff --git a/photologue_custom/static/lightgallery/css/lightgallery.css b/photologue/static/lightgallery/css/lightgallery.css
similarity index 100%
rename from photologue_custom/static/lightgallery/css/lightgallery.css
rename to photologue/static/lightgallery/css/lightgallery.css
diff --git a/photologue_custom/static/lightgallery/fonts/lg.svg b/photologue/static/lightgallery/fonts/lg.svg
similarity index 100%
rename from photologue_custom/static/lightgallery/fonts/lg.svg
rename to photologue/static/lightgallery/fonts/lg.svg
diff --git a/photologue_custom/static/lightgallery/fonts/lg.ttf b/photologue/static/lightgallery/fonts/lg.ttf
similarity index 100%
rename from photologue_custom/static/lightgallery/fonts/lg.ttf
rename to photologue/static/lightgallery/fonts/lg.ttf
diff --git a/photologue_custom/static/lightgallery/fonts/lg.woff b/photologue/static/lightgallery/fonts/lg.woff
similarity index 100%
rename from photologue_custom/static/lightgallery/fonts/lg.woff
rename to photologue/static/lightgallery/fonts/lg.woff
diff --git a/photologue_custom/static/lightgallery/fonts/lg.woff2 b/photologue/static/lightgallery/fonts/lg.woff2
similarity index 100%
rename from photologue_custom/static/lightgallery/fonts/lg.woff2
rename to photologue/static/lightgallery/fonts/lg.woff2
diff --git a/photologue_custom/static/lightgallery/images/loading.gif b/photologue/static/lightgallery/images/loading.gif
similarity index 100%
rename from photologue_custom/static/lightgallery/images/loading.gif
rename to photologue/static/lightgallery/images/loading.gif
diff --git a/photologue_custom/static/lightgallery/lightgallery.min.js b/photologue/static/lightgallery/lightgallery.min.js
similarity index 100%
rename from photologue_custom/static/lightgallery/lightgallery.min.js
rename to photologue/static/lightgallery/lightgallery.min.js
diff --git a/photologue_custom/static/lightgallery/plugins/admin/lg-admin.js b/photologue/static/lightgallery/plugins/admin/lg-admin.js
similarity index 100%
rename from photologue_custom/static/lightgallery/plugins/admin/lg-admin.js
rename to photologue/static/lightgallery/plugins/admin/lg-admin.js
diff --git a/photologue_custom/static/lightgallery/plugins/hash/lg-hash.min.js b/photologue/static/lightgallery/plugins/hash/lg-hash.min.js
similarity index 100%
rename from photologue_custom/static/lightgallery/plugins/hash/lg-hash.min.js
rename to photologue/static/lightgallery/plugins/hash/lg-hash.min.js
diff --git a/photologue_custom/static/lightgallery/plugins/thumbnail/lg-thumbnail.min.js b/photologue/static/lightgallery/plugins/thumbnail/lg-thumbnail.min.js
similarity index 100%
rename from photologue_custom/static/lightgallery/plugins/thumbnail/lg-thumbnail.min.js
rename to photologue/static/lightgallery/plugins/thumbnail/lg-thumbnail.min.js
diff --git a/photologue_custom/static/lightgallery/plugins/zoom/lg-zoom.min.js b/photologue/static/lightgallery/plugins/zoom/lg-zoom.min.js
similarity index 100%
rename from photologue_custom/static/lightgallery/plugins/zoom/lg-zoom.min.js
rename to photologue/static/lightgallery/plugins/zoom/lg-zoom.min.js
diff --git a/photologue_custom/templates/admin/photologue/photo/change_list.html b/photologue/templates/admin/photologue/photo/change_list.html
similarity index 100%
rename from photologue_custom/templates/admin/photologue/photo/change_list.html
rename to photologue/templates/admin/photologue/photo/change_list.html
diff --git a/photologue_custom/templates/photologue/gallery_archive.html b/photologue/templates/photologue/gallery_archive.html
similarity index 100%
rename from photologue_custom/templates/photologue/gallery_archive.html
rename to photologue/templates/photologue/gallery_archive.html
diff --git a/photologue_custom/templates/photologue/gallery_archive_year.html b/photologue/templates/photologue/gallery_archive_year.html
similarity index 100%
rename from photologue_custom/templates/photologue/gallery_archive_year.html
rename to photologue/templates/photologue/gallery_archive_year.html
diff --git a/photologue_custom/templates/photologue/gallery_detail.html b/photologue/templates/photologue/gallery_detail.html
similarity index 100%
rename from photologue_custom/templates/photologue/gallery_detail.html
rename to photologue/templates/photologue/gallery_detail.html
diff --git a/photologue_custom/templates/photologue/includes/gallery_sample.html b/photologue/templates/photologue/includes/gallery_sample.html
similarity index 100%
rename from photologue_custom/templates/photologue/includes/gallery_sample.html
rename to photologue/templates/photologue/includes/gallery_sample.html
diff --git a/photologue_custom/templates/photologue/photo_detail.html b/photologue/templates/photologue/photo_detail.html
similarity index 100%
rename from photologue_custom/templates/photologue/photo_detail.html
rename to photologue/templates/photologue/photo_detail.html
diff --git a/photologue_custom/templates/photologue/tag_detail.html b/photologue/templates/photologue/tag_detail.html
similarity index 100%
rename from photologue_custom/templates/photologue/tag_detail.html
rename to photologue/templates/photologue/tag_detail.html
diff --git a/photologue_custom/templates/photologue/upload.html b/photologue/templates/photologue/upload.html
similarity index 100%
rename from photologue_custom/templates/photologue/upload.html
rename to photologue/templates/photologue/upload.html
diff --git a/photologue/urls.py b/photologue/urls.py
index 4772067..e4949f8 100644
--- a/photologue/urls.py
+++ b/photologue/urls.py
@@ -1,7 +1,8 @@
 from django.urls import path, re_path
-from photologue_custom.views import CustomGalleryDetailView, GalleryDownload, GalleryUpload, TagDetail
 
-from .views import GalleryArchiveIndexView, GalleryYearArchiveView, PhotoDetailView
+from .views import (CustomGalleryDetailView, GalleryArchiveIndexView,
+                    GalleryDownload, GalleryUpload, GalleryYearArchiveView,
+                    PhotoDetailView, TagDetail)
 
 app_name = 'photologue'
 urlpatterns = [
diff --git a/photologue/views.py b/photologue/views.py
index 2741b50..77953a8 100644
--- a/photologue/views.py
+++ b/photologue/views.py
@@ -1,8 +1,26 @@
-from django.contrib.auth.mixins import LoginRequiredMixin
+# 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.dates import ArchiveIndexView, YearArchiveView
 from django.views.generic.detail import DetailView
+from django.views.generic.edit import FormView
+from PIL import Image
 
-from .models import Gallery, Photo
+from .forms import UploadForm
+from .models import Gallery, Photo, Tag
 
 
 class GalleryDateView(LoginRequiredMixin):
@@ -22,3 +40,125 @@ class GalleryYearArchiveView(GalleryDateView, YearArchiveView):
 
 class PhotoDetailView(LoginRequiredMixin, DetailView):
     queryset = Photo.objects.filter(is_public=True)
+
+
+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)
diff --git a/photologue_custom/views.py b/photologue_custom/views.py
deleted file mode 100644
index 38ebc7f..0000000
--- a/photologue_custom/views.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# 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)
diff --git a/tox.ini b/tox.ini
index f69e975..885d2da 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,7 +12,7 @@ deps =
     -r{toxinidir}/requirements.txt
     coverage
 commands =
-    coverage run --omit='photo21/wsgi.py' --source=photo21,photologue,photologue_custom ./manage.py test
+    coverage run --omit='photo21/wsgi.py' --source=photo21,photologue ./manage.py test
     coverage report -m
 
 [testenv:linters]
@@ -26,7 +26,7 @@ deps =
     pep8-naming
     pyflakes
 commands =
-    flake8 photo21 photologue photologue_custom
+    flake8 photo21 photologue
 
 [flake8]
 ignore = W503, I100, I101
-- 
GitLab


From 80085edeeb4eecf44f075176e306bdcf2ab1d4b1 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 11:15:41 +0100
Subject: [PATCH 7/9] Reset migrations

---
 photo21/settings.py                           |   3 -
 photologue/migrations/0001_initial.py         | 166 ++++++------------
 photologue/migrations/0002_photosize_data.py  |  43 -----
 .../migrations/0003_auto_20140822_1716.py     |  19 --
 .../migrations/0004_auto_20140915_1259.py     |  35 ----
 .../migrations/0005_auto_20141027_1552.py     |  20 ---
 .../migrations/0006_auto_20141028_2005.py     |  21 ---
 .../migrations/0007_auto_20150404_1737.py     |  30 ----
 .../migrations/0008_auto_20150509_1557.py     |  22 ---
 .../migrations/0009_auto_20160102_0904.py     |  20 ---
 .../migrations/0010_auto_20160105_1307.py     |  35 ----
 .../migrations/0011_auto_20190223_2138.py     |  18 --
 .../migrations/0012_auto_20220129_2207.py     |  39 ----
 .../migrations/0013_alter_gallery_photos.py   |  18 --
 photologue/migrations/0014_merge_related.py   |  96 ----------
 photologue_custom/__init__.py                 |   0
 photologue_custom/apps.py                     |   6 -
 photologue_custom/migrations/0001_initial.py  |  44 -----
 .../migrations/0002_auto_20211011_1956.py     |  23 ---
 .../migrations/0003_auto_20211013_1507.py     |  24 ---
 .../migrations/0004_photoextended_license.py  |  18 --
 .../migrations/0005_auto_20220130_0953.py     |  27 ---
 photologue_custom/migrations/__init__.py      |   0
 23 files changed, 52 insertions(+), 675 deletions(-)
 delete mode 100644 photologue/migrations/0002_photosize_data.py
 delete mode 100644 photologue/migrations/0003_auto_20140822_1716.py
 delete mode 100644 photologue/migrations/0004_auto_20140915_1259.py
 delete mode 100644 photologue/migrations/0005_auto_20141027_1552.py
 delete mode 100644 photologue/migrations/0006_auto_20141028_2005.py
 delete mode 100644 photologue/migrations/0007_auto_20150404_1737.py
 delete mode 100644 photologue/migrations/0008_auto_20150509_1557.py
 delete mode 100644 photologue/migrations/0009_auto_20160102_0904.py
 delete mode 100644 photologue/migrations/0010_auto_20160105_1307.py
 delete mode 100644 photologue/migrations/0011_auto_20190223_2138.py
 delete mode 100644 photologue/migrations/0012_auto_20220129_2207.py
 delete mode 100644 photologue/migrations/0013_alter_gallery_photos.py
 delete mode 100644 photologue/migrations/0014_merge_related.py
 delete mode 100644 photologue_custom/__init__.py
 delete mode 100644 photologue_custom/apps.py
 delete mode 100644 photologue_custom/migrations/0001_initial.py
 delete mode 100644 photologue_custom/migrations/0002_auto_20211011_1956.py
 delete mode 100644 photologue_custom/migrations/0003_auto_20211013_1507.py
 delete mode 100644 photologue_custom/migrations/0004_photoextended_license.py
 delete mode 100644 photologue_custom/migrations/0005_auto_20220130_0953.py
 delete mode 100644 photologue_custom/migrations/__init__.py

diff --git a/photo21/settings.py b/photo21/settings.py
index 9b2844b..e6773ec 100644
--- a/photo21/settings.py
+++ b/photo21/settings.py
@@ -63,7 +63,6 @@ INSTALLED_APPS = [
     'allauth_note_kfet',
     'crispy_forms',
     'photologue',
-    'taggit',
 ]
 
 MIDDLEWARE = [
@@ -155,8 +154,6 @@ USE_L10N = True
 
 USE_TZ = True
 
-DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
-
 # Limit available languages to this subset
 LANGUAGES = [
     ('de', _('German')),
diff --git a/photologue/migrations/0001_initial.py b/photologue/migrations/0001_initial.py
index 35d3b3d..47bca5b 100644
--- a/photologue/migrations/0001_initial.py
+++ b/photologue/migrations/0001_initial.py
@@ -1,158 +1,96 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
+# Generated by Django 3.2.11 on 2022-01-30 10:14
 
+from django.conf import settings
 import django.core.validators
-import django.utils.timezone
-import sortedm2m.fields
 from django.db import migrations, models
-
+import django.db.models.deletion
+import django.utils.timezone
 import photologue.models
 
 
 class Migration(migrations.Migration):
 
+    initial = True
+
     dependencies = [
-        ('sites', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
 
     operations = [
         migrations.CreateModel(
-            name='Gallery',
+            name='PhotoSize',
             fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
-                ('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published')),
-                ('title', models.CharField(max_length=50, verbose_name='title', unique=True)),
-                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', verbose_name='title slug', unique=True)),
-                ('description', models.TextField(blank=True, verbose_name='description')),
-                ('is_public', models.BooleanField(help_text='Public galleries will be displayed in the default views.', verbose_name='is public', default=True)),
-                ('tags', photologue.models.TagField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
-                ('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(help_text='Photo size name should contain only letters, numbers and underscores. Examples: "thumbnail", "display", "small", "main_page_widget".', max_length=40, unique=True, validators=[django.core.validators.RegexValidator(message='Use only plain lowercase letters (ASCII), numbers and underscores.', regex='^[a-z0-9_]+$')], verbose_name='name')),
+                ('width', models.PositiveIntegerField(default=0, help_text='If width is set to "0" the image will be scaled to the supplied height.', verbose_name='width')),
+                ('height', models.PositiveIntegerField(default=0, help_text='If height is set to "0" the image will be scaled to the supplied width', verbose_name='height')),
+                ('quality', models.PositiveIntegerField(choices=[(30, 'Very Low'), (40, 'Low'), (50, 'Medium-Low'), (60, 'Medium'), (70, 'Medium-High'), (80, 'High'), (90, 'Very High')], default=70, help_text='JPEG image quality.', verbose_name='quality')),
+                ('upscale', models.BooleanField(default=False, help_text='If selected the image will be scaled up if necessary to fit the supplied dimensions. Cropped sizes will be upscaled regardless of this setting.', verbose_name='upscale images?')),
+                ('crop', models.BooleanField(default=False, help_text='If selected the image will be scaled and cropped to fit the supplied dimensions.', verbose_name='crop to fit?')),
+                ('pre_cache', models.BooleanField(default=False, help_text='If selected this photo size will be pre-cached as photos are added.', verbose_name='pre-cache?')),
+                ('increment_count', models.BooleanField(default=False, help_text='If selected the image\'s "view_count" will be incremented when this photo size is displayed.', verbose_name='increment view count?')),
             ],
             options={
-                'get_latest_by': 'date_added',
-                'verbose_name': 'gallery',
-                'ordering': ['-date_added'],
-                'verbose_name_plural': 'galleries',
+                'verbose_name': 'photo size',
+                'verbose_name_plural': 'photo sizes',
+                'ordering': ['width', 'height'],
             },
-            bases=(models.Model,),
         ),
         migrations.CreateModel(
-            name='GalleryUpload',
+            name='Tag',
             fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
-                ('zip_file', models.FileField(help_text='Select a .zip file of images to upload into a new Gallery.', verbose_name='images file (.zip)', upload_to='photologue/temp')),
-                ('title', models.CharField(max_length=50, help_text='All uploaded photos will be given a title made up of this title + a sequential number.', verbose_name='title')),
-                ('caption', models.TextField(help_text='Caption will be added to all photos.', blank=True, verbose_name='caption')),
-                ('description', models.TextField(help_text='A description of this Gallery.', blank=True, verbose_name='description')),
-                ('is_public', models.BooleanField(help_text='Uncheck this to make the uploaded gallery and included photographs private.', verbose_name='is public', default=True)),
-                ('tags', models.CharField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
-                ('gallery', models.ForeignKey(blank=True, verbose_name='gallery', null=True, help_text='Select a gallery to add these images to. Leave this empty to create a new gallery from the supplied title.', to='photologue.Gallery', on_delete=models.CASCADE)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=250, unique=True, verbose_name='name')),
+                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
             ],
             options={
-                'verbose_name': 'gallery upload',
-                'verbose_name_plural': 'gallery uploads',
+                'verbose_name': 'tag',
+                'verbose_name_plural': 'tags',
+                'ordering': ['name'],
             },
-            bases=(models.Model,),
         ),
         migrations.CreateModel(
             name='Photo',
             fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('image', models.ImageField(upload_to=photologue.models.get_storage_path, verbose_name='image')),
-                ('date_taken', models.DateTimeField(verbose_name='date taken', blank=True, editable=False, null=True)),
-                ('view_count', models.PositiveIntegerField(verbose_name='view count', default=0, editable=False)),
-                ('crop_from', models.CharField(max_length=10, default='center', blank=True, verbose_name='crop from', choices=[('top', 'Top'), ('right', 'Right'), ('bottom', 'Bottom'), ('left', 'Left'), ('center', 'Center (Default)')])),
-                ('title', models.CharField(max_length=50, verbose_name='title', unique=True)),
-                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', verbose_name='slug', unique=True)),
+                ('date_taken', models.DateTimeField(blank=True, help_text='Date image was taken; is obtained from the image EXIF data.', null=True, verbose_name='date taken')),
+                ('view_count', models.PositiveIntegerField(default=0, editable=False, verbose_name='view count')),
+                ('crop_from', models.CharField(blank=True, choices=[('top', 'Top'), ('right', 'Right'), ('bottom', 'Bottom'), ('left', 'Left'), ('center', 'Center (Default)')], default='center', max_length=10, verbose_name='crop from')),
+                ('title', models.CharField(max_length=250, unique=True, verbose_name='title')),
+                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
                 ('caption', models.TextField(blank=True, verbose_name='caption')),
                 ('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date added')),
-                ('is_public', models.BooleanField(help_text='Public photographs will be displayed in the default views.', verbose_name='is public', default=True)),
-                ('tags', photologue.models.TagField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
-                ('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')),
+                ('license', models.CharField(blank=True, max_length=255, verbose_name='license')),
+                ('is_public', models.BooleanField(default=True, help_text='Public photographs will be displayed in the default views.', verbose_name='is public')),
+                ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='owner')),
             ],
             options={
-                'get_latest_by': 'date_added',
                 'verbose_name': 'photo',
-                'ordering': ['-date_added'],
                 'verbose_name_plural': 'photos',
+                'ordering': ['-date_added'],
+                'get_latest_by': 'date_added',
             },
-            bases=(models.Model,),
-        ),
-        migrations.AddField(
-            model_name='gallery',
-            name='photos',
-            field=sortedm2m.fields.SortedManyToManyField(blank=True, verbose_name='photos', null=True, to='photologue.Photo'),
-            preserve_default=True,
-        ),
-        migrations.CreateModel(
-            name='PhotoEffect',
-            fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
-                ('name', models.CharField(max_length=30, verbose_name='name', unique=True)),
-                ('description', models.TextField(blank=True, verbose_name='description')),
-                ('transpose_method', models.CharField(max_length=15, blank=True, verbose_name='rotate or flip', choices=[('FLIP_LEFT_RIGHT', 'Flip left to right'), ('FLIP_TOP_BOTTOM', 'Flip top to bottom'), ('ROTATE_90', 'Rotate 90 degrees counter-clockwise'), ('ROTATE_270', 'Rotate 90 degrees clockwise'), ('ROTATE_180', 'Rotate 180 degrees')])),
-                ('color', models.FloatField(help_text='A factor of 0.0 gives a black and white image, a factor of 1.0 gives the original image.', verbose_name='color', default=1.0)),
-                ('brightness', models.FloatField(help_text='A factor of 0.0 gives a black image, a factor of 1.0 gives the original image.', verbose_name='brightness', default=1.0)),
-                ('contrast', models.FloatField(help_text='A factor of 0.0 gives a solid grey image, a factor of 1.0 gives the original image.', verbose_name='contrast', default=1.0)),
-                ('sharpness', models.FloatField(help_text='A factor of 0.0 gives a blurred image, a factor of 1.0 gives the original image.', verbose_name='sharpness', default=1.0)),
-                ('filters', models.CharField(max_length=200, help_text='Chain multiple filters using the following pattern "FILTER_ONE->FILTER_TWO->FILTER_THREE". Image filters will be applied in order. The following filters are available: BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SHARPEN, SMOOTH, SMOOTH_MORE.', blank=True, verbose_name='filters')),
-                ('reflection_size', models.FloatField(help_text='The height of the reflection as a percentage of the orignal image. A factor of 0.0 adds no reflection, a factor of 1.0 adds a reflection equal to the height of the orignal image.', verbose_name='size', default=0)),
-                ('reflection_strength', models.FloatField(help_text='The initial opacity of the reflection gradient.', verbose_name='strength', default=0.6)),
-                ('background_color', models.CharField(max_length=7, help_text='The background color of the reflection gradient. Set this to match the background color of your page.', verbose_name='color', default='#FFFFFF')),
-            ],
-            options={
-                'verbose_name': 'photo effect',
-                'verbose_name_plural': 'photo effects',
-            },
-            bases=(models.Model,),
-        ),
-        migrations.AddField(
-            model_name='photo',
-            name='effect',
-            field=models.ForeignKey(blank=True, verbose_name='effect', null=True, to='photologue.PhotoEffect', on_delete=models.CASCADE),
-            preserve_default=True,
-        ),
-        migrations.CreateModel(
-            name='PhotoSize',
-            fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
-                ('name', models.CharField(max_length=40, help_text='Photo size name should contain only letters, numbers and underscores. Examples: "thumbnail", "display", "small", "main_page_widget".', verbose_name='name', unique=True, validators=[django.core.validators.RegexValidator(regex='^[a-z0-9_]+$', message='Use only plain lowercase letters (ASCII), numbers and underscores.')])),
-                ('width', models.PositiveIntegerField(help_text='If width is set to "0" the image will be scaled to the supplied height.', verbose_name='width', default=0)),
-                ('height', models.PositiveIntegerField(help_text='If height is set to "0" the image will be scaled to the supplied width', verbose_name='height', default=0)),
-                ('quality', models.PositiveIntegerField(help_text='JPEG image quality.', verbose_name='quality', choices=[(30, 'Very Low'), (40, 'Low'), (50, 'Medium-Low'), (60, 'Medium'), (70, 'Medium-High'), (80, 'High'), (90, 'Very High')], default=70)),
-                ('upscale', models.BooleanField(help_text='If selected the image will be scaled up if necessary to fit the supplied dimensions. Cropped sizes will be upscaled regardless of this setting.', verbose_name='upscale images?', default=False)),
-                ('crop', models.BooleanField(help_text='If selected the image will be scaled and cropped to fit the supplied dimensions.', verbose_name='crop to fit?', default=False)),
-                ('pre_cache', models.BooleanField(help_text='If selected this photo size will be pre-cached as photos are added.', verbose_name='pre-cache?', default=False)),
-                ('increment_count', models.BooleanField(help_text='If selected the image\'s "view_count" will be incremented when this photo size is displayed.', verbose_name='increment view count?', default=False)),
-                ('effect', models.ForeignKey(blank=True, verbose_name='photo effect', null=True, to='photologue.PhotoEffect', on_delete=models.CASCADE)),
-            ],
-            options={
-                'verbose_name': 'photo size',
-                'ordering': ['width', 'height'],
-                'verbose_name_plural': 'photo sizes',
-            },
-            bases=(models.Model,),
         ),
         migrations.CreateModel(
-            name='Watermark',
+            name='Gallery',
             fields=[
-                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
-                ('name', models.CharField(max_length=30, verbose_name='name', unique=True)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published')),
+                ('title', models.CharField(max_length=250, unique=True, verbose_name='title')),
+                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='title slug')),
+                ('date_start', models.DateField(default=django.utils.timezone.now, verbose_name='start date')),
+                ('date_end', models.DateField(blank=True, null=True, verbose_name='end date')),
                 ('description', models.TextField(blank=True, verbose_name='description')),
-                ('image', models.ImageField(upload_to='photologue/watermarks', verbose_name='image')),
-                ('style', models.CharField(max_length=5, default='scale', verbose_name='style', choices=[('tile', 'Tile'), ('scale', 'Scale')])),
-                ('opacity', models.FloatField(help_text='The opacity of the overlay.', verbose_name='opacity', default=1)),
+                ('is_public', models.BooleanField(default=True, help_text='Public galleries will be displayed in the default views.', verbose_name='is public')),
+                ('photos', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Photo', verbose_name='photos')),
+                ('tags', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Tag', verbose_name='tags')),
             ],
             options={
-                'verbose_name': 'watermark',
-                'verbose_name_plural': 'watermarks',
+                'verbose_name': 'gallery',
+                'verbose_name_plural': 'galleries',
+                'ordering': ['-date_added'],
+                'get_latest_by': 'date_added',
             },
-            bases=(models.Model,),
-        ),
-        migrations.AddField(
-            model_name='photosize',
-            name='watermark',
-            field=models.ForeignKey(blank=True, verbose_name='watermark image', null=True, to='photologue.Watermark', on_delete=models.CASCADE),
-            preserve_default=True,
         ),
     ]
diff --git a/photologue/migrations/0002_photosize_data.py b/photologue/migrations/0002_photosize_data.py
deleted file mode 100644
index 7bb9229..0000000
--- a/photologue/migrations/0002_photosize_data.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# encoding: utf8
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-def initial_photosizes(apps, schema_editor):
-
-    PhotoSize = apps.get_model('photologue', 'PhotoSize')
-
-    # If there are already Photosizes, then we are upgrading an existing
-    # installation, we don't want to auto-create some PhotoSizes.
-    if PhotoSize.objects.all().count() > 0:
-        return
-    PhotoSize.objects.create(name='admin_thumbnail',
-                             width=100,
-                             height=75,
-                             crop=True,
-                             pre_cache=True,
-                             increment_count=False)
-    PhotoSize.objects.create(name='thumbnail',
-                             width=100,
-                             height=75,
-                             crop=True,
-                             pre_cache=True,
-                             increment_count=False)
-    PhotoSize.objects.create(name='display',
-                             width=400,
-                             crop=False,
-                             pre_cache=True,
-                             increment_count=True)
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0001_initial'),
-        ('contenttypes', '0002_remove_content_type_name'),
-    ]
-
-    operations = [
-        migrations.RunPython(initial_photosizes),
-    ]
diff --git a/photologue/migrations/0003_auto_20140822_1716.py b/photologue/migrations/0003_auto_20140822_1716.py
deleted file mode 100644
index 16d1942..0000000
--- a/photologue/migrations/0003_auto_20140822_1716.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0002_photosize_data'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='galleryupload',
-            name='title',
-            field=models.CharField(null=True, help_text='All uploaded photos will be given a title made up of this title + a sequential number.', max_length=50, verbose_name='title', blank=True),
-        ),
-    ]
diff --git a/photologue/migrations/0004_auto_20140915_1259.py b/photologue/migrations/0004_auto_20140915_1259.py
deleted file mode 100644
index 0202044..0000000
--- a/photologue/migrations/0004_auto_20140915_1259.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import sortedm2m.fields
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0003_auto_20140822_1716'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='gallery',
-            name='photos',
-            field=sortedm2m.fields.SortedManyToManyField(to='photologue.Photo', related_name='galleries', null=True, verbose_name='photos', blank=True, help_text=None),
-        ),
-        migrations.AlterField(
-            model_name='photo',
-            name='effect',
-            field=models.ForeignKey(to='photologue.PhotoEffect', blank=True, related_name='photo_related', verbose_name='effect', null=True, on_delete=models.CASCADE),
-        ),
-        migrations.AlterField(
-            model_name='photosize',
-            name='effect',
-            field=models.ForeignKey(to='photologue.PhotoEffect', blank=True, related_name='photo_sizes', verbose_name='photo effect', null=True, on_delete=models.CASCADE),
-        ),
-        migrations.AlterField(
-            model_name='photosize',
-            name='watermark',
-            field=models.ForeignKey(to='photologue.Watermark', blank=True, related_name='photo_sizes', verbose_name='watermark image', null=True, on_delete=models.CASCADE),
-        ),
-    ]
diff --git a/photologue/migrations/0005_auto_20141027_1552.py b/photologue/migrations/0005_auto_20141027_1552.py
deleted file mode 100644
index 9f3d862..0000000
--- a/photologue/migrations/0005_auto_20141027_1552.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0004_auto_20140915_1259'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='photo',
-            name='title',
-            field=models.CharField(unique=True, max_length=60, verbose_name='title'),
-            preserve_default=True,
-        ),
-    ]
diff --git a/photologue/migrations/0006_auto_20141028_2005.py b/photologue/migrations/0006_auto_20141028_2005.py
deleted file mode 100644
index 583c3b8..0000000
--- a/photologue/migrations/0006_auto_20141028_2005.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0005_auto_20141027_1552'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='galleryupload',
-            name='gallery',
-        ),
-        migrations.DeleteModel(
-            name='GalleryUpload',
-        ),
-    ]
diff --git a/photologue/migrations/0007_auto_20150404_1737.py b/photologue/migrations/0007_auto_20150404_1737.py
deleted file mode 100644
index b41490c..0000000
--- a/photologue/migrations/0007_auto_20150404_1737.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import sortedm2m.fields
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0006_auto_20141028_2005'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='gallery',
-            name='photos',
-            field=sortedm2m.fields.SortedManyToManyField(help_text=None, related_name='galleries', verbose_name='photos', to='photologue.Photo', blank=True),
-        ),
-        migrations.AlterField(
-            model_name='gallery',
-            name='sites',
-            field=models.ManyToManyField(to='sites.Site', verbose_name='sites', blank=True),
-        ),
-        migrations.AlterField(
-            model_name='photo',
-            name='sites',
-            field=models.ManyToManyField(to='sites.Site', verbose_name='sites', blank=True),
-        ),
-    ]
diff --git a/photologue/migrations/0008_auto_20150509_1557.py b/photologue/migrations/0008_auto_20150509_1557.py
deleted file mode 100644
index 0baafd2..0000000
--- a/photologue/migrations/0008_auto_20150509_1557.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0007_auto_20150404_1737'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='gallery',
-            name='tags',
-        ),
-        migrations.RemoveField(
-            model_name='photo',
-            name='tags',
-        ),
-    ]
diff --git a/photologue/migrations/0009_auto_20160102_0904.py b/photologue/migrations/0009_auto_20160102_0904.py
deleted file mode 100644
index 4c64f11..0000000
--- a/photologue/migrations/0009_auto_20160102_0904.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.9 on 2016-01-02 09:04
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0008_auto_20150509_1557'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='photo',
-            name='date_taken',
-            field=models.DateTimeField(blank=True, help_text='Date image was taken; is obtained from the image EXIF data.', null=True, verbose_name='date taken'),
-        ),
-    ]
diff --git a/photologue/migrations/0010_auto_20160105_1307.py b/photologue/migrations/0010_auto_20160105_1307.py
deleted file mode 100644
index d466cae..0000000
--- a/photologue/migrations/0010_auto_20160105_1307.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.9 on 2016-01-05 13:07
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0009_auto_20160102_0904'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='gallery',
-            name='slug',
-            field=models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='title slug'),
-        ),
-        migrations.AlterField(
-            model_name='gallery',
-            name='title',
-            field=models.CharField(max_length=250, unique=True, verbose_name='title'),
-        ),
-        migrations.AlterField(
-            model_name='photo',
-            name='slug',
-            field=models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug'),
-        ),
-        migrations.AlterField(
-            model_name='photo',
-            name='title',
-            field=models.CharField(max_length=250, unique=True, verbose_name='title'),
-        ),
-    ]
diff --git a/photologue/migrations/0011_auto_20190223_2138.py b/photologue/migrations/0011_auto_20190223_2138.py
deleted file mode 100644
index 7bee4eb..0000000
--- a/photologue/migrations/0011_auto_20190223_2138.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.1.7 on 2019-02-23 21:38
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0010_auto_20160105_1307'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='photoeffect',
-            name='filters',
-            field=models.CharField(blank=True, help_text='Chain multiple filters using the following pattern "FILTER_ONE->FILTER_TWO->FILTER_THREE". Image filters will be applied in order. The following filters are available: BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, Kernel, SHARPEN, SMOOTH, SMOOTH_MORE.', max_length=200, verbose_name='filters'),
-        ),
-    ]
diff --git a/photologue/migrations/0012_auto_20220129_2207.py b/photologue/migrations/0012_auto_20220129_2207.py
deleted file mode 100644
index 7ed2b9a..0000000
--- a/photologue/migrations/0012_auto_20220129_2207.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Generated by Django 3.2.11 on 2022-01-29 22:07
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0011_auto_20190223_2138'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='gallery',
-            name='sites',
-        ),
-        migrations.RemoveField(
-            model_name='photo',
-            name='effect',
-        ),
-        migrations.RemoveField(
-            model_name='photo',
-            name='sites',
-        ),
-        migrations.RemoveField(
-            model_name='photosize',
-            name='effect',
-        ),
-        migrations.RemoveField(
-            model_name='photosize',
-            name='watermark',
-        ),
-        migrations.DeleteModel(
-            name='PhotoEffect',
-        ),
-        migrations.DeleteModel(
-            name='Watermark',
-        ),
-    ]
diff --git a/photologue/migrations/0013_alter_gallery_photos.py b/photologue/migrations/0013_alter_gallery_photos.py
deleted file mode 100644
index ab5ecbc..0000000
--- a/photologue/migrations/0013_alter_gallery_photos.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.2.11 on 2022-01-30 07:09
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue', '0012_auto_20220129_2207'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='gallery',
-            name='photos',
-            field=models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Photo', verbose_name='photos'),
-        ),
-    ]
diff --git a/photologue/migrations/0014_merge_related.py b/photologue/migrations/0014_merge_related.py
deleted file mode 100644
index 8dfa458..0000000
--- a/photologue/migrations/0014_merge_related.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# Generated by Django 3.2.11 on 2022-01-30 08:32
-
-import django.db.models.deletion
-import django.utils.timezone
-import taggit.managers
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.db import migrations, models
-from django.template.defaultfilters import slugify
-
-numens = User.objects.get(username="Numens").id
-
-def migrate_related(apps, schema_editor):
-    Gallery = apps.get_model('photologue', 'Gallery')
-    Tag = apps.get_model('photologue', 'Tag')
-    TaggedItems = apps.get_model('taggit', 'TaggedItem')
-    ct_ext = ContentType.objects.get(app_label="photologue_custom", model="galleryextended")
-    for gallery in Gallery.objects.all():
-        tagged_items = TaggedItems.objects.filter(
-            content_type_id=ct_ext.id,
-            object_id=gallery.extended.id
-        )
-        tags = [tag.tag for tag in tagged_items.all()]
-        gallery.date_start = gallery.extended.date_start
-        gallery.date_end = gallery.extended.date_end
-        gallery.save()
-        for tag in tags:
-            try:
-                new_tag, created = Tag.objects.get_or_create(
-                    name=tag.name.capitalize(),
-                    slug=slugify(tag.name),
-                )
-                if new_tag not in gallery.tags.all():
-                    gallery.tags.add(new_tag)
-                new_tag.save()
-                gallery.save()
-            except Exception:
-                continue
-
-    Photo = apps.get_model('photologue', 'Photo')
-    for photo in Photo.objects.all():
-        photo.owner = photo.extended.owner
-        photo.license = photo.extended.license
-        photo.save()
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('photologue', '0013_alter_gallery_photos'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='Tag',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=250, unique=True, verbose_name='name')),
-                ('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
-            ],
-            options={
-                'verbose_name': 'tag',
-                'verbose_name_plural': 'tags',
-                'ordering': ['name'],
-            },
-        ),
-        migrations.AddField(
-            model_name='gallery',
-            name='date_end',
-            field=models.DateField(blank=True, null=True, verbose_name='end date'),
-        ),
-        migrations.AddField(
-            model_name='gallery',
-            name='date_start',
-            field=models.DateField(default=django.utils.timezone.now, verbose_name='start date'),
-        ),
-        migrations.AddField(
-            model_name='photo',
-            name='license',
-            field=models.CharField(blank=True, max_length=255, verbose_name='license'),
-        ),
-        migrations.AddField(
-            model_name='photo',
-            name='owner',
-            field=models.ForeignKey(default=numens, on_delete=django.db.models.deletion.CASCADE, to='auth.user', verbose_name='owner'),
-            preserve_default=False,
-        ),
-        migrations.AddField(
-            model_name='gallery',
-            name='tags',
-            field=models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Tag', verbose_name='tags'),
-        ),
-        migrations.RunPython(migrate_related),
-    ]
diff --git a/photologue_custom/__init__.py b/photologue_custom/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/photologue_custom/apps.py b/photologue_custom/apps.py
deleted file mode 100644
index 55acba8..0000000
--- a/photologue_custom/apps.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.apps import AppConfig
-
-
-class PhotologueCustomConfig(AppConfig):
-    default_auto_field = 'django.db.models.AutoField'
-    name = 'photologue_custom'
diff --git a/photologue_custom/migrations/0001_initial.py b/photologue_custom/migrations/0001_initial.py
deleted file mode 100644
index 9fb5a1f..0000000
--- a/photologue_custom/migrations/0001_initial.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 2.2.24 on 2021-10-11 19:12
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import taggit.managers
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('photologue', '0011_auto_20190223_2138'),
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('taggit', '0002_auto_20150616_2121'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='PhotoExtended',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='owner')),
-                ('photo', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extented', to='photologue.Photo')),
-            ],
-            options={
-                'verbose_name': 'Extra fields',
-                'verbose_name_plural': 'Extra fields',
-            },
-        ),
-        migrations.CreateModel(
-            name='GalleryExtended',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('gallery', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extended', to='photologue.Gallery')),
-                ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
-            ],
-            options={
-                'verbose_name': 'Extra fields',
-                'verbose_name_plural': 'Extra fields',
-            },
-        ),
-    ]
diff --git a/photologue_custom/migrations/0002_auto_20211011_1956.py b/photologue_custom/migrations/0002_auto_20211011_1956.py
deleted file mode 100644
index d03ea2c..0000000
--- a/photologue_custom/migrations/0002_auto_20211011_1956.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2.24 on 2021-10-11 19:56
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue_custom', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='galleryextended',
-            name='date_end',
-            field=models.DateField(blank=True, null=True, verbose_name='end date'),
-        ),
-        migrations.AddField(
-            model_name='galleryextended',
-            name='date_start',
-            field=models.DateField(blank=True, null=True, verbose_name='start date'),
-        ),
-    ]
diff --git a/photologue_custom/migrations/0003_auto_20211013_1507.py b/photologue_custom/migrations/0003_auto_20211013_1507.py
deleted file mode 100644
index a99c067..0000000
--- a/photologue_custom/migrations/0003_auto_20211013_1507.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 2.2.24 on 2021-10-13 15:07
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue_custom', '0002_auto_20211011_1956'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='galleryextended',
-            name='gallery',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extended', to='photologue.Gallery'),
-        ),
-        migrations.AlterField(
-            model_name='photoextended',
-            name='photo',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extended', to='photologue.Photo'),
-        ),
-    ]
\ No newline at end of file
diff --git a/photologue_custom/migrations/0004_photoextended_license.py b/photologue_custom/migrations/0004_photoextended_license.py
deleted file mode 100644
index 87c6887..0000000
--- a/photologue_custom/migrations/0004_photoextended_license.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.24 on 2021-10-22 16:04
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue_custom', '0003_auto_20211013_1507'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='photoextended',
-            name='license',
-            field=models.CharField(blank=True, max_length=255, verbose_name='license'),
-        ),
-    ]
diff --git a/photologue_custom/migrations/0005_auto_20220130_0953.py b/photologue_custom/migrations/0005_auto_20220130_0953.py
deleted file mode 100644
index ef769ec..0000000
--- a/photologue_custom/migrations/0005_auto_20220130_0953.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 3.2.11 on 2022-01-30 09:53
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('photologue_custom', '0004_photoextended_license'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='photoextended',
-            name='owner',
-        ),
-        migrations.RemoveField(
-            model_name='photoextended',
-            name='photo',
-        ),
-        migrations.DeleteModel(
-            name='GalleryExtended',
-        ),
-        migrations.DeleteModel(
-            name='PhotoExtended',
-        ),
-    ]
diff --git a/photologue_custom/migrations/__init__.py b/photologue_custom/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
-- 
GitLab


From 2ef6dd2a44826e1d1c845fdcd4afdc7e02ea10fd Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 11:21:03 +0100
Subject: [PATCH 8/9] Remove date_added field in Gallery

---
 photologue/admin.py                           |  6 +++---
 .../migrations/0002_auto_20220130_1020.py     | 21 +++++++++++++++++++
 photologue/models.py                          |  6 ++----
 3 files changed, 26 insertions(+), 7 deletions(-)
 create mode 100644 photologue/migrations/0002_auto_20220130_1020.py

diff --git a/photologue/admin.py b/photologue/admin.py
index f398fc7..774ed8e 100644
--- a/photologue/admin.py
+++ b/photologue/admin.py
@@ -5,9 +5,9 @@ from .models import Gallery, Photo, Tag
 
 
 class GalleryAdmin(admin.ModelAdmin):
-    list_display = ('title', 'date_added', 'photo_count', 'is_public')
-    list_filter = ['date_added', 'is_public']
-    date_hierarchy = 'date_added'
+    list_display = ('title', 'date_start', 'photo_count', 'is_public')
+    list_filter = ['date_start', 'is_public']
+    date_hierarchy = 'date_start'
     prepopulated_fields = {'slug': ('title',)}
     model = Gallery
     autocomplete_fields = ['photos', 'tags']
diff --git a/photologue/migrations/0002_auto_20220130_1020.py b/photologue/migrations/0002_auto_20220130_1020.py
new file mode 100644
index 0000000..21338b0
--- /dev/null
+++ b/photologue/migrations/0002_auto_20220130_1020.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.11 on 2022-01-30 10:20
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('photologue', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='gallery',
+            options={'get_latest_by': 'date_start', 'ordering': ['-date_start'], 'verbose_name': 'gallery', 'verbose_name_plural': 'galleries'},
+        ),
+        migrations.RemoveField(
+            model_name='gallery',
+            name='date_added',
+        ),
+    ]
diff --git a/photologue/models.py b/photologue/models.py
index 8b94e5c..b7e975c 100644
--- a/photologue/models.py
+++ b/photologue/models.py
@@ -136,8 +136,6 @@ class TagField(models.CharField):
 
 
 class Gallery(models.Model):
-    date_added = models.DateTimeField(_('date published'),
-                                      default=now)
     title = models.CharField(_('title'),
                              max_length=250,
                              unique=True)
@@ -172,8 +170,8 @@ class Gallery(models.Model):
                                     blank=True)
 
     class Meta:
-        ordering = ['-date_added']
-        get_latest_by = 'date_added'
+        ordering = ['-date_start']
+        get_latest_by = 'date_start'
         verbose_name = _('gallery')
         verbose_name_plural = _('galleries')
 
-- 
GitLab


From a4faa7e04e54b82519836a17552d21c3553ff4eb Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 30 Jan 2022 11:23:34 +0100
Subject: [PATCH 9/9] Fix Django Admin docs

---
 photo21/urls.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/photo21/urls.py b/photo21/urls.py
index 0538e1c..66d4912 100644
--- a/photo21/urls.py
+++ b/photo21/urls.py
@@ -25,8 +25,8 @@ urlpatterns = [
     path('', include('photologue.urls', namespace='photologue')),
     path('accounts/', include('allauth.urls')),
     path('i18n/', include('django.conf.urls.i18n')),
-    path('admin/', admin.site.urls),
     path('admin/doc/', include('django.contrib.admindocs.urls')),
+    path('admin/', admin.site.urls),
 ]
 
 # In production media are served through NGINX with X-Accel-Redirect
-- 
GitLab