Merge branch 'simplify_models' into 'master'

Simplify models

See merge request bde/photo21!22
This commit is contained in:
erdnaxe 2022-01-30 11:26:08 +01:00
commit d7a39a0334
64 changed files with 732 additions and 1268 deletions

View file

@ -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 \ $ sudo apt install nginx git gettext uwsgi uwsgi-plugin-python3 python3-venv \
python3-certbot-nginx python3-django python3-django-crispy-forms \ python3-certbot-nginx python3-django python3-django-crispy-forms \
python3-django-taggit python3-pil python3-exifread python3-django-allauth \ python3-pil python3-exifread python3-django-allauth python3-docutils
python3-psycopg2 python3-docutils
``` ```
2. **Clonage du dépot dans `/var/www/photos/photo21`** 2. **Clonage du dépot dans `/var/www/photos/photo21`**

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -40,19 +40,19 @@ msgstr ""
msgid "hash" msgid "hash"
msgstr "" msgstr ""
#: photo21/settings.py:162 #: photo21/settings.py:161
msgid "German" msgid "German"
msgstr "" msgstr ""
#: photo21/settings.py:163 #: photo21/settings.py:162
msgid "English" msgid "English"
msgstr "" msgstr ""
#: photo21/settings.py:164 #: photo21/settings.py:163
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: photo21/settings.py:165 #: photo21/settings.py:164
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -199,6 +199,16 @@ msgstr ""
msgid "If any problem, please contact the server owners at" msgid "If any problem, please contact the server owners at"
msgstr "" 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 #: photo21/templates/account/signup.html:6
msgid "Signup" msgid "Signup"
msgstr "" msgstr ""

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -39,19 +39,19 @@ msgstr ""
msgid "hash" msgid "hash"
msgstr "" msgstr ""
#: photo21/settings.py:162 #: photo21/settings.py:161
msgid "German" msgid "German"
msgstr "" msgstr ""
#: photo21/settings.py:163 #: photo21/settings.py:162
msgid "English" msgid "English"
msgstr "" msgstr ""
#: photo21/settings.py:164 #: photo21/settings.py:163
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: photo21/settings.py:165 #: photo21/settings.py:164
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -198,6 +198,16 @@ msgstr ""
msgid "If any problem, please contact the server owners at" msgid "If any problem, please contact the server owners at"
msgstr "" 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 #: photo21/templates/account/signup.html:6
msgid "Signup" msgid "Signup"
msgstr "" msgstr ""

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -42,19 +42,19 @@ msgstr ""
msgid "hash" msgid "hash"
msgstr "" msgstr ""
#: photo21/settings.py:162 #: photo21/settings.py:160
msgid "German" msgid "German"
msgstr "" msgstr ""
#: photo21/settings.py:163 #: photo21/settings.py:161
msgid "English" msgid "English"
msgstr "" msgstr ""
#: photo21/settings.py:164 #: photo21/settings.py:162
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: photo21/settings.py:165 #: photo21/settings.py:163
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -206,6 +206,18 @@ msgstr ""
msgid "If any problem, please contact the server owners at" msgid "If any problem, please contact the server owners at"
msgstr "En cas de problème, contactez les administrateurs à" 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 #: photo21/templates/account/signup.html:6
msgid "Signup" msgid "Signup"
msgstr "" msgstr ""
@ -333,48 +345,3 @@ msgstr ""
#: photo21/templates/socialaccount/connections.html:54 #: photo21/templates/socialaccount/connections.html:54
msgid "Add a 3rd Party Account" msgid "Add a 3rd Party Account"
msgstr "" 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"

View file

@ -62,9 +62,7 @@ INSTALLED_APPS = [
'allauth.socialaccount', 'allauth.socialaccount',
'allauth_note_kfet', 'allauth_note_kfet',
'crispy_forms', 'crispy_forms',
'photologue_custom',
'photologue', 'photologue',
'taggit',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -156,8 +154,6 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Limit available languages to this subset # Limit available languages to this subset
LANGUAGES = [ LANGUAGES = [
('de', _('German')), ('de', _('German')),

View file

@ -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 %}

View file

@ -22,11 +22,11 @@ from .views import IndexView, MediaAccess
urlpatterns = [ urlpatterns = [
path('', IndexView.as_view(), name='index'), 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('accounts/', include('allauth.urls')),
path('i18n/', include('django.conf.urls.i18n')), path('i18n/', include('django.conf.urls.i18n')),
path('admin/', admin.site.urls),
path('admin/doc/', include('django.contrib.admindocs.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 # In production media are served through NGINX with X-Accel-Redirect

View file

@ -1,24 +1,41 @@
from django.contrib import admin from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Gallery, Photo from .models import Gallery, Photo, Tag
class GalleryAdmin(admin.ModelAdmin): class GalleryAdmin(admin.ModelAdmin):
list_display = ('title', 'date_added', 'photo_count', 'is_public') list_display = ('title', 'date_start', 'photo_count', 'is_public')
list_filter = ['date_added', 'is_public'] list_filter = ['date_start', 'is_public']
date_hierarchy = 'date_added' date_hierarchy = 'date_start'
prepopulated_fields = {'slug': ('title',)} prepopulated_fields = {'slug': ('title',)}
model = Gallery model = Gallery
autocomplete_fields = ['photos', ] autocomplete_fields = ['photos', 'tags']
search_fields = ['title', ] search_fields = ['title', ]
class PhotoAdmin(admin.ModelAdmin): class PhotoAdmin(admin.ModelAdmin):
list_display = ('title', 'date_taken', 'date_added', list_display = ('title', 'date_taken', 'date_added',
'is_public', 'view_count', 'admin_thumbnail') 'is_public', 'view_count', 'admin_thumbnail', 'get_owner')
list_filter = ['date_added', 'is_public'] list_filter = ['date_added', 'is_public', 'owner']
search_fields = ['title', 'slug', 'caption'] search_fields = ['title', 'slug', 'caption']
list_per_page = 10 list_per_page = 10
prepopulated_fields = {'slug': ('title',)} prepopulated_fields = {'slug': ('title',)}
readonly_fields = ('date_taken',) readonly_fields = ('date_taken',)
model = Photo 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',)
search_fields = ('name',)
model = Tag
admin.site.register(Gallery, GalleryAdmin)
admin.site.register(Photo, PhotoAdmin)
admin.site.register(Tag, TagAdmin)

View file

@ -5,19 +5,8 @@ from crispy_forms.layout import Div, Layout, Submit
from django import forms from django import forms
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from photologue.models import Gallery
from taggit.models import Tag
from .models import GalleryExtended from .models import Gallery, Tag
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
class UploadForm(forms.Form): class UploadForm(forms.Form):
@ -29,7 +18,7 @@ class UploadForm(forms.Form):
'class': 'mb-3', 'class': 'mb-3',
}), }),
) )
gallery = GalleryChoiceField( gallery = forms.ModelChoiceField(
Gallery.objects.all(), Gallery.objects.all(),
label=_('Gallery'), label=_('Gallery'),
required=False, required=False,
@ -100,12 +89,12 @@ class UploadForm(forms.Form):
if not gallery: if not gallery:
# Create new gallery # Create new gallery
title = self.cleaned_data.get('new_gallery_title') title = self.cleaned_data.get('new_gallery_title')
gallery = Gallery.objects.create(title=title, slug=slugify(title)) gallery = Gallery.objects.create(
ext = GalleryExtended.objects.create( title=title,
gallery=gallery, slug=slugify(title),
date_start=self.cleaned_data['new_gallery_date_start'], date_start=self.cleaned_data['new_gallery_date_start'],
date_end=self.cleaned_data['new_gallery_date_end'], date_end=self.cleaned_data['new_gallery_date_end'],
) )
for tag in self.cleaned_data['new_gallery_tags']: for tag in self.cleaned_data['new_gallery_tags']:
ext.tags.add(tag) gallery.tags.add(tag)
return gallery return gallery

View file

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Photologue\n" "Project-Id-Version: Photologue\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2017-12-03 14:47+0000\n"
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n" "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
"Language-Team: German (http://www.transifex.com/richardbarran/django-" "Language-Team: German (http://www.transifex.com/richardbarran/django-"
@ -22,75 +22,79 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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" msgid "Very Low"
msgstr "Sehr niedrig" msgstr "Sehr niedrig"
#: photologue/models.py:87 #: photologue/models.py:85
msgid "Low" msgid "Low"
msgstr "Niedrig" msgstr "Niedrig"
#: photologue/models.py:88 #: photologue/models.py:86
msgid "Medium-Low" msgid "Medium-Low"
msgstr "Mittel-niedrig" msgstr "Mittel-niedrig"
#: photologue/models.py:89 #: photologue/models.py:87
msgid "Medium" msgid "Medium"
msgstr "Mittel" msgstr "Mittel"
#: photologue/models.py:90 #: photologue/models.py:88
msgid "Medium-High" msgid "Medium-High"
msgstr "Mittel-hoch" msgstr "Mittel-hoch"
#: photologue/models.py:91 #: photologue/models.py:89
msgid "High" msgid "High"
msgstr "Hoch" msgstr "Hoch"
#: photologue/models.py:92 #: photologue/models.py:90
msgid "Very High" msgid "Very High"
msgstr "Sehr hoch" msgstr "Sehr hoch"
#: photologue/models.py:97 #: photologue/models.py:95
msgid "Top" msgid "Top"
msgstr "Oben" msgstr "Oben"
#: photologue/models.py:98 #: photologue/models.py:96
msgid "Right" msgid "Right"
msgstr "Rechts" msgstr "Rechts"
#: photologue/models.py:99 #: photologue/models.py:97
msgid "Bottom" msgid "Bottom"
msgstr "Unten" msgstr "Unten"
#: photologue/models.py:100 #: photologue/models.py:98
msgid "Left" msgid "Left"
msgstr "Links" msgstr "Links"
#: photologue/models.py:101 #: photologue/models.py:99
msgid "Center (Default)" msgid "Center (Default)"
msgstr "Mitte (Standard)" msgstr "Mitte (Standard)"
#: photologue/models.py:105 #: photologue/models.py:103
msgid "Flip left to right" msgid "Flip left to right"
msgstr "Horizontal spiegeln" msgstr "Horizontal spiegeln"
#: photologue/models.py:106 #: photologue/models.py:104
msgid "Flip top to bottom" msgid "Flip top to bottom"
msgstr "Vertikal spiegeln" msgstr "Vertikal spiegeln"
#: photologue/models.py:107 #: photologue/models.py:105
msgid "Rotate 90 degrees counter-clockwise" msgid "Rotate 90 degrees counter-clockwise"
msgstr "Um 90° nach links drehen" msgstr "Um 90° nach links drehen"
#: photologue/models.py:108 #: photologue/models.py:106
msgid "Rotate 90 degrees clockwise" msgid "Rotate 90 degrees clockwise"
msgstr "Um 90° nach rechts drehen" msgstr "Um 90° nach rechts drehen"
#: photologue/models.py:109 #: photologue/models.py:107
msgid "Rotate 180 degrees" msgid "Rotate 180 degrees"
msgstr "Um 180° drehen" msgstr "Um 180° drehen"
#: photologue/models.py:119 #: photologue/models.py:117
#, python-format #, python-format
msgid "" msgid ""
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-" "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 " "\". Bildfilter werden nach der Reihe angewendet. Folgende Filter sind "
"verfügbar: %s." "verfügbar: %s."
#: photologue/models.py:141 #: photologue/models.py:139
msgid "date published" msgid "date published"
msgstr "Veröffentlichungsdatum" msgstr "Veröffentlichungsdatum"
#: photologue/models.py:143 photologue/models.py:474 #: photologue/models.py:141 photologue/models.py:485
msgid "title" msgid "title"
msgstr "Titel" msgstr "Titel"
#: photologue/models.py:146 #: photologue/models.py:144
msgid "title slug" msgid "title slug"
msgstr "Kurztitel" 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." msgid "A \"slug\" is a unique URL-friendly title for an object."
msgstr "" msgstr ""
"Ein Kurztitel (\"slug\") ist ein eindeutiger, URL-geeigneter Titel für ein " "Ein Kurztitel (\"slug\") ist ein eindeutiger, URL-geeigneter Titel für ein "
"Objekt." "Objekt."
#: photologue/models.py:150 #: photologue/models.py:150
msgid "start date"
msgstr ""
#: photologue/models.py:155
msgid "end date"
msgstr ""
#: photologue/models.py:157
msgid "description" msgid "description"
msgstr "Beschreibung" 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" msgid "is public"
msgstr "ist öffentlich" msgstr "ist öffentlich"
#: photologue/models.py:154 #: photologue/models.py:167
msgid "Public galleries will be displayed in the default views." msgid "Public galleries will be displayed in the default views."
msgstr "Öffentliche Galerien werden in den Standard-Views angezeigt." 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" msgid "photos"
msgstr "Fotos" msgstr "Fotos"
#: photologue/models.py:166 #: photologue/models.py:177
msgid "gallery" msgid "gallery"
msgstr "Galerie" msgstr "Galerie"
#: photologue/models.py:167 #: photologue/models.py:178
msgid "galleries" msgid "galleries"
msgstr "Galerien" msgstr "Galerien"
#: photologue/models.py:202 #: photologue/models.py:213
msgid "count" msgid "count"
msgstr "Anzahl" msgstr "Anzahl"
#: photologue/models.py:210 #: photologue/models.py:221
msgid "image" msgid "image"
msgstr "Bild" msgstr "Bild"
#: photologue/models.py:213 #: photologue/models.py:224
msgid "date taken" msgid "date taken"
msgstr "Aufnahmedatum" msgstr "Aufnahmedatum"
#: photologue/models.py:216 #: photologue/models.py:227
msgid "Date image was taken; is obtained from the image EXIF data." msgid "Date image was taken; is obtained from the image EXIF data."
msgstr "" msgstr ""
"Datum, an dem das Foto geschossen wurde; ausgelesen aus den EXIF-Daten." "Datum, an dem das Foto geschossen wurde; ausgelesen aus den EXIF-Daten."
#: photologue/models.py:217 #: photologue/models.py:228
msgid "view count" msgid "view count"
msgstr "Anzahl an Aufrufen" msgstr "Anzahl an Aufrufen"
#: photologue/models.py:220 #: photologue/models.py:231
msgid "crop from" msgid "crop from"
msgstr "Beschneiden von" msgstr "Beschneiden von"
#: photologue/models.py:243 #: photologue/models.py:254
msgid "An \"admin_thumbnail\" photo size has not been defined." msgid "An \"admin_thumbnail\" photo size has not been defined."
msgstr "Es ist keine Fotogröße \"admin_thumbnail\" definiert." msgstr "Es ist keine Fotogröße \"admin_thumbnail\" definiert."
#: photologue/models.py:250 #: photologue/models.py:261
msgid "Thumbnail" msgid "Thumbnail"
msgstr "Vorschaubild" msgstr "Vorschaubild"
#: photologue/models.py:477 #: photologue/models.py:488 photologue/models.py:696
msgid "slug" msgid "slug"
msgstr "Kurztitel" msgstr "Kurztitel"
#: photologue/models.py:481 #: photologue/models.py:492
msgid "caption" msgid "caption"
msgstr "Bildunterschrift" msgstr "Bildunterschrift"
#: photologue/models.py:483 #: photologue/models.py:494
msgid "date added" msgid "date added"
msgstr "Datum des Eintrags" 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." msgid "Public photographs will be displayed in the default views."
msgstr "Öffentliche Fotos werden in den Standard-Views angezeigt." msgstr "Öffentliche Fotos werden in den Standard-Views angezeigt."
#: photologue/models.py:494 #: photologue/models.py:513
msgid "photo" msgid "photo"
msgstr "Foto" msgstr "Foto"
#: photologue/models.py:556 #: photologue/models.py:575 photologue/models.py:691
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: photologue/models.py:560 #: photologue/models.py:579
msgid "" msgid ""
"Photo size name should contain only letters, numbers and underscores. " "Photo size name should contain only letters, numbers and underscores. "
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"." "Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
@ -209,41 +229,41 @@ msgstr ""
"enthalten. Beispiele: \"thumbnail\", \"display\", \"small\", " "enthalten. Beispiele: \"thumbnail\", \"display\", \"small\", "
"\"main_page_widget\"." "\"main_page_widget\"."
#: photologue/models.py:567 #: photologue/models.py:586
msgid "width" msgid "width"
msgstr "Breite" msgstr "Breite"
#: photologue/models.py:570 #: photologue/models.py:589
msgid "" msgid ""
"If width is set to \"0\" the image will be scaled to the supplied height." "If width is set to \"0\" the image will be scaled to the supplied height."
msgstr "" msgstr ""
"Wenn die Breite auf \"0\" gesetzt ist, wird das Bild proportional auf die " "Wenn die Breite auf \"0\" gesetzt ist, wird das Bild proportional auf die "
"angebene Höhe skaliert." "angebene Höhe skaliert."
#: photologue/models.py:571 #: photologue/models.py:590
msgid "height" msgid "height"
msgstr "Höhe" msgstr "Höhe"
#: photologue/models.py:574 #: photologue/models.py:593
msgid "" msgid ""
"If height is set to \"0\" the image will be scaled to the supplied width" "If height is set to \"0\" the image will be scaled to the supplied width"
msgstr "" msgstr ""
"Wenn die Höhe auf \"0\" gesetzt ist, wird das Bild proportional auf die " "Wenn die Höhe auf \"0\" gesetzt ist, wird das Bild proportional auf die "
"angebene Breite skaliert." "angebene Breite skaliert."
#: photologue/models.py:575 #: photologue/models.py:594
msgid "quality" msgid "quality"
msgstr "Qualität" msgstr "Qualität"
#: photologue/models.py:578 #: photologue/models.py:597
msgid "JPEG image quality." msgid "JPEG image quality."
msgstr "JPEG-Bildqualität" msgstr "JPEG-Bildqualität"
#: photologue/models.py:579 #: photologue/models.py:598
msgid "upscale images?" msgid "upscale images?"
msgstr "Bilder hochskalieren?" msgstr "Bilder hochskalieren?"
#: photologue/models.py:581 #: photologue/models.py:600
msgid "" msgid ""
"If selected the image will be scaled up if necessary to fit the supplied " "If selected the image will be scaled up if necessary to fit the supplied "
"dimensions. Cropped sizes will be upscaled regardless of this setting." "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 " "Beschnittene Größen werden unabhängig von dieser Einstellung bei Bedarf "
"hochskaliert." "hochskaliert."
#: photologue/models.py:585 #: photologue/models.py:604
msgid "crop to fit?" msgid "crop to fit?"
msgstr "Zuschneiden?" msgstr "Zuschneiden?"
#: photologue/models.py:587 #: photologue/models.py:606
msgid "" msgid ""
"If selected the image will be scaled and cropped to fit the supplied " "If selected the image will be scaled and cropped to fit the supplied "
"dimensions." "dimensions."
msgstr "" msgstr ""
"Soll das Bild auf das angegebene Format skaliert und beschnitten werden?" "Soll das Bild auf das angegebene Format skaliert und beschnitten werden?"
#: photologue/models.py:589 #: photologue/models.py:608
msgid "pre-cache?" msgid "pre-cache?"
msgstr "Vorausspeichern?" msgstr "Vorausspeichern?"
#: photologue/models.py:591 #: photologue/models.py:610
msgid "If selected this photo size will be pre-cached as photos are added." msgid "If selected this photo size will be pre-cached as photos are added."
msgstr "" msgstr ""
"Soll diese Bildgröße im Voraus gespeichert (pre-cached) werden, wenn Fotos " "Soll diese Bildgröße im Voraus gespeichert (pre-cached) werden, wenn Fotos "
"hinzugefügt werden?" "hinzugefügt werden?"
#: photologue/models.py:592 #: photologue/models.py:611
msgid "increment view count?" msgid "increment view count?"
msgstr "Bildzähler?" msgstr "Bildzähler?"
#: photologue/models.py:594 #: photologue/models.py:613
msgid "" msgid ""
"If selected the image's \"view_count\" will be incremented when this photo " "If selected the image's \"view_count\" will be incremented when this photo "
"size is displayed." "size is displayed."
@ -285,32 +305,32 @@ msgstr ""
"Soll der Ansichts-Zähler (view-count) hochgezählt werden, wenn ein Foto " "Soll der Ansichts-Zähler (view-count) hochgezählt werden, wenn ein Foto "
"dieser Größe angezeigt wird?" "dieser Größe angezeigt wird?"
#: photologue/models.py:599 #: photologue/models.py:618
msgid "photo size" msgid "photo size"
msgstr "Foto-Größe" msgstr "Foto-Größe"
#: photologue/models.py:600 #: photologue/models.py:619
msgid "photo sizes" msgid "photo sizes"
msgstr "Foto-Größen" 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." msgid "Can only crop photos if both width and height dimensions are set."
msgstr "" msgstr ""
"Fotos können nur zugeschnitten werden, wenn Breite und Höhe angegeben sind." "Fotos können nur zugeschnitten werden, wenn Breite und Höhe angegeben sind."
#: photologue_custom/admin.py:43 photologue_custom/models.py:51 #: photologue/models.py:702
msgid "owner" msgid "tag"
msgstr "" msgstr ""
#: photologue_custom/forms.py:34 #: photologue_custom/forms.py:22
msgid "Gallery" msgid "Gallery"
msgstr "Galerie" msgstr "Galerie"
#: photologue_custom/forms.py:36 #: photologue_custom/forms.py:24
msgid "-- Create a new gallery --" msgid "-- Create a new gallery --"
msgstr "" msgstr ""
#: photologue_custom/forms.py:37 #: photologue_custom/forms.py:25
msgid "" msgid ""
"Select a gallery to add these images to. Leave this empty to create a new " "Select a gallery to add these images to. Leave this empty to create a new "
"gallery from the supplied title." "gallery from the supplied title."
@ -318,59 +338,43 @@ msgstr ""
"Wähle eine Galerie aus, zu der diese Bilder hinzugefügt werden sollen. Lasse " "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." "dieses Feld leer, um eine neue Galerie mit dem angegeben Titel zu erzeugen."
#: photologue_custom/forms.py:41 #: photologue_custom/forms.py:29
#, fuzzy
#| msgid "View all galleries"
msgid "New gallery title" 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" msgid "New gallery event start date"
msgstr "" msgstr ""
#: photologue_custom/forms.py:51 #: photologue_custom/forms.py:39
msgid "New gallery event end date" msgid "New gallery event end date"
msgstr "" msgstr ""
#: photologue_custom/forms.py:57 #: photologue_custom/forms.py:45
#, fuzzy
#| msgid "gallery"
msgid "New gallery tags" msgid "New gallery tags"
msgstr "Galerie" msgstr ""
#: photologue_custom/forms.py:59 #: photologue_custom/forms.py:47
msgid "" msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr "" msgstr ""
#: photologue_custom/forms.py:76 #: photologue_custom/forms.py:64
#: photologue_custom/templates/photologue/upload.html:6 #: photologue_custom/templates/photologue/upload.html:6
#: photologue_custom/templates/photologue/upload.html:73 #: photologue_custom/templates/photologue/upload.html:73
msgid "Upload" msgid "Upload"
msgstr "Hochladen" msgstr "Hochladen"
#: photologue_custom/forms.py:82 #: photologue_custom/forms.py:70
msgid "A gallery with that title already exists." msgid "A gallery with that title already exists."
msgstr "Es existiert bereits eine Gallerie mit diesem Titel." 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." msgid "Select an existing gallery, or enter a title for a new gallery."
msgstr "" msgstr ""
"Wähle eine existierende Galerie aus oder gib einen Titel für eine neue " "Wähle eine existierende Galerie aus oder gib einen Titel für eine neue "
"Galerie ein." "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:7
#: photologue_custom/templates/photologue/gallery_archive.html:12 #: photologue_custom/templates/photologue/gallery_archive.html:12
msgid "Latest photo galleries" msgid "Latest photo galleries"
@ -403,16 +407,12 @@ msgid "to"
msgstr "" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:57 #: photologue_custom/templates/photologue/gallery_detail.html:57
#, fuzzy
#| msgid "All photos"
msgid "All pictures" msgid "All pictures"
msgstr "Alle Fotos" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:78 #: photologue_custom/templates/photologue/gallery_detail.html:78
#, fuzzy
#| msgid "View all galleries"
msgid "Download all gallery" msgid "Download all gallery"
msgstr "Zeige alle Galerien." msgstr ""
#: photologue_custom/templates/photologue/photo_detail.html:13 #: photologue_custom/templates/photologue/photo_detail.html:13
msgid "Published" msgid "Published"

View file

@ -12,7 +12,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Photologue\n" "Project-Id-Version: Photologue\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2017-12-03 14:46+0000\n"
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n" "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
"Language-Team: Spanish (Spain) (http://www.transifex.com/richardbarran/" "Language-Team: Spanish (Spain) (http://www.transifex.com/richardbarran/"
@ -23,75 +23,79 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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" msgid "Very Low"
msgstr "Muy bajo" msgstr "Muy bajo"
#: photologue/models.py:87 #: photologue/models.py:85
msgid "Low" msgid "Low"
msgstr "Bajo" msgstr "Bajo"
#: photologue/models.py:88 #: photologue/models.py:86
msgid "Medium-Low" msgid "Medium-Low"
msgstr "Medio-bajo" msgstr "Medio-bajo"
#: photologue/models.py:89 #: photologue/models.py:87
msgid "Medium" msgid "Medium"
msgstr "Medio" msgstr "Medio"
#: photologue/models.py:90 #: photologue/models.py:88
msgid "Medium-High" msgid "Medium-High"
msgstr "Medio-alto" msgstr "Medio-alto"
#: photologue/models.py:91 #: photologue/models.py:89
msgid "High" msgid "High"
msgstr "Alto" msgstr "Alto"
#: photologue/models.py:92 #: photologue/models.py:90
msgid "Very High" msgid "Very High"
msgstr "Muy alto" msgstr "Muy alto"
#: photologue/models.py:97 #: photologue/models.py:95
msgid "Top" msgid "Top"
msgstr "Arriba" msgstr "Arriba"
#: photologue/models.py:98 #: photologue/models.py:96
msgid "Right" msgid "Right"
msgstr "Derecha" msgstr "Derecha"
#: photologue/models.py:99 #: photologue/models.py:97
msgid "Bottom" msgid "Bottom"
msgstr "Abajo" msgstr "Abajo"
#: photologue/models.py:100 #: photologue/models.py:98
msgid "Left" msgid "Left"
msgstr "Izquierda" msgstr "Izquierda"
#: photologue/models.py:101 #: photologue/models.py:99
msgid "Center (Default)" msgid "Center (Default)"
msgstr "Centro (por defecto)" msgstr "Centro (por defecto)"
#: photologue/models.py:105 #: photologue/models.py:103
msgid "Flip left to right" msgid "Flip left to right"
msgstr "Voltear de izquerda a derecha" msgstr "Voltear de izquerda a derecha"
#: photologue/models.py:106 #: photologue/models.py:104
msgid "Flip top to bottom" msgid "Flip top to bottom"
msgstr "Voltear de arriba a abajo" msgstr "Voltear de arriba a abajo"
#: photologue/models.py:107 #: photologue/models.py:105
msgid "Rotate 90 degrees counter-clockwise" msgid "Rotate 90 degrees counter-clockwise"
msgstr "Rotar 90 grados en sentido horario" msgstr "Rotar 90 grados en sentido horario"
#: photologue/models.py:108 #: photologue/models.py:106
msgid "Rotate 90 degrees clockwise" msgid "Rotate 90 degrees clockwise"
msgstr "Rotar 90 grados en sentido antihorario" msgstr "Rotar 90 grados en sentido antihorario"
#: photologue/models.py:109 #: photologue/models.py:107
msgid "Rotate 180 degrees" msgid "Rotate 180 degrees"
msgstr "Rotar 180 grados" msgstr "Rotar 180 grados"
#: photologue/models.py:119 #: photologue/models.py:117
#, python-format #, python-format
msgid "" msgid ""
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-" "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 " ">FILTRO_DOS->FILTRO_TRES\". Los filtros de imagen se aplicarán en orden. Los "
"siguientes filtros están disponibles: %s." "siguientes filtros están disponibles: %s."
#: photologue/models.py:141 #: photologue/models.py:139
msgid "date published" msgid "date published"
msgstr "fecha de publicación" msgstr "fecha de publicación"
#: photologue/models.py:143 photologue/models.py:474 #: photologue/models.py:141 photologue/models.py:485
msgid "title" msgid "title"
msgstr "título" msgstr "título"
#: photologue/models.py:146 #: photologue/models.py:144
msgid "title slug" msgid "title slug"
msgstr "título 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." 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." msgstr "Un \"slug\" es un único título URL-amigable para un objeto."
#: photologue/models.py:150 #: photologue/models.py:150
msgid "start date"
msgstr ""
#: photologue/models.py:155
msgid "end date"
msgstr ""
#: photologue/models.py:157
msgid "description" msgid "description"
msgstr "descripción" 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" msgid "is public"
msgstr "es público" msgstr "es público"
#: photologue/models.py:154 #: photologue/models.py:167
msgid "Public galleries will be displayed in the default views." msgid "Public galleries will be displayed in the default views."
msgstr "Las galerías públicas serán mostradas en las vistas por defecto." 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" msgid "photos"
msgstr "fotos" msgstr "fotos"
#: photologue/models.py:166 #: photologue/models.py:177
msgid "gallery" msgid "gallery"
msgstr "galería" msgstr "galería"
#: photologue/models.py:167 #: photologue/models.py:178
msgid "galleries" msgid "galleries"
msgstr "galerías" msgstr "galerías"
#: photologue/models.py:202 #: photologue/models.py:213
msgid "count" msgid "count"
msgstr "contar" msgstr "contar"
#: photologue/models.py:210 #: photologue/models.py:221
msgid "image" msgid "image"
msgstr "imagen" msgstr "imagen"
#: photologue/models.py:213 #: photologue/models.py:224
msgid "date taken" msgid "date taken"
msgstr "fecha en la que se tomó" 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." 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." 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" msgid "view count"
msgstr "Contador de visitas" msgstr "Contador de visitas"
#: photologue/models.py:220 #: photologue/models.py:231
msgid "crop from" msgid "crop from"
msgstr "Recortar desde" msgstr "Recortar desde"
#: photologue/models.py:243 #: photologue/models.py:254
msgid "An \"admin_thumbnail\" photo size has not been defined." msgid "An \"admin_thumbnail\" photo size has not been defined."
msgstr "El tamaño de foto de \"miniatura de admin\" no ha sido definido." msgstr "El tamaño de foto de \"miniatura de admin\" no ha sido definido."
#: photologue/models.py:250 #: photologue/models.py:261
msgid "Thumbnail" msgid "Thumbnail"
msgstr "Miniatura" msgstr "Miniatura"
#: photologue/models.py:477 #: photologue/models.py:488 photologue/models.py:696
msgid "slug" msgid "slug"
msgstr "slug" msgstr "slug"
#: photologue/models.py:481 #: photologue/models.py:492
msgid "caption" msgid "caption"
msgstr "pie de foto" msgstr "pie de foto"
#: photologue/models.py:483 #: photologue/models.py:494
msgid "date added" msgid "date added"
msgstr "fecha añadida" 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." msgid "Public photographs will be displayed in the default views."
msgstr "Las fotos públicas serán mostradas en las vistas por defecto." msgstr "Las fotos públicas serán mostradas en las vistas por defecto."
#: photologue/models.py:494 #: photologue/models.py:513
msgid "photo" msgid "photo"
msgstr "foto" msgstr "foto"
#: photologue/models.py:556 #: photologue/models.py:575 photologue/models.py:691
msgid "name" msgid "name"
msgstr "nombre" msgstr "nombre"
#: photologue/models.py:560 #: photologue/models.py:579
msgid "" msgid ""
"Photo size name should contain only letters, numbers and underscores. " "Photo size name should contain only letters, numbers and underscores. "
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"." "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 " "El nombre del tamaño solo puede contener letras, números y subrayados. Por "
"ejemplo:\"miniaturas\", \"muestra\", \"muestra_principal\"." "ejemplo:\"miniaturas\", \"muestra\", \"muestra_principal\"."
#: photologue/models.py:567 #: photologue/models.py:586
msgid "width" msgid "width"
msgstr "anchura" msgstr "anchura"
#: photologue/models.py:570 #: photologue/models.py:589
msgid "" msgid ""
"If width is set to \"0\" the image will be scaled to the supplied height." "If width is set to \"0\" the image will be scaled to the supplied height."
msgstr "" msgstr ""
"Si la anchura se establece a \"0\" la imagen será escalada hasta la altura " "Si la anchura se establece a \"0\" la imagen será escalada hasta la altura "
"proporcionada" "proporcionada"
#: photologue/models.py:571 #: photologue/models.py:590
msgid "height" msgid "height"
msgstr "altura" msgstr "altura"
#: photologue/models.py:574 #: photologue/models.py:593
msgid "" msgid ""
"If height is set to \"0\" the image will be scaled to the supplied width" "If height is set to \"0\" the image will be scaled to the supplied width"
msgstr "" msgstr ""
"Si la altura se establece a \"0\" la imagen será escalada hasta la anchura " "Si la altura se establece a \"0\" la imagen será escalada hasta la anchura "
"proporcionada" "proporcionada"
#: photologue/models.py:575 #: photologue/models.py:594
msgid "quality" msgid "quality"
msgstr "calidad" msgstr "calidad"
#: photologue/models.py:578 #: photologue/models.py:597
msgid "JPEG image quality." msgid "JPEG image quality."
msgstr "Calidad de imagen JPEG." msgstr "Calidad de imagen JPEG."
#: photologue/models.py:579 #: photologue/models.py:598
msgid "upscale images?" msgid "upscale images?"
msgstr "¿Aumentar imágenes?" msgstr "¿Aumentar imágenes?"
#: photologue/models.py:581 #: photologue/models.py:600
msgid "" msgid ""
"If selected the image will be scaled up if necessary to fit the supplied " "If selected the image will be scaled up if necessary to fit the supplied "
"dimensions. Cropped sizes will be upscaled regardless of this setting." "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 " "las dimensiones proporcionadas. Los tamaños recortados serán aumentados de "
"acuerdo a esta opción." "acuerdo a esta opción."
#: photologue/models.py:585 #: photologue/models.py:604
msgid "crop to fit?" msgid "crop to fit?"
msgstr "¿Recortar hasta ajustar?" msgstr "¿Recortar hasta ajustar?"
#: photologue/models.py:587 #: photologue/models.py:606
msgid "" msgid ""
"If selected the image will be scaled and cropped to fit the supplied " "If selected the image will be scaled and cropped to fit the supplied "
"dimensions." "dimensions."
@ -261,21 +281,21 @@ msgstr ""
"Si se selecciona la imagen será escalada y recortada para ajustarse a las " "Si se selecciona la imagen será escalada y recortada para ajustarse a las "
"dimensiones proporcionadas." "dimensiones proporcionadas."
#: photologue/models.py:589 #: photologue/models.py:608
msgid "pre-cache?" msgid "pre-cache?"
msgstr "¿pre-cachear?" 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." msgid "If selected this photo size will be pre-cached as photos are added."
msgstr "" msgstr ""
"Si se selecciona, este tamaño de foto será pre-cacheado cuando se añadan " "Si se selecciona, este tamaño de foto será pre-cacheado cuando se añadan "
"nuevas fotos." "nuevas fotos."
#: photologue/models.py:592 #: photologue/models.py:611
msgid "increment view count?" msgid "increment view count?"
msgstr "¿incrementar contador de visualizaciones?" msgstr "¿incrementar contador de visualizaciones?"
#: photologue/models.py:594 #: photologue/models.py:613
msgid "" msgid ""
"If selected the image's \"view_count\" will be incremented when this photo " "If selected the image's \"view_count\" will be incremented when this photo "
"size is displayed." "size is displayed."
@ -283,31 +303,31 @@ msgstr ""
"Si se selecciona el \"contador de visualizaciones\" se incrementará cuando " "Si se selecciona el \"contador de visualizaciones\" se incrementará cuando "
"esta foto sea visualizada." "esta foto sea visualizada."
#: photologue/models.py:599 #: photologue/models.py:618
msgid "photo size" msgid "photo size"
msgstr "tamaño de foto" msgstr "tamaño de foto"
#: photologue/models.py:600 #: photologue/models.py:619
msgid "photo sizes" msgid "photo sizes"
msgstr "tamaños de foto" 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." 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." msgstr "Solo puede recortar las fotos si ancho y alto están establecidos."
#: photologue_custom/admin.py:43 photologue_custom/models.py:51 #: photologue/models.py:702
msgid "owner" msgid "tag"
msgstr "" msgstr ""
#: photologue_custom/forms.py:34 #: photologue_custom/forms.py:22
msgid "Gallery" msgid "Gallery"
msgstr "Galería" msgstr "Galería"
#: photologue_custom/forms.py:36 #: photologue_custom/forms.py:24
msgid "-- Create a new gallery --" msgid "-- Create a new gallery --"
msgstr "" msgstr ""
#: photologue_custom/forms.py:37 #: photologue_custom/forms.py:25
msgid "" msgid ""
"Select a gallery to add these images to. Leave this empty to create a new " "Select a gallery to add these images to. Leave this empty to create a new "
"gallery from the supplied title." "gallery from the supplied title."
@ -315,58 +335,42 @@ msgstr ""
"Seleccione una galería para agregarle estas imágenes. Déjelo vacío para " "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." "crear una nueva galería a partir de este título."
#: photologue_custom/forms.py:41 #: photologue_custom/forms.py:29
#, fuzzy
#| msgid "View all galleries"
msgid "New gallery title" 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" msgid "New gallery event start date"
msgstr "" msgstr ""
#: photologue_custom/forms.py:51 #: photologue_custom/forms.py:39
msgid "New gallery event end date" msgid "New gallery event end date"
msgstr "" msgstr ""
#: photologue_custom/forms.py:57 #: photologue_custom/forms.py:45
#, fuzzy
#| msgid "gallery"
msgid "New gallery tags" msgid "New gallery tags"
msgstr "galería" msgstr ""
#: photologue_custom/forms.py:59 #: photologue_custom/forms.py:47
msgid "" msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr "" msgstr ""
#: photologue_custom/forms.py:76 #: photologue_custom/forms.py:64
#: photologue_custom/templates/photologue/upload.html:6 #: photologue_custom/templates/photologue/upload.html:6
#: photologue_custom/templates/photologue/upload.html:73 #: photologue_custom/templates/photologue/upload.html:73
msgid "Upload" msgid "Upload"
msgstr "Subir" msgstr "Subir"
#: photologue_custom/forms.py:82 #: photologue_custom/forms.py:70
msgid "A gallery with that title already exists." msgid "A gallery with that title already exists."
msgstr "Ya existe una galería con ese título." 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." msgid "Select an existing gallery, or enter a title for a new gallery."
msgstr "" msgstr ""
"Seleccione una galería existente o ingrese un nuevo nombre para la galería." "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:7
#: photologue_custom/templates/photologue/gallery_archive.html:12 #: photologue_custom/templates/photologue/gallery_archive.html:12
msgid "Latest photo galleries" msgid "Latest photo galleries"
@ -399,16 +403,12 @@ msgid "to"
msgstr "" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:57 #: photologue_custom/templates/photologue/gallery_detail.html:57
#, fuzzy
#| msgid "All photos"
msgid "All pictures" msgid "All pictures"
msgstr "Todas las fotos" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:78 #: photologue_custom/templates/photologue/gallery_detail.html:78
#, fuzzy
#| msgid "View all galleries"
msgid "Download all gallery" msgid "Download all gallery"
msgstr "Ver todas las galerías" msgstr ""
#: photologue_custom/templates/photologue/photo_detail.html:13 #: photologue_custom/templates/photologue/photo_detail.html:13
msgid "Published" msgid "Published"

View file

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Photologue\n" "Project-Id-Version: Photologue\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2017-12-03 14:47+0000\n"
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n" "Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
"Language-Team: French (http://www.transifex.com/richardbarran/django-" "Language-Team: French (http://www.transifex.com/richardbarran/django-"
@ -21,75 +21,131 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\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" msgid "Very Low"
msgstr "Très Bas" msgstr "Très Bas"
#: photologue/models.py:87 #: photologue/models.py:85
msgid "Low" msgid "Low"
msgstr "Bas" msgstr "Bas"
#: photologue/models.py:88 #: photologue/models.py:86
msgid "Medium-Low" msgid "Medium-Low"
msgstr "Moyen-Bas" msgstr "Moyen-Bas"
#: photologue/models.py:89 #: photologue/models.py:87
msgid "Medium" msgid "Medium"
msgstr "Moyen" msgstr "Moyen"
#: photologue/models.py:90 #: photologue/models.py:88
msgid "Medium-High" msgid "Medium-High"
msgstr "Moyen-Haut" msgstr "Moyen-Haut"
#: photologue/models.py:91 #: photologue/models.py:89
msgid "High" msgid "High"
msgstr "Haut" msgstr "Haut"
#: photologue/models.py:92 #: photologue/models.py:90
msgid "Very High" msgid "Very High"
msgstr "Très Haut" msgstr "Très Haut"
#: photologue/models.py:97 #: photologue/models.py:95
msgid "Top" msgid "Top"
msgstr "Sommet" msgstr "Sommet"
#: photologue/models.py:98 #: photologue/models.py:96
msgid "Right" msgid "Right"
msgstr "Droite" msgstr "Droite"
#: photologue/models.py:99 #: photologue/models.py:97
msgid "Bottom" msgid "Bottom"
msgstr "Bas" msgstr "Bas"
#: photologue/models.py:100 #: photologue/models.py:98
msgid "Left" msgid "Left"
msgstr "Gauche" msgstr "Gauche"
#: photologue/models.py:101 #: photologue/models.py:99
msgid "Center (Default)" msgid "Center (Default)"
msgstr "Centré (par défaut)" msgstr "Centré (par défaut)"
#: photologue/models.py:105 #: photologue/models.py:103
msgid "Flip left to right" msgid "Flip left to right"
msgstr "Inversion de gauche à droite" msgstr "Inversion de gauche à droite"
#: photologue/models.py:106 #: photologue/models.py:104
msgid "Flip top to bottom" msgid "Flip top to bottom"
msgstr "Inversion de haut en bas" msgstr "Inversion de haut en bas"
#: photologue/models.py:107 #: photologue/models.py:105
msgid "Rotate 90 degrees counter-clockwise" msgid "Rotate 90 degrees counter-clockwise"
msgstr "Rotation de 90 degrés dans le sens anti-horloger" msgstr "Rotation de 90 degrés dans le sens anti-horloger"
#: photologue/models.py:108 #: photologue/models.py:106
msgid "Rotate 90 degrees clockwise" msgid "Rotate 90 degrees clockwise"
msgstr "Rotation de 90 degrés dans le sens horloger" msgstr "Rotation de 90 degrés dans le sens horloger"
#: photologue/models.py:109 #: photologue/models.py:107
msgid "Rotate 180 degrees" msgid "Rotate 180 degrees"
msgstr "Rotation de 180 degrés" msgstr "Rotation de 180 degrés"
#: photologue/models.py:119 #: photologue/models.py:117
#, python-format #, python-format
msgid "" msgid ""
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-" "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 " ">FILTRE_DEUX->FILTRE_TROIS\". Les filtres d'image seront appliqués dans "
"l'ordre. Les filtres suivants sont disponibles: %s." "l'ordre. Les filtres suivants sont disponibles: %s."
#: photologue/models.py:141 #: photologue/models.py:139
msgid "date published" msgid "date published"
msgstr "date de publication" msgstr "date de publication"
#: photologue/models.py:143 photologue/models.py:474 #: photologue/models.py:141 photologue/models.py:485
msgid "title" msgid "title"
msgstr "titre" msgstr "titre"
#: photologue/models.py:146 #: photologue/models.py:144
msgid "title slug" msgid "title slug"
msgstr "version abrégée du titre" 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." msgid "A \"slug\" is a unique URL-friendly title for an object."
msgstr "" msgstr ""
"Un \"slug\" est un titre abrégé et unique, compatible avec les URL, pour un " "Un \"slug\" est un titre abrégé et unique, compatible avec les URL, pour un "
"objet." "objet."
#: photologue/models.py:150 #: 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" msgid "description"
msgstr "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" msgid "is public"
msgstr "est public" msgstr "est public"
#: photologue/models.py:154 #: photologue/models.py:167
msgid "Public galleries will be displayed in the default views." msgid "Public galleries will be displayed in the default views."
msgstr "Les galeries publiques seront affichée dans les vues par défaut." 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" msgid "photos"
msgstr "photos" msgstr "photos"
#: photologue/models.py:166 #: photologue/models.py:177
msgid "gallery" msgid "gallery"
msgstr "galerie" msgstr "galerie"
#: photologue/models.py:167 #: photologue/models.py:178
msgid "galleries" msgid "galleries"
msgstr "galleries" msgstr "galleries"
#: photologue/models.py:202 #: photologue/models.py:213
msgid "count" msgid "count"
msgstr "nombre" msgstr "nombre"
#: photologue/models.py:210 #: photologue/models.py:221
msgid "image" msgid "image"
msgstr "image" msgstr "image"
#: photologue/models.py:213 #: photologue/models.py:224
msgid "date taken" msgid "date taken"
msgstr "date de prise de vue" 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." msgid "Date image was taken; is obtained from the image EXIF data."
msgstr "" msgstr ""
"La date à laquelle l'image a été prise ; obtenue à partir des données EXIF " "La date à laquelle l'image a été prise ; obtenue à partir des données EXIF "
"de l'image." "de l'image."
#: photologue/models.py:217 #: photologue/models.py:228
msgid "view count" msgid "view count"
msgstr "nombre" msgstr "nombre"
#: photologue/models.py:220 #: photologue/models.py:231
msgid "crop from" msgid "crop from"
msgstr "découper à partir de" msgstr "découper à partir de"
#: photologue/models.py:243 #: photologue/models.py:254
msgid "An \"admin_thumbnail\" photo size has not been defined." msgid "An \"admin_thumbnail\" photo size has not been defined."
msgstr "Une taille de photo \"admin_thumbnail\" n'a pas encore été définie." msgstr "Une taille de photo \"admin_thumbnail\" n'a pas encore été définie."
#: photologue/models.py:250 #: photologue/models.py:261
msgid "Thumbnail" msgid "Thumbnail"
msgstr "Miniature" msgstr "Miniature"
#: photologue/models.py:477 #: photologue/models.py:488 photologue/models.py:696
msgid "slug" msgid "slug"
msgstr "libellé court" msgstr "libellé court"
#: photologue/models.py:481 #: photologue/models.py:492
msgid "caption" msgid "caption"
msgstr "légende" msgstr "légende"
#: photologue/models.py:483 #: photologue/models.py:494
msgid "date added" msgid "date added"
msgstr "date d'ajout" 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." msgid "Public photographs will be displayed in the default views."
msgstr "Les photographies publique seront affichées dans les vues par défaut." msgstr "Les photographies publique seront affichées dans les vues par défaut."
#: photologue/models.py:494 #: photologue/models.py:513
msgid "photo" msgid "photo"
msgstr "photo" msgstr "photo"
#: photologue/models.py:556 #: photologue/models.py:575 photologue/models.py:691
msgid "name" msgid "name"
msgstr "nom" msgstr "nom"
#: photologue/models.py:560 #: photologue/models.py:579
msgid "" msgid ""
"Photo size name should contain only letters, numbers and underscores. " "Photo size name should contain only letters, numbers and underscores. "
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"." "Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
@ -209,41 +281,41 @@ msgstr ""
"chiffres et des caractères de soulignement. Exemples: \"miniature\", " "chiffres et des caractères de soulignement. Exemples: \"miniature\", "
"\"affichage\", \"petit\", \"widget_page_principale\"." "\"affichage\", \"petit\", \"widget_page_principale\"."
#: photologue/models.py:567 #: photologue/models.py:586
msgid "width" msgid "width"
msgstr "largeur" msgstr "largeur"
#: photologue/models.py:570 #: photologue/models.py:589
msgid "" msgid ""
"If width is set to \"0\" the image will be scaled to the supplied height." "If width is set to \"0\" the image will be scaled to the supplied height."
msgstr "" msgstr ""
"Si la largeur est réglée à \"0\" l l'image sera redimensionnée par rapport à " "Si la largeur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
"la hauteur fournie." "la hauteur fournie."
#: photologue/models.py:571 #: photologue/models.py:590
msgid "height" msgid "height"
msgstr "hauteur" msgstr "hauteur"
#: photologue/models.py:574 #: photologue/models.py:593
msgid "" msgid ""
"If height is set to \"0\" the image will be scaled to the supplied width" "If height is set to \"0\" the image will be scaled to the supplied width"
msgstr "" msgstr ""
"Si la hauteur est réglée à \"0\" l l'image sera redimensionnée par rapport à " "Si la hauteur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
"la largeur fournie." "la largeur fournie."
#: photologue/models.py:575 #: photologue/models.py:594
msgid "quality" msgid "quality"
msgstr "qualité" msgstr "qualité"
#: photologue/models.py:578 #: photologue/models.py:597
msgid "JPEG image quality." msgid "JPEG image quality."
msgstr "Qualité JPEG de l'image." msgstr "Qualité JPEG de l'image."
#: photologue/models.py:579 #: photologue/models.py:598
msgid "upscale images?" msgid "upscale images?"
msgstr "agrandir les images ?" msgstr "agrandir les images ?"
#: photologue/models.py:581 #: photologue/models.py:600
msgid "" msgid ""
"If selected the image will be scaled up if necessary to fit the supplied " "If selected the image will be scaled up if necessary to fit the supplied "
"dimensions. Cropped sizes will be upscaled regardless of this setting." "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 " "dimensions fournies. Les dimensions ajustées seront agrandies sans prendre "
"en compte ce paramètre." "en compte ce paramètre."
#: photologue/models.py:585 #: photologue/models.py:604
msgid "crop to fit?" msgid "crop to fit?"
msgstr "découper pour adapter à la taille ?" msgstr "découper pour adapter à la taille ?"
#: photologue/models.py:587 #: photologue/models.py:606
msgid "" msgid ""
"If selected the image will be scaled and cropped to fit the supplied " "If selected the image will be scaled and cropped to fit the supplied "
"dimensions." "dimensions."
@ -264,21 +336,21 @@ msgstr ""
"Si sélectionné l'image sera redimensionnée et recadrée pour coïncider avec " "Si sélectionné l'image sera redimensionnée et recadrée pour coïncider avec "
"les dimensions fournies." "les dimensions fournies."
#: photologue/models.py:589 #: photologue/models.py:608
msgid "pre-cache?" msgid "pre-cache?"
msgstr "mise en 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." msgid "If selected this photo size will be pre-cached as photos are added."
msgstr "" msgstr ""
"Si sélectionné cette taille de photo sera mise en cache au moment au les " "Si sélectionné cette taille de photo sera mise en cache au moment au les "
"photos sont ajoutées." "photos sont ajoutées."
#: photologue/models.py:592 #: photologue/models.py:611
msgid "increment view count?" msgid "increment view count?"
msgstr "incrémenter le nombre d'affichages ?" msgstr "incrémenter le nombre d'affichages ?"
#: photologue/models.py:594 #: photologue/models.py:613
msgid "" msgid ""
"If selected the image's \"view_count\" will be incremented when this photo " "If selected the image's \"view_count\" will be incremented when this photo "
"size is displayed." "size is displayed."
@ -286,148 +358,75 @@ msgstr ""
"Si sélectionné le \"view_count\" (nombre d'affichage) de l'image sera " "Si sélectionné le \"view_count\" (nombre d'affichage) de l'image sera "
"incrémenté quand cette taille de photo sera affichée." "incrémenté quand cette taille de photo sera affichée."
#: photologue/models.py:599 #: photologue/models.py:618
msgid "photo size" msgid "photo size"
msgstr "taille de la photo" msgstr "taille de la photo"
#: photologue/models.py:600 #: photologue/models.py:619
msgid "photo sizes" msgid "photo sizes"
msgstr "tailles des photos" 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." msgid "Can only crop photos if both width and height dimensions are set."
msgstr "" msgstr ""
"La hauteur et la largeur doivent être toutes les deux définies pour " "La hauteur et la largeur doivent être toutes les deux définies pour "
"retailler des photos." "retailler des photos."
#: photologue_custom/admin.py:43 photologue_custom/models.py:51 #: photologue/models.py:702
msgid "owner" msgid "tag"
msgstr "" msgstr ""
#: photologue_custom/forms.py:34 #: photologue/templates/photologue/gallery_archive.html:7
msgid "Gallery" #: photologue/templates/photologue/gallery_archive.html:12
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."
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
msgid "Latest photo galleries" msgid "Latest photo galleries"
msgstr "Dernières galeries de photos" 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" msgid "Filter by year"
msgstr "Filtrer par année" 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" msgid "No galleries were found"
msgstr "Aucune galerie trouvée" msgstr "Aucune galerie trouvée"
#: photologue_custom/templates/photologue/gallery_archive_year.html:7 #: photologue/templates/photologue/gallery_archive_year.html:7
#: photologue_custom/templates/photologue/gallery_archive_year.html:12 #: photologue/templates/photologue/gallery_archive_year.html:12
#, python-format #, python-format
msgid "Galleries for %(show_year)s" msgid "Galleries for %(show_year)s"
msgstr "Galeries de %(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" msgid "View all galleries"
msgstr "Afficher toutes les galeries" 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." msgid "No galleries were found."
msgstr "Aucune galerie trouvée." msgstr "Aucune galerie trouvée."
#: photologue_custom/templates/photologue/gallery_detail.html:41 #: photologue/templates/photologue/gallery_detail.html:41
msgid "to" msgid "to"
msgstr "" msgstr "au"
#: photologue_custom/templates/photologue/gallery_detail.html:57 #: photologue/templates/photologue/gallery_detail.html:57
#, fuzzy
#| msgid "All photos"
msgid "All pictures" msgid "All pictures"
msgstr "Toutes les photos" msgstr "Toutes les photos"
#: photologue_custom/templates/photologue/gallery_detail.html:78 #: photologue/templates/photologue/gallery_detail.html:78
#, fuzzy
#| msgid "View all galleries"
msgid "Download all gallery" 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" msgid "Published"
msgstr "Publiée le" 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" msgid "This photo is found in the following galleries"
msgstr "Cette photo se trouve dans les galeries suivantes" 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" 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" msgid "Owner will be"
msgstr "" msgstr "Le propriétaire sera"

View file

@ -1,8 +1,8 @@
import hashlib
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from photologue.models import Gallery from photologue.models import Gallery
import hashlib
class Command(BaseCommand): class Command(BaseCommand):
help = 'List all duplicate for chosen galleries' help = 'List all duplicate for chosen galleries'

View file

@ -1,9 +1,9 @@
from pathlib import Path
import os import os
from pathlib import Path
from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from photologue.models import Gallery from photologue.models import Gallery
from django.conf import settings
class Command(BaseCommand): class Command(BaseCommand):
@ -16,7 +16,7 @@ class Command(BaseCommand):
media_dir = Path(settings.MEDIA_ROOT) media_dir = Path(settings.MEDIA_ROOT)
for gallery in Gallery.objects.all(): for gallery in Gallery.objects.all():
# Create gallery directory # 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_dir = Path('photos') / gallery_year / gallery.slug
gallery_path = media_dir / gallery_dir gallery_path = media_dir / gallery_dir
if not gallery_path.exists(): if not gallery_path.exists():

View file

@ -1,158 +1,96 @@
# -*- coding: utf-8 -*- # Generated by Django 3.2.11 on 2022-01-30 10:14
from __future__ import unicode_literals
from django.conf import settings
import django.core.validators import django.core.validators
import django.utils.timezone
import sortedm2m.fields
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import photologue.models import photologue.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
('sites', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Gallery', name='PhotoSize',
fields=[ 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')),
('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published')), ('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')),
('title', models.CharField(max_length=50, verbose_name='title', unique=True)), ('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')),
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', verbose_name='title slug', unique=True)), ('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')),
('description', models.TextField(blank=True, verbose_name='description')), ('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')),
('is_public', models.BooleanField(help_text='Public galleries will be displayed in the default views.', verbose_name='is public', default=True)), ('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?')),
('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')), ('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?')),
('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')), ('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={ options={
'get_latest_by': 'date_added', 'verbose_name': 'photo size',
'verbose_name': 'gallery', 'verbose_name_plural': 'photo sizes',
'ordering': ['-date_added'], 'ordering': ['width', 'height'],
'verbose_name_plural': 'galleries',
}, },
bases=(models.Model,),
), ),
migrations.CreateModel( migrations.CreateModel(
name='GalleryUpload', name='Tag',
fields=[ 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')),
('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')), ('name', models.CharField(max_length=250, unique=True, verbose_name='name')),
('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')), ('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(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)),
], ],
options={ options={
'verbose_name': 'gallery upload', 'verbose_name': 'tag',
'verbose_name_plural': 'gallery uploads', 'verbose_name_plural': 'tags',
'ordering': ['name'],
}, },
bases=(models.Model,),
), ),
migrations.CreateModel( migrations.CreateModel(
name='Photo', name='Photo',
fields=[ 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')), ('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)), ('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(verbose_name='view count', default=0, editable=False)), ('view_count', models.PositiveIntegerField(default=0, editable=False, verbose_name='view count')),
('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)')])), ('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=50, verbose_name='title', unique=True)), ('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.', verbose_name='slug', unique=True)), ('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')), ('caption', models.TextField(blank=True, verbose_name='caption')),
('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date added')), ('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)), ('license', models.CharField(blank=True, max_length=255, verbose_name='license')),
('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')), ('is_public', models.BooleanField(default=True, help_text='Public photographs will be displayed in the default views.', verbose_name='is public')),
('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='owner')),
], ],
options={ options={
'get_latest_by': 'date_added',
'verbose_name': 'photo', 'verbose_name': 'photo',
'ordering': ['-date_added'],
'verbose_name_plural': 'photos', '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( migrations.CreateModel(
name='PhotoEffect', name='Gallery',
fields=[ 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')),
('name', models.CharField(max_length=30, verbose_name='name', unique=True)), ('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')), ('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')])), ('is_public', models.BooleanField(default=True, help_text='Public galleries will be displayed in the default views.', verbose_name='is public')),
('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)), ('photos', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Photo', verbose_name='photos')),
('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)), ('tags', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Tag', verbose_name='tags')),
('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={ options={
'verbose_name': 'photo effect', 'verbose_name': 'gallery',
'verbose_name_plural': 'photo effects', 'verbose_name_plural': 'galleries',
'ordering': ['-date_added'],
'get_latest_by': 'date_added',
}, },
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',
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')),
('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)),
],
options={
'verbose_name': 'watermark',
'verbose_name_plural': 'watermarks',
},
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,
), ),
] ]

View file

@ -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',
),
]

View file

@ -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),
]

View file

@ -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),
),
]

View file

@ -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),
),
]

View file

@ -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,
),
]

View file

@ -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',
),
]

View file

@ -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),
),
]

View file

@ -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',
),
]

View file

@ -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'),
),
]

View file

@ -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'),
),
]

View file

@ -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'),
),
]

View file

@ -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',
),
]

View file

@ -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'),
),
]

View file

@ -136,8 +136,6 @@ class TagField(models.CharField):
class Gallery(models.Model): class Gallery(models.Model):
date_added = models.DateTimeField(_('date published'),
default=now)
title = models.CharField(_('title'), title = models.CharField(_('title'),
max_length=250, max_length=250,
unique=True) unique=True)
@ -145,8 +143,23 @@ class Gallery(models.Model):
unique=True, unique=True,
max_length=250, max_length=250,
help_text=_('A "slug" is a unique URL-friendly title for an object.')) 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'), description = models.TextField(_('description'),
blank=True) blank=True)
tags = models.ManyToManyField(
'photologue.Tag',
related_name='galleries',
verbose_name=_('tags'),
blank=True,
)
is_public = models.BooleanField(_('is public'), is_public = models.BooleanField(_('is public'),
default=True, default=True,
help_text=_('Public galleries will be displayed ' help_text=_('Public galleries will be displayed '
@ -157,13 +170,13 @@ class Gallery(models.Model):
blank=True) blank=True)
class Meta: class Meta:
ordering = ['-date_added'] ordering = ['-date_start']
get_latest_by = 'date_added' get_latest_by = 'date_start'
verbose_name = _('gallery') verbose_name = _('gallery')
verbose_name_plural = _('galleries') verbose_name_plural = _('galleries')
def __str__(self): def __str__(self):
return self.title return f"{ self.title } ({self.date_start})"
def get_absolute_url(self): def get_absolute_url(self):
return reverse('photologue:pl-gallery', args=[self.slug]) return reverse('photologue:pl-gallery', args=[self.slug])
@ -478,6 +491,16 @@ class Photo(ImageModel):
blank=True) blank=True)
date_added = models.DateTimeField(_('date added'), date_added = models.DateTimeField(_('date added'),
default=now) 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'), is_public = models.BooleanField(_('is public'),
default=True, default=True,
help_text=_('Public photographs will be displayed in the default views.')) help_text=_('Public photographs will be displayed in the default views.'))
@ -657,3 +680,25 @@ def init_size_method_map():
{'base_name': '_get_size_url', 'size': size} {'base_name': '_get_size_url', 'size': size}
size_method_map['get_%s_filename' % size] = \ size_method_map['get_%s_filename' % size] = \
{'base_name': '_get_size_filename', 'size': 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

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before After
Before After

View file

@ -38,10 +38,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
</a> </a>
{% endif %} {% endif %}
</h1> </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.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.extended.tags.all %} {% if gallery.tags.all %}
<p class="text-muted"> <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> <a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'photologue:tag-detail' tag.slug %}">{{ tag }}</a>
{% endfor %} {% endfor %}
</p> </p>
@ -70,7 +70,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<div class="card-body row" id="lightgallery"> <div class="card-body row" id="lightgallery">
{% for photo in photos %} {% 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 }}"> <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.owner.get_full_name }}{% if photo.license %} - {{ photo.license }}{% endif %}">
</a> </a>
{% endfor %} {% endfor %}
</div> </div>

View file

@ -6,7 +6,7 @@
{% endfor %} {% endfor %}
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ gallery.title }}</h5> <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> <a href="{{ gallery.get_absolute_url }}" class="stretched-link"></a>
</div> </div>
</div> </div>

View file

@ -1,10 +1,9 @@
from django.urls import path, re_path from django.urls import path, re_path
from photologue.views import GalleryArchiveIndexView, GalleryYearArchiveView, PhotoDetailView
from .views import CustomGalleryDetailView, GalleryDownload, GalleryUpload, TagDetail from .views import (CustomGalleryDetailView, GalleryArchiveIndexView,
GalleryDownload, GalleryUpload, GalleryYearArchiveView,
PhotoDetailView, TagDetail)
# Rather than using photologue default router, we redefine our own router
# with login and permission checks.
app_name = 'photologue' app_name = 'photologue'
urlpatterns = [ urlpatterns = [
path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'), path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'),

View file

@ -1,13 +1,31 @@
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.dates import ArchiveIndexView, YearArchiveView
from django.views.generic.detail import DetailView 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): class GalleryDateView(LoginRequiredMixin):
queryset = Gallery.objects.filter(is_public=True) queryset = Gallery.objects.filter(is_public=True)
date_field = 'extended__date_start' date_field = 'date_start'
uses_datetime_field = False # Fix related object access uses_datetime_field = False # Fix related object access
allow_empty = True allow_empty = True
@ -22,3 +40,125 @@ class GalleryYearArchiveView(GalleryDateView, YearArchiveView):
class PhotoDetailView(LoginRequiredMixin, DetailView): class PhotoDetailView(LoginRequiredMixin, DetailView):
queryset = Photo.objects.filter(is_public=True) 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)

View file

@ -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)

View file

@ -1,6 +0,0 @@
from django.apps import AppConfig
class PhotologueCustomConfig(AppConfig):
default_auto_field = 'django.db.models.AutoField'
name = 'photologue_custom'

View file

@ -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',
},
),
]

View file

@ -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'),
),
]

View file

@ -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'),
),
]

View file

@ -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'),
),
]

View file

@ -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)

View file

@ -1,143 +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
from PIL import Image
from taggit.models import Tag
from .forms import UploadForm
from .models import PhotoExtended
class TagDetail(LoginRequiredMixin, DetailView):
model = Tag
def get_context_data(self, **kwargs):
"""
Insert the single object into the context dict.
"""
current_tag = self.get_object().slug
context = super().get_context_data(**kwargs)
context['galleries'] = Gallery.objects.filter(is_public=True) \
.filter(extended__tags__slug=current_tag) \
.order_by('-extended__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 extended and owner to reduce database lag
context['photos'] = self.object.public().select_related('extended__owner')
# List owners
context['owners'] = []
for photo in context['photos']:
if hasattr(photo, 'extended') and photo.extended.owner not in context['owners']:
context['owners'].append(photo.extended.owner)
# Filter on owner
if 'owner' in self.kwargs:
context['photos'] = context['photos'].filter(extended__owner__id=self.kwargs['owner'])
return context
class GalleryDownload(LoginRequiredMixin, DetailView):
model = Gallery
def get(self, request, *args, **kwargs):
"""
Download a zip file of the gallery on GET request.
"""
# Create zip file with pictures
gallery = self.get_object()
byte_data = BytesIO()
zip_file = zipfile.ZipFile(byte_data, "w")
for photo in gallery.public():
filename = os.path.basename(os.path.normpath(photo.image.path))
zip_file.write(photo.image.path, filename)
zip_file.close()
# Return zip file
response = HttpResponse(byte_data.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = f"attachment; filename={gallery.slug}.zip"
return response
class GalleryUpload(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.extended.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))
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
# 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)

View file

@ -1,6 +1,5 @@
django-allauth>=0.44 django-allauth>=0.44
django-crispy-forms~=1.7 django-crispy-forms~=1.7
django-taggit>=1.5.0
Django>=2.2.20 Django>=2.2.20
ExifRead>=2.1.2 ExifRead>=2.1.2
git+https://gitlab.crans.org/bde/allauth-note-kfet.git git+https://gitlab.crans.org/bde/allauth-note-kfet.git

View file

@ -12,7 +12,7 @@ deps =
-r{toxinidir}/requirements.txt -r{toxinidir}/requirements.txt
coverage coverage
commands = 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 coverage report -m
[testenv:linters] [testenv:linters]
@ -26,7 +26,7 @@ deps =
pep8-naming pep8-naming
pyflakes pyflakes
commands = commands =
flake8 photo21 photologue photologue_custom flake8 photo21 photologue
[flake8] [flake8]
ignore = W503, I100, I101 ignore = W503, I100, I101