Merge branch 'simplify_models' into 'master'
Simplify models See merge request bde/photo21!22
This commit is contained in:
commit
d7a39a0334
64 changed files with 732 additions and 1268 deletions
|
|
@ -79,8 +79,7 @@ production néccessite **une installation de Debian Bullseye ou plus récent**.
|
|||
```
|
||||
$ sudo apt install nginx git gettext uwsgi uwsgi-plugin-python3 python3-venv \
|
||||
python3-certbot-nginx python3-django python3-django-crispy-forms \
|
||||
python3-django-taggit python3-pil python3-exifread python3-django-allauth \
|
||||
python3-psycopg2 python3-docutils
|
||||
python3-pil python3-exifread python3-django-allauth python3-docutils
|
||||
```
|
||||
|
||||
2. **Clonage du dépot dans `/var/www/photos/photo21`**
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-29 21:58+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 09:55+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -40,19 +40,19 @@ msgstr ""
|
|||
msgid "hash"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:162
|
||||
#: photo21/settings.py:161
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:163
|
||||
#: photo21/settings.py:162
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:164
|
||||
#: photo21/settings.py:163
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:165
|
||||
#: photo21/settings.py:164
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -199,6 +199,16 @@ msgstr ""
|
|||
msgid "If any problem, please contact the server owners at"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/logout.html:6
|
||||
#: photo21/templates/account/logout.html:11
|
||||
#: photo21/templates/account/logout.html:20
|
||||
msgid "Sign Out"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/logout.html:14
|
||||
msgid "Are you sure you want to sign out?"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/signup.html:6
|
||||
msgid "Signup"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-29 21:58+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 09:55+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -39,19 +39,19 @@ msgstr ""
|
|||
msgid "hash"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:162
|
||||
#: photo21/settings.py:161
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:163
|
||||
#: photo21/settings.py:162
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:164
|
||||
#: photo21/settings.py:163
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:165
|
||||
#: photo21/settings.py:164
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -198,6 +198,16 @@ msgstr ""
|
|||
msgid "If any problem, please contact the server owners at"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/logout.html:6
|
||||
#: photo21/templates/account/logout.html:11
|
||||
#: photo21/templates/account/logout.html:20
|
||||
msgid "Sign Out"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/logout.html:14
|
||||
msgid "Are you sure you want to sign out?"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/signup.html:6
|
||||
msgid "Signup"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-30 07:09+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 10:06+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -42,19 +42,19 @@ msgstr ""
|
|||
msgid "hash"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:162
|
||||
#: photo21/settings.py:160
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:163
|
||||
#: photo21/settings.py:161
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:164
|
||||
#: photo21/settings.py:162
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/settings.py:165
|
||||
#: photo21/settings.py:163
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -206,6 +206,18 @@ msgstr ""
|
|||
msgid "If any problem, please contact the server owners at"
|
||||
msgstr "En cas de problème, contactez les administrateurs à"
|
||||
|
||||
#: photo21/templates/account/logout.html:6
|
||||
#: photo21/templates/account/logout.html:11
|
||||
#: photo21/templates/account/logout.html:20
|
||||
#, fuzzy
|
||||
#| msgid "Sign up"
|
||||
msgid "Sign Out"
|
||||
msgstr "Inscription"
|
||||
|
||||
#: photo21/templates/account/logout.html:14
|
||||
msgid "Are you sure you want to sign out?"
|
||||
msgstr ""
|
||||
|
||||
#: photo21/templates/account/signup.html:6
|
||||
msgid "Signup"
|
||||
msgstr ""
|
||||
|
|
@ -333,48 +345,3 @@ msgstr ""
|
|||
#: photo21/templates/socialaccount/connections.html:54
|
||||
msgid "Add a 3rd Party Account"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "owner"
|
||||
#~ msgstr "propriétaire"
|
||||
|
||||
#~ msgid "Gallery"
|
||||
#~ msgstr "Galerie"
|
||||
|
||||
#~ msgid "-- Create a new gallery --"
|
||||
#~ msgstr "-- Créer une nouvelle galerie --"
|
||||
|
||||
#~ msgid "New gallery title"
|
||||
#~ msgstr "Titre de la nouvelle galerie"
|
||||
|
||||
#~ msgid "New gallery event start date"
|
||||
#~ msgstr "Date de début de l'évènement de la nouvelle galerie"
|
||||
|
||||
#~ msgid "New gallery event end date"
|
||||
#~ msgstr "Date de fin de l'évènement de la nouvelle galerie"
|
||||
|
||||
#~ msgid "New gallery tags"
|
||||
#~ msgstr "Tags de la nouvelle galerie"
|
||||
|
||||
#~ msgid "start date"
|
||||
#~ msgstr "date de début"
|
||||
|
||||
#~ msgid "end date"
|
||||
#~ msgstr "date de fin"
|
||||
|
||||
#~ msgid "license"
|
||||
#~ msgstr "licence"
|
||||
|
||||
#~ msgid "to"
|
||||
#~ msgstr "au"
|
||||
|
||||
#~ msgid "All pictures"
|
||||
#~ msgstr "Toutes les photos"
|
||||
|
||||
#~ msgid "Download all gallery"
|
||||
#~ msgstr "Télécharger toute la galerie"
|
||||
|
||||
#~ msgid "Drag and drop photos here"
|
||||
#~ msgstr "Glissez et déposez les photos ici"
|
||||
|
||||
#~ msgid "Owner will be"
|
||||
#~ msgstr "Le propriétaire sera"
|
||||
|
|
|
|||
|
|
@ -62,9 +62,7 @@ INSTALLED_APPS = [
|
|||
'allauth.socialaccount',
|
||||
'allauth_note_kfet',
|
||||
'crispy_forms',
|
||||
'photologue_custom',
|
||||
'photologue',
|
||||
'taggit',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
@ -156,8 +154,6 @@ USE_L10N = True
|
|||
|
||||
USE_TZ = True
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# Limit available languages to this subset
|
||||
LANGUAGES = [
|
||||
('de', _('German')),
|
||||
|
|
|
|||
24
photo21/templates/account/logout.html
Normal file
24
photo21/templates/account/logout.html
Normal 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 %}
|
||||
|
|
@ -22,11 +22,11 @@ from .views import IndexView, MediaAccess
|
|||
|
||||
urlpatterns = [
|
||||
path('', IndexView.as_view(), name='index'),
|
||||
path('', include('photologue_custom.urls', namespace='photologue')),
|
||||
path('', include('photologue.urls', namespace='photologue')),
|
||||
path('accounts/', include('allauth.urls')),
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
path('admin/', admin.site.urls),
|
||||
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
|
||||
# In production media are served through NGINX with X-Accel-Redirect
|
||||
|
|
|
|||
|
|
@ -1,24 +1,41 @@
|
|||
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):
|
||||
list_display = ('title', 'date_added', 'photo_count', 'is_public')
|
||||
list_filter = ['date_added', 'is_public']
|
||||
date_hierarchy = 'date_added'
|
||||
list_display = ('title', 'date_start', 'photo_count', 'is_public')
|
||||
list_filter = ['date_start', 'is_public']
|
||||
date_hierarchy = 'date_start'
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
model = Gallery
|
||||
autocomplete_fields = ['photos', ]
|
||||
autocomplete_fields = ['photos', 'tags']
|
||||
search_fields = ['title', ]
|
||||
|
||||
|
||||
class PhotoAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'date_taken', 'date_added',
|
||||
'is_public', 'view_count', 'admin_thumbnail')
|
||||
list_filter = ['date_added', 'is_public']
|
||||
'is_public', 'view_count', 'admin_thumbnail', 'get_owner')
|
||||
list_filter = ['date_added', 'is_public', 'owner']
|
||||
search_fields = ['title', 'slug', 'caption']
|
||||
list_per_page = 10
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
readonly_fields = ('date_taken',)
|
||||
model = Photo
|
||||
|
||||
def get_owner(self, obj):
|
||||
return obj.owner.username
|
||||
get_owner.admin_order_field = 'owner'
|
||||
get_owner.short_description = _('owner')
|
||||
|
||||
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',)
|
||||
search_fields = ('name',)
|
||||
model = Tag
|
||||
|
||||
|
||||
admin.site.register(Gallery, GalleryAdmin)
|
||||
admin.site.register(Photo, PhotoAdmin)
|
||||
admin.site.register(Tag, TagAdmin)
|
||||
|
|
|
|||
|
|
@ -5,19 +5,8 @@ from crispy_forms.layout import Div, Layout, Submit
|
|||
from django import forms
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from photologue.models import Gallery
|
||||
from taggit.models import Tag
|
||||
|
||||
from .models import GalleryExtended
|
||||
|
||||
|
||||
class GalleryChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
"""Show gallery event date."""
|
||||
if hasattr(obj, 'extended'):
|
||||
return f"{ obj.title } ({obj.extended.date_start})"
|
||||
else:
|
||||
return obj.title
|
||||
from .models import Gallery, Tag
|
||||
|
||||
|
||||
class UploadForm(forms.Form):
|
||||
|
|
@ -29,7 +18,7 @@ class UploadForm(forms.Form):
|
|||
'class': 'mb-3',
|
||||
}),
|
||||
)
|
||||
gallery = GalleryChoiceField(
|
||||
gallery = forms.ModelChoiceField(
|
||||
Gallery.objects.all(),
|
||||
label=_('Gallery'),
|
||||
required=False,
|
||||
|
|
@ -100,12 +89,12 @@ class UploadForm(forms.Form):
|
|||
if not gallery:
|
||||
# Create new gallery
|
||||
title = self.cleaned_data.get('new_gallery_title')
|
||||
gallery = Gallery.objects.create(title=title, slug=slugify(title))
|
||||
ext = GalleryExtended.objects.create(
|
||||
gallery=gallery,
|
||||
gallery = Gallery.objects.create(
|
||||
title=title,
|
||||
slug=slugify(title),
|
||||
date_start=self.cleaned_data['new_gallery_date_start'],
|
||||
date_end=self.cleaned_data['new_gallery_date_end'],
|
||||
)
|
||||
for tag in self.cleaned_data['new_gallery_tags']:
|
||||
ext.tags.add(tag)
|
||||
gallery.tags.add(tag)
|
||||
return gallery
|
||||
|
|
@ -11,7 +11,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Photologue\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-30 07:09+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 09:55+0000\n"
|
||||
"PO-Revision-Date: 2017-12-03 14:47+0000\n"
|
||||
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
|
||||
"Language-Team: German (http://www.transifex.com/richardbarran/django-"
|
||||
|
|
@ -22,75 +22,79 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: photologue/models.py:86
|
||||
#: photologue/admin.py:30 photologue/models.py:499
|
||||
msgid "owner"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:84
|
||||
msgid "Very Low"
|
||||
msgstr "Sehr niedrig"
|
||||
|
||||
#: photologue/models.py:87
|
||||
#: photologue/models.py:85
|
||||
msgid "Low"
|
||||
msgstr "Niedrig"
|
||||
|
||||
#: photologue/models.py:88
|
||||
#: photologue/models.py:86
|
||||
msgid "Medium-Low"
|
||||
msgstr "Mittel-niedrig"
|
||||
|
||||
#: photologue/models.py:89
|
||||
#: photologue/models.py:87
|
||||
msgid "Medium"
|
||||
msgstr "Mittel"
|
||||
|
||||
#: photologue/models.py:90
|
||||
#: photologue/models.py:88
|
||||
msgid "Medium-High"
|
||||
msgstr "Mittel-hoch"
|
||||
|
||||
#: photologue/models.py:91
|
||||
#: photologue/models.py:89
|
||||
msgid "High"
|
||||
msgstr "Hoch"
|
||||
|
||||
#: photologue/models.py:92
|
||||
#: photologue/models.py:90
|
||||
msgid "Very High"
|
||||
msgstr "Sehr hoch"
|
||||
|
||||
#: photologue/models.py:97
|
||||
#: photologue/models.py:95
|
||||
msgid "Top"
|
||||
msgstr "Oben"
|
||||
|
||||
#: photologue/models.py:98
|
||||
#: photologue/models.py:96
|
||||
msgid "Right"
|
||||
msgstr "Rechts"
|
||||
|
||||
#: photologue/models.py:99
|
||||
#: photologue/models.py:97
|
||||
msgid "Bottom"
|
||||
msgstr "Unten"
|
||||
|
||||
#: photologue/models.py:100
|
||||
#: photologue/models.py:98
|
||||
msgid "Left"
|
||||
msgstr "Links"
|
||||
|
||||
#: photologue/models.py:101
|
||||
#: photologue/models.py:99
|
||||
msgid "Center (Default)"
|
||||
msgstr "Mitte (Standard)"
|
||||
|
||||
#: photologue/models.py:105
|
||||
#: photologue/models.py:103
|
||||
msgid "Flip left to right"
|
||||
msgstr "Horizontal spiegeln"
|
||||
|
||||
#: photologue/models.py:106
|
||||
#: photologue/models.py:104
|
||||
msgid "Flip top to bottom"
|
||||
msgstr "Vertikal spiegeln"
|
||||
|
||||
#: photologue/models.py:107
|
||||
#: photologue/models.py:105
|
||||
msgid "Rotate 90 degrees counter-clockwise"
|
||||
msgstr "Um 90° nach links drehen"
|
||||
|
||||
#: photologue/models.py:108
|
||||
#: photologue/models.py:106
|
||||
msgid "Rotate 90 degrees clockwise"
|
||||
msgstr "Um 90° nach rechts drehen"
|
||||
|
||||
#: photologue/models.py:109
|
||||
#: photologue/models.py:107
|
||||
msgid "Rotate 180 degrees"
|
||||
msgstr "Um 180° drehen"
|
||||
|
||||
#: photologue/models.py:119
|
||||
#: photologue/models.py:117
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
|
||||
|
|
@ -101,106 +105,122 @@ msgstr ""
|
|||
"\". Bildfilter werden nach der Reihe angewendet. Folgende Filter sind "
|
||||
"verfügbar: %s."
|
||||
|
||||
#: photologue/models.py:141
|
||||
#: photologue/models.py:139
|
||||
msgid "date published"
|
||||
msgstr "Veröffentlichungsdatum"
|
||||
|
||||
#: photologue/models.py:143 photologue/models.py:474
|
||||
#: photologue/models.py:141 photologue/models.py:485
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: photologue/models.py:146
|
||||
#: photologue/models.py:144
|
||||
msgid "title slug"
|
||||
msgstr "Kurztitel"
|
||||
|
||||
#: photologue/models.py:149 photologue/models.py:480
|
||||
#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
|
||||
msgid "A \"slug\" is a unique URL-friendly title for an object."
|
||||
msgstr ""
|
||||
"Ein Kurztitel (\"slug\") ist ein eindeutiger, URL-geeigneter Titel für ein "
|
||||
"Objekt."
|
||||
|
||||
#: photologue/models.py:150
|
||||
msgid "start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:155
|
||||
msgid "end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:157
|
||||
msgid "description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: photologue/models.py:152 photologue/models.py:485
|
||||
#: photologue/models.py:162 photologue/models.py:703
|
||||
msgid "tags"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:165 photologue/models.py:506
|
||||
msgid "is public"
|
||||
msgstr "ist öffentlich"
|
||||
|
||||
#: photologue/models.py:154
|
||||
#: photologue/models.py:167
|
||||
msgid "Public galleries will be displayed in the default views."
|
||||
msgstr "Öffentliche Galerien werden in den Standard-Views angezeigt."
|
||||
|
||||
#: photologue/models.py:158 photologue/models.py:495
|
||||
#: photologue/models.py:171 photologue/models.py:514
|
||||
msgid "photos"
|
||||
msgstr "Fotos"
|
||||
|
||||
#: photologue/models.py:166
|
||||
#: photologue/models.py:177
|
||||
msgid "gallery"
|
||||
msgstr "Galerie"
|
||||
|
||||
#: photologue/models.py:167
|
||||
#: photologue/models.py:178
|
||||
msgid "galleries"
|
||||
msgstr "Galerien"
|
||||
|
||||
#: photologue/models.py:202
|
||||
#: photologue/models.py:213
|
||||
msgid "count"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: photologue/models.py:210
|
||||
#: photologue/models.py:221
|
||||
msgid "image"
|
||||
msgstr "Bild"
|
||||
|
||||
#: photologue/models.py:213
|
||||
#: photologue/models.py:224
|
||||
msgid "date taken"
|
||||
msgstr "Aufnahmedatum"
|
||||
|
||||
#: photologue/models.py:216
|
||||
#: photologue/models.py:227
|
||||
msgid "Date image was taken; is obtained from the image EXIF data."
|
||||
msgstr ""
|
||||
"Datum, an dem das Foto geschossen wurde; ausgelesen aus den EXIF-Daten."
|
||||
|
||||
#: photologue/models.py:217
|
||||
#: photologue/models.py:228
|
||||
msgid "view count"
|
||||
msgstr "Anzahl an Aufrufen"
|
||||
|
||||
#: photologue/models.py:220
|
||||
#: photologue/models.py:231
|
||||
msgid "crop from"
|
||||
msgstr "Beschneiden von"
|
||||
|
||||
#: photologue/models.py:243
|
||||
#: photologue/models.py:254
|
||||
msgid "An \"admin_thumbnail\" photo size has not been defined."
|
||||
msgstr "Es ist keine Fotogröße \"admin_thumbnail\" definiert."
|
||||
|
||||
#: photologue/models.py:250
|
||||
#: photologue/models.py:261
|
||||
msgid "Thumbnail"
|
||||
msgstr "Vorschaubild"
|
||||
|
||||
#: photologue/models.py:477
|
||||
#: photologue/models.py:488 photologue/models.py:696
|
||||
msgid "slug"
|
||||
msgstr "Kurztitel"
|
||||
|
||||
#: photologue/models.py:481
|
||||
#: photologue/models.py:492
|
||||
msgid "caption"
|
||||
msgstr "Bildunterschrift"
|
||||
|
||||
#: photologue/models.py:483
|
||||
#: photologue/models.py:494
|
||||
msgid "date added"
|
||||
msgstr "Datum des Eintrags"
|
||||
|
||||
#: photologue/models.py:487
|
||||
#: photologue/models.py:504
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:508
|
||||
msgid "Public photographs will be displayed in the default views."
|
||||
msgstr "Öffentliche Fotos werden in den Standard-Views angezeigt."
|
||||
|
||||
#: photologue/models.py:494
|
||||
#: photologue/models.py:513
|
||||
msgid "photo"
|
||||
msgstr "Foto"
|
||||
|
||||
#: photologue/models.py:556
|
||||
#: photologue/models.py:575 photologue/models.py:691
|
||||
msgid "name"
|
||||
msgstr "Name"
|
||||
|
||||
#: photologue/models.py:560
|
||||
#: photologue/models.py:579
|
||||
msgid ""
|
||||
"Photo size name should contain only letters, numbers and underscores. "
|
||||
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
|
||||
|
|
@ -209,41 +229,41 @@ msgstr ""
|
|||
"enthalten. Beispiele: \"thumbnail\", \"display\", \"small\", "
|
||||
"\"main_page_widget\"."
|
||||
|
||||
#: photologue/models.py:567
|
||||
#: photologue/models.py:586
|
||||
msgid "width"
|
||||
msgstr "Breite"
|
||||
|
||||
#: photologue/models.py:570
|
||||
#: photologue/models.py:589
|
||||
msgid ""
|
||||
"If width is set to \"0\" the image will be scaled to the supplied height."
|
||||
msgstr ""
|
||||
"Wenn die Breite auf \"0\" gesetzt ist, wird das Bild proportional auf die "
|
||||
"angebene Höhe skaliert."
|
||||
|
||||
#: photologue/models.py:571
|
||||
#: photologue/models.py:590
|
||||
msgid "height"
|
||||
msgstr "Höhe"
|
||||
|
||||
#: photologue/models.py:574
|
||||
#: photologue/models.py:593
|
||||
msgid ""
|
||||
"If height is set to \"0\" the image will be scaled to the supplied width"
|
||||
msgstr ""
|
||||
"Wenn die Höhe auf \"0\" gesetzt ist, wird das Bild proportional auf die "
|
||||
"angebene Breite skaliert."
|
||||
|
||||
#: photologue/models.py:575
|
||||
#: photologue/models.py:594
|
||||
msgid "quality"
|
||||
msgstr "Qualität"
|
||||
|
||||
#: photologue/models.py:578
|
||||
#: photologue/models.py:597
|
||||
msgid "JPEG image quality."
|
||||
msgstr "JPEG-Bildqualität"
|
||||
|
||||
#: photologue/models.py:579
|
||||
#: photologue/models.py:598
|
||||
msgid "upscale images?"
|
||||
msgstr "Bilder hochskalieren?"
|
||||
|
||||
#: photologue/models.py:581
|
||||
#: photologue/models.py:600
|
||||
msgid ""
|
||||
"If selected the image will be scaled up if necessary to fit the supplied "
|
||||
"dimensions. Cropped sizes will be upscaled regardless of this setting."
|
||||
|
|
@ -252,32 +272,32 @@ msgstr ""
|
|||
"Beschnittene Größen werden unabhängig von dieser Einstellung bei Bedarf "
|
||||
"hochskaliert."
|
||||
|
||||
#: photologue/models.py:585
|
||||
#: photologue/models.py:604
|
||||
msgid "crop to fit?"
|
||||
msgstr "Zuschneiden?"
|
||||
|
||||
#: photologue/models.py:587
|
||||
#: photologue/models.py:606
|
||||
msgid ""
|
||||
"If selected the image will be scaled and cropped to fit the supplied "
|
||||
"dimensions."
|
||||
msgstr ""
|
||||
"Soll das Bild auf das angegebene Format skaliert und beschnitten werden?"
|
||||
|
||||
#: photologue/models.py:589
|
||||
#: photologue/models.py:608
|
||||
msgid "pre-cache?"
|
||||
msgstr "Vorausspeichern?"
|
||||
|
||||
#: photologue/models.py:591
|
||||
#: photologue/models.py:610
|
||||
msgid "If selected this photo size will be pre-cached as photos are added."
|
||||
msgstr ""
|
||||
"Soll diese Bildgröße im Voraus gespeichert (pre-cached) werden, wenn Fotos "
|
||||
"hinzugefügt werden?"
|
||||
|
||||
#: photologue/models.py:592
|
||||
#: photologue/models.py:611
|
||||
msgid "increment view count?"
|
||||
msgstr "Bildzähler?"
|
||||
|
||||
#: photologue/models.py:594
|
||||
#: photologue/models.py:613
|
||||
msgid ""
|
||||
"If selected the image's \"view_count\" will be incremented when this photo "
|
||||
"size is displayed."
|
||||
|
|
@ -285,32 +305,32 @@ msgstr ""
|
|||
"Soll der Ansichts-Zähler (view-count) hochgezählt werden, wenn ein Foto "
|
||||
"dieser Größe angezeigt wird?"
|
||||
|
||||
#: photologue/models.py:599
|
||||
#: photologue/models.py:618
|
||||
msgid "photo size"
|
||||
msgstr "Foto-Größe"
|
||||
|
||||
#: photologue/models.py:600
|
||||
#: photologue/models.py:619
|
||||
msgid "photo sizes"
|
||||
msgstr "Foto-Größen"
|
||||
|
||||
#: photologue/models.py:617
|
||||
#: photologue/models.py:636
|
||||
msgid "Can only crop photos if both width and height dimensions are set."
|
||||
msgstr ""
|
||||
"Fotos können nur zugeschnitten werden, wenn Breite und Höhe angegeben sind."
|
||||
|
||||
#: photologue_custom/admin.py:43 photologue_custom/models.py:51
|
||||
msgid "owner"
|
||||
#: photologue/models.py:702
|
||||
msgid "tag"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:34
|
||||
#: photologue_custom/forms.py:22
|
||||
msgid "Gallery"
|
||||
msgstr "Galerie"
|
||||
|
||||
#: photologue_custom/forms.py:36
|
||||
#: photologue_custom/forms.py:24
|
||||
msgid "-- Create a new gallery --"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:37
|
||||
#: photologue_custom/forms.py:25
|
||||
msgid ""
|
||||
"Select a gallery to add these images to. Leave this empty to create a new "
|
||||
"gallery from the supplied title."
|
||||
|
|
@ -318,59 +338,43 @@ msgstr ""
|
|||
"Wähle eine Galerie aus, zu der diese Bilder hinzugefügt werden sollen. Lasse "
|
||||
"dieses Feld leer, um eine neue Galerie mit dem angegeben Titel zu erzeugen."
|
||||
|
||||
#: photologue_custom/forms.py:41
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
#: photologue_custom/forms.py:29
|
||||
msgid "New gallery title"
|
||||
msgstr "Zeige alle Galerien."
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:46
|
||||
#: photologue_custom/forms.py:34
|
||||
msgid "New gallery event start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:51
|
||||
#: photologue_custom/forms.py:39
|
||||
msgid "New gallery event end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:57
|
||||
#, fuzzy
|
||||
#| msgid "gallery"
|
||||
#: photologue_custom/forms.py:45
|
||||
msgid "New gallery tags"
|
||||
msgstr "Galerie"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:59
|
||||
#: photologue_custom/forms.py:47
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:76
|
||||
#: photologue_custom/forms.py:64
|
||||
#: photologue_custom/templates/photologue/upload.html:6
|
||||
#: photologue_custom/templates/photologue/upload.html:73
|
||||
msgid "Upload"
|
||||
msgstr "Hochladen"
|
||||
|
||||
#: photologue_custom/forms.py:82
|
||||
#: photologue_custom/forms.py:70
|
||||
msgid "A gallery with that title already exists."
|
||||
msgstr "Es existiert bereits eine Gallerie mit diesem Titel."
|
||||
|
||||
#: photologue_custom/forms.py:91
|
||||
#: photologue_custom/forms.py:79
|
||||
msgid "Select an existing gallery, or enter a title for a new gallery."
|
||||
msgstr ""
|
||||
"Wähle eine existierende Galerie aus oder gib einen Titel für eine neue "
|
||||
"Galerie ein."
|
||||
|
||||
#: photologue_custom/models.py:23
|
||||
msgid "start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:28
|
||||
msgid "end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:56
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:7
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:12
|
||||
msgid "Latest photo galleries"
|
||||
|
|
@ -403,16 +407,12 @@ msgid "to"
|
|||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:57
|
||||
#, fuzzy
|
||||
#| msgid "All photos"
|
||||
msgid "All pictures"
|
||||
msgstr "Alle Fotos"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:78
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
msgid "Download all gallery"
|
||||
msgstr "Zeige alle Galerien."
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/photo_detail.html:13
|
||||
msgid "Published"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Photologue\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-30 07:09+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 09:55+0000\n"
|
||||
"PO-Revision-Date: 2017-12-03 14:46+0000\n"
|
||||
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
|
||||
"Language-Team: Spanish (Spain) (http://www.transifex.com/richardbarran/"
|
||||
|
|
@ -23,75 +23,79 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: photologue/models.py:86
|
||||
#: photologue/admin.py:30 photologue/models.py:499
|
||||
msgid "owner"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:84
|
||||
msgid "Very Low"
|
||||
msgstr "Muy bajo"
|
||||
|
||||
#: photologue/models.py:87
|
||||
#: photologue/models.py:85
|
||||
msgid "Low"
|
||||
msgstr "Bajo"
|
||||
|
||||
#: photologue/models.py:88
|
||||
#: photologue/models.py:86
|
||||
msgid "Medium-Low"
|
||||
msgstr "Medio-bajo"
|
||||
|
||||
#: photologue/models.py:89
|
||||
#: photologue/models.py:87
|
||||
msgid "Medium"
|
||||
msgstr "Medio"
|
||||
|
||||
#: photologue/models.py:90
|
||||
#: photologue/models.py:88
|
||||
msgid "Medium-High"
|
||||
msgstr "Medio-alto"
|
||||
|
||||
#: photologue/models.py:91
|
||||
#: photologue/models.py:89
|
||||
msgid "High"
|
||||
msgstr "Alto"
|
||||
|
||||
#: photologue/models.py:92
|
||||
#: photologue/models.py:90
|
||||
msgid "Very High"
|
||||
msgstr "Muy alto"
|
||||
|
||||
#: photologue/models.py:97
|
||||
#: photologue/models.py:95
|
||||
msgid "Top"
|
||||
msgstr "Arriba"
|
||||
|
||||
#: photologue/models.py:98
|
||||
#: photologue/models.py:96
|
||||
msgid "Right"
|
||||
msgstr "Derecha"
|
||||
|
||||
#: photologue/models.py:99
|
||||
#: photologue/models.py:97
|
||||
msgid "Bottom"
|
||||
msgstr "Abajo"
|
||||
|
||||
#: photologue/models.py:100
|
||||
#: photologue/models.py:98
|
||||
msgid "Left"
|
||||
msgstr "Izquierda"
|
||||
|
||||
#: photologue/models.py:101
|
||||
#: photologue/models.py:99
|
||||
msgid "Center (Default)"
|
||||
msgstr "Centro (por defecto)"
|
||||
|
||||
#: photologue/models.py:105
|
||||
#: photologue/models.py:103
|
||||
msgid "Flip left to right"
|
||||
msgstr "Voltear de izquerda a derecha"
|
||||
|
||||
#: photologue/models.py:106
|
||||
#: photologue/models.py:104
|
||||
msgid "Flip top to bottom"
|
||||
msgstr "Voltear de arriba a abajo"
|
||||
|
||||
#: photologue/models.py:107
|
||||
#: photologue/models.py:105
|
||||
msgid "Rotate 90 degrees counter-clockwise"
|
||||
msgstr "Rotar 90 grados en sentido horario"
|
||||
|
||||
#: photologue/models.py:108
|
||||
#: photologue/models.py:106
|
||||
msgid "Rotate 90 degrees clockwise"
|
||||
msgstr "Rotar 90 grados en sentido antihorario"
|
||||
|
||||
#: photologue/models.py:109
|
||||
#: photologue/models.py:107
|
||||
msgid "Rotate 180 degrees"
|
||||
msgstr "Rotar 180 grados"
|
||||
|
||||
#: photologue/models.py:119
|
||||
#: photologue/models.py:117
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
|
||||
|
|
@ -102,103 +106,119 @@ msgstr ""
|
|||
">FILTRO_DOS->FILTRO_TRES\". Los filtros de imagen se aplicarán en orden. Los "
|
||||
"siguientes filtros están disponibles: %s."
|
||||
|
||||
#: photologue/models.py:141
|
||||
#: photologue/models.py:139
|
||||
msgid "date published"
|
||||
msgstr "fecha de publicación"
|
||||
|
||||
#: photologue/models.py:143 photologue/models.py:474
|
||||
#: photologue/models.py:141 photologue/models.py:485
|
||||
msgid "title"
|
||||
msgstr "título"
|
||||
|
||||
#: photologue/models.py:146
|
||||
#: photologue/models.py:144
|
||||
msgid "title slug"
|
||||
msgstr "título slug"
|
||||
|
||||
#: photologue/models.py:149 photologue/models.py:480
|
||||
#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
|
||||
msgid "A \"slug\" is a unique URL-friendly title for an object."
|
||||
msgstr "Un \"slug\" es un único título URL-amigable para un objeto."
|
||||
|
||||
#: photologue/models.py:150
|
||||
msgid "start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:155
|
||||
msgid "end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:157
|
||||
msgid "description"
|
||||
msgstr "descripción"
|
||||
|
||||
#: photologue/models.py:152 photologue/models.py:485
|
||||
#: photologue/models.py:162 photologue/models.py:703
|
||||
msgid "tags"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:165 photologue/models.py:506
|
||||
msgid "is public"
|
||||
msgstr "es público"
|
||||
|
||||
#: photologue/models.py:154
|
||||
#: photologue/models.py:167
|
||||
msgid "Public galleries will be displayed in the default views."
|
||||
msgstr "Las galerías públicas serán mostradas en las vistas por defecto."
|
||||
|
||||
#: photologue/models.py:158 photologue/models.py:495
|
||||
#: photologue/models.py:171 photologue/models.py:514
|
||||
msgid "photos"
|
||||
msgstr "fotos"
|
||||
|
||||
#: photologue/models.py:166
|
||||
#: photologue/models.py:177
|
||||
msgid "gallery"
|
||||
msgstr "galería"
|
||||
|
||||
#: photologue/models.py:167
|
||||
#: photologue/models.py:178
|
||||
msgid "galleries"
|
||||
msgstr "galerías"
|
||||
|
||||
#: photologue/models.py:202
|
||||
#: photologue/models.py:213
|
||||
msgid "count"
|
||||
msgstr "contar"
|
||||
|
||||
#: photologue/models.py:210
|
||||
#: photologue/models.py:221
|
||||
msgid "image"
|
||||
msgstr "imagen"
|
||||
|
||||
#: photologue/models.py:213
|
||||
#: photologue/models.py:224
|
||||
msgid "date taken"
|
||||
msgstr "fecha en la que se tomó"
|
||||
|
||||
#: photologue/models.py:216
|
||||
#: photologue/models.py:227
|
||||
msgid "Date image was taken; is obtained from the image EXIF data."
|
||||
msgstr "La fecha de la imagen fue obtenida por información EXIF de la imagen."
|
||||
|
||||
#: photologue/models.py:217
|
||||
#: photologue/models.py:228
|
||||
msgid "view count"
|
||||
msgstr "Contador de visitas"
|
||||
|
||||
#: photologue/models.py:220
|
||||
#: photologue/models.py:231
|
||||
msgid "crop from"
|
||||
msgstr "Recortar desde"
|
||||
|
||||
#: photologue/models.py:243
|
||||
#: photologue/models.py:254
|
||||
msgid "An \"admin_thumbnail\" photo size has not been defined."
|
||||
msgstr "El tamaño de foto de \"miniatura de admin\" no ha sido definido."
|
||||
|
||||
#: photologue/models.py:250
|
||||
#: photologue/models.py:261
|
||||
msgid "Thumbnail"
|
||||
msgstr "Miniatura"
|
||||
|
||||
#: photologue/models.py:477
|
||||
#: photologue/models.py:488 photologue/models.py:696
|
||||
msgid "slug"
|
||||
msgstr "slug"
|
||||
|
||||
#: photologue/models.py:481
|
||||
#: photologue/models.py:492
|
||||
msgid "caption"
|
||||
msgstr "pie de foto"
|
||||
|
||||
#: photologue/models.py:483
|
||||
#: photologue/models.py:494
|
||||
msgid "date added"
|
||||
msgstr "fecha añadida"
|
||||
|
||||
#: photologue/models.py:487
|
||||
#: photologue/models.py:504
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
#: photologue/models.py:508
|
||||
msgid "Public photographs will be displayed in the default views."
|
||||
msgstr "Las fotos públicas serán mostradas en las vistas por defecto."
|
||||
|
||||
#: photologue/models.py:494
|
||||
#: photologue/models.py:513
|
||||
msgid "photo"
|
||||
msgstr "foto"
|
||||
|
||||
#: photologue/models.py:556
|
||||
#: photologue/models.py:575 photologue/models.py:691
|
||||
msgid "name"
|
||||
msgstr "nombre"
|
||||
|
||||
#: photologue/models.py:560
|
||||
#: photologue/models.py:579
|
||||
msgid ""
|
||||
"Photo size name should contain only letters, numbers and underscores. "
|
||||
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
|
||||
|
|
@ -206,41 +226,41 @@ msgstr ""
|
|||
"El nombre del tamaño solo puede contener letras, números y subrayados. Por "
|
||||
"ejemplo:\"miniaturas\", \"muestra\", \"muestra_principal\"."
|
||||
|
||||
#: photologue/models.py:567
|
||||
#: photologue/models.py:586
|
||||
msgid "width"
|
||||
msgstr "anchura"
|
||||
|
||||
#: photologue/models.py:570
|
||||
#: photologue/models.py:589
|
||||
msgid ""
|
||||
"If width is set to \"0\" the image will be scaled to the supplied height."
|
||||
msgstr ""
|
||||
"Si la anchura se establece a \"0\" la imagen será escalada hasta la altura "
|
||||
"proporcionada"
|
||||
|
||||
#: photologue/models.py:571
|
||||
#: photologue/models.py:590
|
||||
msgid "height"
|
||||
msgstr "altura"
|
||||
|
||||
#: photologue/models.py:574
|
||||
#: photologue/models.py:593
|
||||
msgid ""
|
||||
"If height is set to \"0\" the image will be scaled to the supplied width"
|
||||
msgstr ""
|
||||
"Si la altura se establece a \"0\" la imagen será escalada hasta la anchura "
|
||||
"proporcionada"
|
||||
|
||||
#: photologue/models.py:575
|
||||
#: photologue/models.py:594
|
||||
msgid "quality"
|
||||
msgstr "calidad"
|
||||
|
||||
#: photologue/models.py:578
|
||||
#: photologue/models.py:597
|
||||
msgid "JPEG image quality."
|
||||
msgstr "Calidad de imagen JPEG."
|
||||
|
||||
#: photologue/models.py:579
|
||||
#: photologue/models.py:598
|
||||
msgid "upscale images?"
|
||||
msgstr "¿Aumentar imágenes?"
|
||||
|
||||
#: photologue/models.py:581
|
||||
#: photologue/models.py:600
|
||||
msgid ""
|
||||
"If selected the image will be scaled up if necessary to fit the supplied "
|
||||
"dimensions. Cropped sizes will be upscaled regardless of this setting."
|
||||
|
|
@ -249,11 +269,11 @@ msgstr ""
|
|||
"las dimensiones proporcionadas. Los tamaños recortados serán aumentados de "
|
||||
"acuerdo a esta opción."
|
||||
|
||||
#: photologue/models.py:585
|
||||
#: photologue/models.py:604
|
||||
msgid "crop to fit?"
|
||||
msgstr "¿Recortar hasta ajustar?"
|
||||
|
||||
#: photologue/models.py:587
|
||||
#: photologue/models.py:606
|
||||
msgid ""
|
||||
"If selected the image will be scaled and cropped to fit the supplied "
|
||||
"dimensions."
|
||||
|
|
@ -261,21 +281,21 @@ msgstr ""
|
|||
"Si se selecciona la imagen será escalada y recortada para ajustarse a las "
|
||||
"dimensiones proporcionadas."
|
||||
|
||||
#: photologue/models.py:589
|
||||
#: photologue/models.py:608
|
||||
msgid "pre-cache?"
|
||||
msgstr "¿pre-cachear?"
|
||||
|
||||
#: photologue/models.py:591
|
||||
#: photologue/models.py:610
|
||||
msgid "If selected this photo size will be pre-cached as photos are added."
|
||||
msgstr ""
|
||||
"Si se selecciona, este tamaño de foto será pre-cacheado cuando se añadan "
|
||||
"nuevas fotos."
|
||||
|
||||
#: photologue/models.py:592
|
||||
#: photologue/models.py:611
|
||||
msgid "increment view count?"
|
||||
msgstr "¿incrementar contador de visualizaciones?"
|
||||
|
||||
#: photologue/models.py:594
|
||||
#: photologue/models.py:613
|
||||
msgid ""
|
||||
"If selected the image's \"view_count\" will be incremented when this photo "
|
||||
"size is displayed."
|
||||
|
|
@ -283,31 +303,31 @@ msgstr ""
|
|||
"Si se selecciona el \"contador de visualizaciones\" se incrementará cuando "
|
||||
"esta foto sea visualizada."
|
||||
|
||||
#: photologue/models.py:599
|
||||
#: photologue/models.py:618
|
||||
msgid "photo size"
|
||||
msgstr "tamaño de foto"
|
||||
|
||||
#: photologue/models.py:600
|
||||
#: photologue/models.py:619
|
||||
msgid "photo sizes"
|
||||
msgstr "tamaños de foto"
|
||||
|
||||
#: photologue/models.py:617
|
||||
#: photologue/models.py:636
|
||||
msgid "Can only crop photos if both width and height dimensions are set."
|
||||
msgstr "Solo puede recortar las fotos si ancho y alto están establecidos."
|
||||
|
||||
#: photologue_custom/admin.py:43 photologue_custom/models.py:51
|
||||
msgid "owner"
|
||||
#: photologue/models.py:702
|
||||
msgid "tag"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:34
|
||||
#: photologue_custom/forms.py:22
|
||||
msgid "Gallery"
|
||||
msgstr "Galería"
|
||||
|
||||
#: photologue_custom/forms.py:36
|
||||
#: photologue_custom/forms.py:24
|
||||
msgid "-- Create a new gallery --"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:37
|
||||
#: photologue_custom/forms.py:25
|
||||
msgid ""
|
||||
"Select a gallery to add these images to. Leave this empty to create a new "
|
||||
"gallery from the supplied title."
|
||||
|
|
@ -315,58 +335,42 @@ msgstr ""
|
|||
"Seleccione una galería para agregarle estas imágenes. Déjelo vacío para "
|
||||
"crear una nueva galería a partir de este título."
|
||||
|
||||
#: photologue_custom/forms.py:41
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
#: photologue_custom/forms.py:29
|
||||
msgid "New gallery title"
|
||||
msgstr "Ver todas las galerías"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:46
|
||||
#: photologue_custom/forms.py:34
|
||||
msgid "New gallery event start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:51
|
||||
#: photologue_custom/forms.py:39
|
||||
msgid "New gallery event end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:57
|
||||
#, fuzzy
|
||||
#| msgid "gallery"
|
||||
#: photologue_custom/forms.py:45
|
||||
msgid "New gallery tags"
|
||||
msgstr "galería"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:59
|
||||
#: photologue_custom/forms.py:47
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:76
|
||||
#: photologue_custom/forms.py:64
|
||||
#: photologue_custom/templates/photologue/upload.html:6
|
||||
#: photologue_custom/templates/photologue/upload.html:73
|
||||
msgid "Upload"
|
||||
msgstr "Subir"
|
||||
|
||||
#: photologue_custom/forms.py:82
|
||||
#: photologue_custom/forms.py:70
|
||||
msgid "A gallery with that title already exists."
|
||||
msgstr "Ya existe una galería con ese título."
|
||||
|
||||
#: photologue_custom/forms.py:91
|
||||
#: photologue_custom/forms.py:79
|
||||
msgid "Select an existing gallery, or enter a title for a new gallery."
|
||||
msgstr ""
|
||||
"Seleccione una galería existente o ingrese un nuevo nombre para la galería."
|
||||
|
||||
#: photologue_custom/models.py:23
|
||||
msgid "start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:28
|
||||
msgid "end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:56
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:7
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:12
|
||||
msgid "Latest photo galleries"
|
||||
|
|
@ -399,16 +403,12 @@ msgid "to"
|
|||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:57
|
||||
#, fuzzy
|
||||
#| msgid "All photos"
|
||||
msgid "All pictures"
|
||||
msgstr "Todas las fotos"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:78
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
msgid "Download all gallery"
|
||||
msgstr "Ver todas las galerías"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/photo_detail.html:13
|
||||
msgid "Published"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Photologue\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-30 07:09+0000\n"
|
||||
"POT-Creation-Date: 2022-01-30 10:06+0000\n"
|
||||
"PO-Revision-Date: 2017-12-03 14:47+0000\n"
|
||||
"Last-Translator: Richard Barran <richard@arbee-design.co.uk>\n"
|
||||
"Language-Team: French (http://www.transifex.com/richardbarran/django-"
|
||||
|
|
@ -21,75 +21,131 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: photologue/models.py:86
|
||||
#: photologue/admin.py:30 photologue/models.py:499
|
||||
msgid "owner"
|
||||
msgstr "propriétaire"
|
||||
|
||||
#: photologue/forms.py:23
|
||||
msgid "Gallery"
|
||||
msgstr "Galerie"
|
||||
|
||||
#: photologue/forms.py:25
|
||||
msgid "-- Create a new gallery --"
|
||||
msgstr "-- Créer une nouvelle galerie --"
|
||||
|
||||
#: photologue/forms.py:26
|
||||
msgid ""
|
||||
"Select a gallery to add these images to. Leave this empty to create a new "
|
||||
"gallery from the supplied title."
|
||||
msgstr ""
|
||||
"Sélectionner une galerie à laquelle ajouter ces images. Laisser ce champ "
|
||||
"vide pour créer une nouvelle galerie à partir du titre indiqué."
|
||||
|
||||
#: photologue/forms.py:30
|
||||
msgid "New gallery title"
|
||||
msgstr "Titre de la nouvelle galerie"
|
||||
|
||||
#: photologue/forms.py:35
|
||||
msgid "New gallery event start date"
|
||||
msgstr "Date de début de l'évènement de la nouvelle galerie"
|
||||
|
||||
#: photologue/forms.py:40
|
||||
msgid "New gallery event end date"
|
||||
msgstr "Date de fin de l'évènement de la nouvelle galerie"
|
||||
|
||||
#: photologue/forms.py:46
|
||||
msgid "New gallery tags"
|
||||
msgstr "Balises de la nouvelle galerie"
|
||||
|
||||
#: photologue/forms.py:48
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
|
||||
#: photologue/forms.py:65 photologue/templates/photologue/upload.html:6
|
||||
#: photologue/templates/photologue/upload.html:73
|
||||
msgid "Upload"
|
||||
msgstr "Télécharger"
|
||||
|
||||
#: photologue/forms.py:71
|
||||
msgid "A gallery with that title already exists."
|
||||
msgstr "Une galerie portant ce nom existe déjà."
|
||||
|
||||
#: photologue/forms.py:80
|
||||
msgid "Select an existing gallery, or enter a title for a new gallery."
|
||||
msgstr ""
|
||||
"Sélectionner une galerie existante ou entrer un titre pour une nouvelle "
|
||||
"galerie."
|
||||
|
||||
#: photologue/models.py:84
|
||||
msgid "Very Low"
|
||||
msgstr "Très Bas"
|
||||
|
||||
#: photologue/models.py:87
|
||||
#: photologue/models.py:85
|
||||
msgid "Low"
|
||||
msgstr "Bas"
|
||||
|
||||
#: photologue/models.py:88
|
||||
#: photologue/models.py:86
|
||||
msgid "Medium-Low"
|
||||
msgstr "Moyen-Bas"
|
||||
|
||||
#: photologue/models.py:89
|
||||
#: photologue/models.py:87
|
||||
msgid "Medium"
|
||||
msgstr "Moyen"
|
||||
|
||||
#: photologue/models.py:90
|
||||
#: photologue/models.py:88
|
||||
msgid "Medium-High"
|
||||
msgstr "Moyen-Haut"
|
||||
|
||||
#: photologue/models.py:91
|
||||
#: photologue/models.py:89
|
||||
msgid "High"
|
||||
msgstr "Haut"
|
||||
|
||||
#: photologue/models.py:92
|
||||
#: photologue/models.py:90
|
||||
msgid "Very High"
|
||||
msgstr "Très Haut"
|
||||
|
||||
#: photologue/models.py:97
|
||||
#: photologue/models.py:95
|
||||
msgid "Top"
|
||||
msgstr "Sommet"
|
||||
|
||||
#: photologue/models.py:98
|
||||
#: photologue/models.py:96
|
||||
msgid "Right"
|
||||
msgstr "Droite"
|
||||
|
||||
#: photologue/models.py:99
|
||||
#: photologue/models.py:97
|
||||
msgid "Bottom"
|
||||
msgstr "Bas"
|
||||
|
||||
#: photologue/models.py:100
|
||||
#: photologue/models.py:98
|
||||
msgid "Left"
|
||||
msgstr "Gauche"
|
||||
|
||||
#: photologue/models.py:101
|
||||
#: photologue/models.py:99
|
||||
msgid "Center (Default)"
|
||||
msgstr "Centré (par défaut)"
|
||||
|
||||
#: photologue/models.py:105
|
||||
#: photologue/models.py:103
|
||||
msgid "Flip left to right"
|
||||
msgstr "Inversion de gauche à droite"
|
||||
|
||||
#: photologue/models.py:106
|
||||
#: photologue/models.py:104
|
||||
msgid "Flip top to bottom"
|
||||
msgstr "Inversion de haut en bas"
|
||||
|
||||
#: photologue/models.py:107
|
||||
#: photologue/models.py:105
|
||||
msgid "Rotate 90 degrees counter-clockwise"
|
||||
msgstr "Rotation de 90 degrés dans le sens anti-horloger"
|
||||
|
||||
#: photologue/models.py:108
|
||||
#: photologue/models.py:106
|
||||
msgid "Rotate 90 degrees clockwise"
|
||||
msgstr "Rotation de 90 degrés dans le sens horloger"
|
||||
|
||||
#: photologue/models.py:109
|
||||
#: photologue/models.py:107
|
||||
msgid "Rotate 180 degrees"
|
||||
msgstr "Rotation de 180 degrés"
|
||||
|
||||
#: photologue/models.py:119
|
||||
#: photologue/models.py:117
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Chain multiple filters using the following pattern \"FILTER_ONE->FILTER_TWO-"
|
||||
|
|
@ -100,107 +156,123 @@ msgstr ""
|
|||
">FILTRE_DEUX->FILTRE_TROIS\". Les filtres d'image seront appliqués dans "
|
||||
"l'ordre. Les filtres suivants sont disponibles: %s."
|
||||
|
||||
#: photologue/models.py:141
|
||||
#: photologue/models.py:139
|
||||
msgid "date published"
|
||||
msgstr "date de publication"
|
||||
|
||||
#: photologue/models.py:143 photologue/models.py:474
|
||||
#: photologue/models.py:141 photologue/models.py:485
|
||||
msgid "title"
|
||||
msgstr "titre"
|
||||
|
||||
#: photologue/models.py:146
|
||||
#: photologue/models.py:144
|
||||
msgid "title slug"
|
||||
msgstr "version abrégée du titre"
|
||||
|
||||
#: photologue/models.py:149 photologue/models.py:480
|
||||
#: photologue/models.py:147 photologue/models.py:491 photologue/models.py:697
|
||||
msgid "A \"slug\" is a unique URL-friendly title for an object."
|
||||
msgstr ""
|
||||
"Un \"slug\" est un titre abrégé et unique, compatible avec les URL, pour un "
|
||||
"objet."
|
||||
|
||||
#: photologue/models.py:150
|
||||
msgid "start date"
|
||||
msgstr "date de début"
|
||||
|
||||
#: photologue/models.py:155
|
||||
msgid "end date"
|
||||
msgstr "date de fin"
|
||||
|
||||
#: photologue/models.py:157
|
||||
msgid "description"
|
||||
msgstr "description"
|
||||
|
||||
#: photologue/models.py:152 photologue/models.py:485
|
||||
#: photologue/models.py:162 photologue/models.py:703
|
||||
msgid "tags"
|
||||
msgstr "balises"
|
||||
|
||||
#: photologue/models.py:165 photologue/models.py:506
|
||||
msgid "is public"
|
||||
msgstr "est public"
|
||||
|
||||
#: photologue/models.py:154
|
||||
#: photologue/models.py:167
|
||||
msgid "Public galleries will be displayed in the default views."
|
||||
msgstr "Les galeries publiques seront affichée dans les vues par défaut."
|
||||
|
||||
#: photologue/models.py:158 photologue/models.py:495
|
||||
#: photologue/models.py:171 photologue/models.py:514
|
||||
msgid "photos"
|
||||
msgstr "photos"
|
||||
|
||||
#: photologue/models.py:166
|
||||
#: photologue/models.py:177
|
||||
msgid "gallery"
|
||||
msgstr "galerie"
|
||||
|
||||
#: photologue/models.py:167
|
||||
#: photologue/models.py:178
|
||||
msgid "galleries"
|
||||
msgstr "galleries"
|
||||
|
||||
#: photologue/models.py:202
|
||||
#: photologue/models.py:213
|
||||
msgid "count"
|
||||
msgstr "nombre"
|
||||
|
||||
#: photologue/models.py:210
|
||||
#: photologue/models.py:221
|
||||
msgid "image"
|
||||
msgstr "image"
|
||||
|
||||
#: photologue/models.py:213
|
||||
#: photologue/models.py:224
|
||||
msgid "date taken"
|
||||
msgstr "date de prise de vue"
|
||||
|
||||
#: photologue/models.py:216
|
||||
#: photologue/models.py:227
|
||||
msgid "Date image was taken; is obtained from the image EXIF data."
|
||||
msgstr ""
|
||||
"La date à laquelle l'image a été prise ; obtenue à partir des données EXIF "
|
||||
"de l'image."
|
||||
|
||||
#: photologue/models.py:217
|
||||
#: photologue/models.py:228
|
||||
msgid "view count"
|
||||
msgstr "nombre"
|
||||
|
||||
#: photologue/models.py:220
|
||||
#: photologue/models.py:231
|
||||
msgid "crop from"
|
||||
msgstr "découper à partir de"
|
||||
|
||||
#: photologue/models.py:243
|
||||
#: photologue/models.py:254
|
||||
msgid "An \"admin_thumbnail\" photo size has not been defined."
|
||||
msgstr "Une taille de photo \"admin_thumbnail\" n'a pas encore été définie."
|
||||
|
||||
#: photologue/models.py:250
|
||||
#: photologue/models.py:261
|
||||
msgid "Thumbnail"
|
||||
msgstr "Miniature"
|
||||
|
||||
#: photologue/models.py:477
|
||||
#: photologue/models.py:488 photologue/models.py:696
|
||||
msgid "slug"
|
||||
msgstr "libellé court"
|
||||
|
||||
#: photologue/models.py:481
|
||||
#: photologue/models.py:492
|
||||
msgid "caption"
|
||||
msgstr "légende"
|
||||
|
||||
#: photologue/models.py:483
|
||||
#: photologue/models.py:494
|
||||
msgid "date added"
|
||||
msgstr "date d'ajout"
|
||||
|
||||
#: photologue/models.py:487
|
||||
#: photologue/models.py:504
|
||||
msgid "license"
|
||||
msgstr "licence"
|
||||
|
||||
#: photologue/models.py:508
|
||||
msgid "Public photographs will be displayed in the default views."
|
||||
msgstr "Les photographies publique seront affichées dans les vues par défaut."
|
||||
|
||||
#: photologue/models.py:494
|
||||
#: photologue/models.py:513
|
||||
msgid "photo"
|
||||
msgstr "photo"
|
||||
|
||||
#: photologue/models.py:556
|
||||
#: photologue/models.py:575 photologue/models.py:691
|
||||
msgid "name"
|
||||
msgstr "nom"
|
||||
|
||||
#: photologue/models.py:560
|
||||
#: photologue/models.py:579
|
||||
msgid ""
|
||||
"Photo size name should contain only letters, numbers and underscores. "
|
||||
"Examples: \"thumbnail\", \"display\", \"small\", \"main_page_widget\"."
|
||||
|
|
@ -209,41 +281,41 @@ msgstr ""
|
|||
"chiffres et des caractères de soulignement. Exemples: \"miniature\", "
|
||||
"\"affichage\", \"petit\", \"widget_page_principale\"."
|
||||
|
||||
#: photologue/models.py:567
|
||||
#: photologue/models.py:586
|
||||
msgid "width"
|
||||
msgstr "largeur"
|
||||
|
||||
#: photologue/models.py:570
|
||||
#: photologue/models.py:589
|
||||
msgid ""
|
||||
"If width is set to \"0\" the image will be scaled to the supplied height."
|
||||
msgstr ""
|
||||
"Si la largeur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
|
||||
"la hauteur fournie."
|
||||
|
||||
#: photologue/models.py:571
|
||||
#: photologue/models.py:590
|
||||
msgid "height"
|
||||
msgstr "hauteur"
|
||||
|
||||
#: photologue/models.py:574
|
||||
#: photologue/models.py:593
|
||||
msgid ""
|
||||
"If height is set to \"0\" the image will be scaled to the supplied width"
|
||||
msgstr ""
|
||||
"Si la hauteur est réglée à \"0\" l l'image sera redimensionnée par rapport à "
|
||||
"la largeur fournie."
|
||||
|
||||
#: photologue/models.py:575
|
||||
#: photologue/models.py:594
|
||||
msgid "quality"
|
||||
msgstr "qualité"
|
||||
|
||||
#: photologue/models.py:578
|
||||
#: photologue/models.py:597
|
||||
msgid "JPEG image quality."
|
||||
msgstr "Qualité JPEG de l'image."
|
||||
|
||||
#: photologue/models.py:579
|
||||
#: photologue/models.py:598
|
||||
msgid "upscale images?"
|
||||
msgstr "agrandir les images ?"
|
||||
|
||||
#: photologue/models.py:581
|
||||
#: photologue/models.py:600
|
||||
msgid ""
|
||||
"If selected the image will be scaled up if necessary to fit the supplied "
|
||||
"dimensions. Cropped sizes will be upscaled regardless of this setting."
|
||||
|
|
@ -252,11 +324,11 @@ msgstr ""
|
|||
"dimensions fournies. Les dimensions ajustées seront agrandies sans prendre "
|
||||
"en compte ce paramètre."
|
||||
|
||||
#: photologue/models.py:585
|
||||
#: photologue/models.py:604
|
||||
msgid "crop to fit?"
|
||||
msgstr "découper pour adapter à la taille ?"
|
||||
|
||||
#: photologue/models.py:587
|
||||
#: photologue/models.py:606
|
||||
msgid ""
|
||||
"If selected the image will be scaled and cropped to fit the supplied "
|
||||
"dimensions."
|
||||
|
|
@ -264,21 +336,21 @@ msgstr ""
|
|||
"Si sélectionné l'image sera redimensionnée et recadrée pour coïncider avec "
|
||||
"les dimensions fournies."
|
||||
|
||||
#: photologue/models.py:589
|
||||
#: photologue/models.py:608
|
||||
msgid "pre-cache?"
|
||||
msgstr "mise en cache ?"
|
||||
|
||||
#: photologue/models.py:591
|
||||
#: photologue/models.py:610
|
||||
msgid "If selected this photo size will be pre-cached as photos are added."
|
||||
msgstr ""
|
||||
"Si sélectionné cette taille de photo sera mise en cache au moment au les "
|
||||
"photos sont ajoutées."
|
||||
|
||||
#: photologue/models.py:592
|
||||
#: photologue/models.py:611
|
||||
msgid "increment view count?"
|
||||
msgstr "incrémenter le nombre d'affichages ?"
|
||||
|
||||
#: photologue/models.py:594
|
||||
#: photologue/models.py:613
|
||||
msgid ""
|
||||
"If selected the image's \"view_count\" will be incremented when this photo "
|
||||
"size is displayed."
|
||||
|
|
@ -286,148 +358,75 @@ msgstr ""
|
|||
"Si sélectionné le \"view_count\" (nombre d'affichage) de l'image sera "
|
||||
"incrémenté quand cette taille de photo sera affichée."
|
||||
|
||||
#: photologue/models.py:599
|
||||
#: photologue/models.py:618
|
||||
msgid "photo size"
|
||||
msgstr "taille de la photo"
|
||||
|
||||
#: photologue/models.py:600
|
||||
#: photologue/models.py:619
|
||||
msgid "photo sizes"
|
||||
msgstr "tailles des photos"
|
||||
|
||||
#: photologue/models.py:617
|
||||
#: photologue/models.py:636
|
||||
msgid "Can only crop photos if both width and height dimensions are set."
|
||||
msgstr ""
|
||||
"La hauteur et la largeur doivent être toutes les deux définies pour "
|
||||
"retailler des photos."
|
||||
|
||||
#: photologue_custom/admin.py:43 photologue_custom/models.py:51
|
||||
msgid "owner"
|
||||
#: photologue/models.py:702
|
||||
msgid "tag"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:34
|
||||
msgid "Gallery"
|
||||
msgstr "Galerie"
|
||||
|
||||
#: photologue_custom/forms.py:36
|
||||
msgid "-- Create a new gallery --"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:37
|
||||
msgid ""
|
||||
"Select a gallery to add these images to. Leave this empty to create a new "
|
||||
"gallery from the supplied title."
|
||||
msgstr ""
|
||||
"Sélectionner une galerie à laquelle ajouter ces images. Laisser ce champ "
|
||||
"vide pour créer une nouvelle galerie à partir du titre indiqué."
|
||||
|
||||
#: photologue_custom/forms.py:41
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
msgid "New gallery title"
|
||||
msgstr "Afficher toutes les galeries"
|
||||
|
||||
#: photologue_custom/forms.py:46
|
||||
msgid "New gallery event start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:51
|
||||
msgid "New gallery event end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:57
|
||||
#, fuzzy
|
||||
#| msgid "gallery uploads"
|
||||
msgid "New gallery tags"
|
||||
msgstr "gallery uploads"
|
||||
|
||||
#: photologue_custom/forms.py:59
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/forms.py:76
|
||||
#: photologue_custom/templates/photologue/upload.html:6
|
||||
#: photologue_custom/templates/photologue/upload.html:73
|
||||
msgid "Upload"
|
||||
msgstr "Télécharger"
|
||||
|
||||
#: photologue_custom/forms.py:82
|
||||
msgid "A gallery with that title already exists."
|
||||
msgstr "Une galerie portant ce nom existe déjà."
|
||||
|
||||
#: photologue_custom/forms.py:91
|
||||
msgid "Select an existing gallery, or enter a title for a new gallery."
|
||||
msgstr ""
|
||||
"Sélectionner une galerie existante ou entrer un titre pour une nouvelle "
|
||||
"galerie."
|
||||
|
||||
#: photologue_custom/models.py:23
|
||||
msgid "start date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:28
|
||||
msgid "end date"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/models.py:56
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:7
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:12
|
||||
#: photologue/templates/photologue/gallery_archive.html:7
|
||||
#: photologue/templates/photologue/gallery_archive.html:12
|
||||
msgid "Latest photo galleries"
|
||||
msgstr "Dernières galeries de photos"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:18
|
||||
#: photologue/templates/photologue/gallery_archive.html:18
|
||||
msgid "Filter by year"
|
||||
msgstr "Filtrer par année"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive.html:35
|
||||
#: photologue/templates/photologue/gallery_archive.html:35
|
||||
msgid "No galleries were found"
|
||||
msgstr "Aucune galerie trouvée"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive_year.html:7
|
||||
#: photologue_custom/templates/photologue/gallery_archive_year.html:12
|
||||
#: photologue/templates/photologue/gallery_archive_year.html:7
|
||||
#: photologue/templates/photologue/gallery_archive_year.html:12
|
||||
#, python-format
|
||||
msgid "Galleries for %(show_year)s"
|
||||
msgstr "Galeries de %(show_year)s"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive_year.html:17
|
||||
#: photologue/templates/photologue/gallery_archive_year.html:17
|
||||
msgid "View all galleries"
|
||||
msgstr "Afficher toutes les galeries"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_archive_year.html:29
|
||||
#: photologue/templates/photologue/gallery_archive_year.html:29
|
||||
msgid "No galleries were found."
|
||||
msgstr "Aucune galerie trouvée."
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:41
|
||||
#: photologue/templates/photologue/gallery_detail.html:41
|
||||
msgid "to"
|
||||
msgstr ""
|
||||
msgstr "au"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:57
|
||||
#, fuzzy
|
||||
#| msgid "All photos"
|
||||
#: photologue/templates/photologue/gallery_detail.html:57
|
||||
msgid "All pictures"
|
||||
msgstr "Toutes les photos"
|
||||
|
||||
#: photologue_custom/templates/photologue/gallery_detail.html:78
|
||||
#, fuzzy
|
||||
#| msgid "View all galleries"
|
||||
#: photologue/templates/photologue/gallery_detail.html:78
|
||||
msgid "Download all gallery"
|
||||
msgstr "Afficher toutes les galeries"
|
||||
msgstr "Télécharger toute la galerie"
|
||||
|
||||
#: photologue_custom/templates/photologue/photo_detail.html:13
|
||||
#: photologue/templates/photologue/photo_detail.html:13
|
||||
msgid "Published"
|
||||
msgstr "Publiée le"
|
||||
|
||||
#: photologue_custom/templates/photologue/photo_detail.html:25
|
||||
#: photologue/templates/photologue/photo_detail.html:25
|
||||
msgid "This photo is found in the following galleries"
|
||||
msgstr "Cette photo se trouve dans les galeries suivantes"
|
||||
|
||||
#: photologue_custom/templates/photologue/upload.html:78
|
||||
#: photologue/templates/photologue/upload.html:78
|
||||
msgid "Drag and drop photos here"
|
||||
msgstr ""
|
||||
msgstr "Glissez et déposez les photos ici"
|
||||
|
||||
#: photologue_custom/templates/photologue/upload.html:82
|
||||
#: photologue/templates/photologue/upload.html:82
|
||||
msgid "Owner will be"
|
||||
msgstr ""
|
||||
msgstr "Le propriétaire sera"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import hashlib
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from photologue.models import Gallery
|
||||
|
||||
import hashlib
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'List all duplicate for chosen galleries'
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from pathlib import Path
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from photologue.models import Gallery
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
@ -16,7 +16,7 @@ class Command(BaseCommand):
|
|||
media_dir = Path(settings.MEDIA_ROOT)
|
||||
for gallery in Gallery.objects.all():
|
||||
# Create gallery directory
|
||||
gallery_year = str(gallery.extended.date_start.year)
|
||||
gallery_year = str(gallery.date_start.year)
|
||||
gallery_dir = Path('photos') / gallery_year / gallery.slug
|
||||
gallery_path = media_dir / gallery_dir
|
||||
if not gallery_path.exists():
|
||||
|
|
@ -1,158 +1,96 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 3.2.11 on 2022-01-30 10:14
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
import django.utils.timezone
|
||||
import sortedm2m.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import photologue.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('sites', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Gallery',
|
||||
name='PhotoSize',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
|
||||
('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published')),
|
||||
('title', models.CharField(max_length=50, verbose_name='title', unique=True)),
|
||||
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', verbose_name='title slug', unique=True)),
|
||||
('description', models.TextField(blank=True, verbose_name='description')),
|
||||
('is_public', models.BooleanField(help_text='Public galleries will be displayed in the default views.', verbose_name='is public', default=True)),
|
||||
('tags', photologue.models.TagField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
|
||||
('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Photo size name should contain only letters, numbers and underscores. Examples: "thumbnail", "display", "small", "main_page_widget".', max_length=40, unique=True, validators=[django.core.validators.RegexValidator(message='Use only plain lowercase letters (ASCII), numbers and underscores.', regex='^[a-z0-9_]+$')], verbose_name='name')),
|
||||
('width', models.PositiveIntegerField(default=0, help_text='If width is set to "0" the image will be scaled to the supplied height.', verbose_name='width')),
|
||||
('height', models.PositiveIntegerField(default=0, help_text='If height is set to "0" the image will be scaled to the supplied width', verbose_name='height')),
|
||||
('quality', models.PositiveIntegerField(choices=[(30, 'Very Low'), (40, 'Low'), (50, 'Medium-Low'), (60, 'Medium'), (70, 'Medium-High'), (80, 'High'), (90, 'Very High')], default=70, help_text='JPEG image quality.', verbose_name='quality')),
|
||||
('upscale', models.BooleanField(default=False, help_text='If selected the image will be scaled up if necessary to fit the supplied dimensions. Cropped sizes will be upscaled regardless of this setting.', verbose_name='upscale images?')),
|
||||
('crop', models.BooleanField(default=False, help_text='If selected the image will be scaled and cropped to fit the supplied dimensions.', verbose_name='crop to fit?')),
|
||||
('pre_cache', models.BooleanField(default=False, help_text='If selected this photo size will be pre-cached as photos are added.', verbose_name='pre-cache?')),
|
||||
('increment_count', models.BooleanField(default=False, help_text='If selected the image\'s "view_count" will be incremented when this photo size is displayed.', verbose_name='increment view count?')),
|
||||
],
|
||||
options={
|
||||
'get_latest_by': 'date_added',
|
||||
'verbose_name': 'gallery',
|
||||
'ordering': ['-date_added'],
|
||||
'verbose_name_plural': 'galleries',
|
||||
'verbose_name': 'photo size',
|
||||
'verbose_name_plural': 'photo sizes',
|
||||
'ordering': ['width', 'height'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GalleryUpload',
|
||||
name='Tag',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
|
||||
('zip_file', models.FileField(help_text='Select a .zip file of images to upload into a new Gallery.', verbose_name='images file (.zip)', upload_to='photologue/temp')),
|
||||
('title', models.CharField(max_length=50, help_text='All uploaded photos will be given a title made up of this title + a sequential number.', verbose_name='title')),
|
||||
('caption', models.TextField(help_text='Caption will be added to all photos.', blank=True, verbose_name='caption')),
|
||||
('description', models.TextField(help_text='A description of this Gallery.', blank=True, verbose_name='description')),
|
||||
('is_public', models.BooleanField(help_text='Uncheck this to make the uploaded gallery and included photographs private.', verbose_name='is public', default=True)),
|
||||
('tags', models.CharField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
|
||||
('gallery', models.ForeignKey(blank=True, verbose_name='gallery', null=True, help_text='Select a gallery to add these images to. Leave this empty to create a new gallery from the supplied title.', to='photologue.Gallery', on_delete=models.CASCADE)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=250, unique=True, verbose_name='name')),
|
||||
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'gallery upload',
|
||||
'verbose_name_plural': 'gallery uploads',
|
||||
'verbose_name': 'tag',
|
||||
'verbose_name_plural': 'tags',
|
||||
'ordering': ['name'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Photo',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('image', models.ImageField(upload_to=photologue.models.get_storage_path, verbose_name='image')),
|
||||
('date_taken', models.DateTimeField(verbose_name='date taken', blank=True, editable=False, null=True)),
|
||||
('view_count', models.PositiveIntegerField(verbose_name='view count', default=0, editable=False)),
|
||||
('crop_from', models.CharField(max_length=10, default='center', blank=True, verbose_name='crop from', choices=[('top', 'Top'), ('right', 'Right'), ('bottom', 'Bottom'), ('left', 'Left'), ('center', 'Center (Default)')])),
|
||||
('title', models.CharField(max_length=50, verbose_name='title', unique=True)),
|
||||
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', verbose_name='slug', unique=True)),
|
||||
('date_taken', models.DateTimeField(blank=True, help_text='Date image was taken; is obtained from the image EXIF data.', null=True, verbose_name='date taken')),
|
||||
('view_count', models.PositiveIntegerField(default=0, editable=False, verbose_name='view count')),
|
||||
('crop_from', models.CharField(blank=True, choices=[('top', 'Top'), ('right', 'Right'), ('bottom', 'Bottom'), ('left', 'Left'), ('center', 'Center (Default)')], default='center', max_length=10, verbose_name='crop from')),
|
||||
('title', models.CharField(max_length=250, unique=True, verbose_name='title')),
|
||||
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='slug')),
|
||||
('caption', models.TextField(blank=True, verbose_name='caption')),
|
||||
('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date added')),
|
||||
('is_public', models.BooleanField(help_text='Public photographs will be displayed in the default views.', verbose_name='is public', default=True)),
|
||||
('tags', photologue.models.TagField(max_length=255, help_text='Django-tagging was not found, tags will be treated as plain text.', blank=True, verbose_name='tags')),
|
||||
('sites', models.ManyToManyField(blank=True, verbose_name='sites', null=True, to='sites.Site')),
|
||||
('license', models.CharField(blank=True, max_length=255, verbose_name='license')),
|
||||
('is_public', models.BooleanField(default=True, help_text='Public photographs will be displayed in the default views.', verbose_name='is public')),
|
||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='owner')),
|
||||
],
|
||||
options={
|
||||
'get_latest_by': 'date_added',
|
||||
'verbose_name': 'photo',
|
||||
'ordering': ['-date_added'],
|
||||
'verbose_name_plural': 'photos',
|
||||
'ordering': ['-date_added'],
|
||||
'get_latest_by': 'date_added',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='gallery',
|
||||
name='photos',
|
||||
field=sortedm2m.fields.SortedManyToManyField(blank=True, verbose_name='photos', null=True, to='photologue.Photo'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PhotoEffect',
|
||||
name='Gallery',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
|
||||
('name', models.CharField(max_length=30, verbose_name='name', unique=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date_added', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date published')),
|
||||
('title', models.CharField(max_length=250, unique=True, verbose_name='title')),
|
||||
('slug', models.SlugField(help_text='A "slug" is a unique URL-friendly title for an object.', max_length=250, unique=True, verbose_name='title slug')),
|
||||
('date_start', models.DateField(default=django.utils.timezone.now, verbose_name='start date')),
|
||||
('date_end', models.DateField(blank=True, null=True, verbose_name='end date')),
|
||||
('description', models.TextField(blank=True, verbose_name='description')),
|
||||
('transpose_method', models.CharField(max_length=15, blank=True, verbose_name='rotate or flip', choices=[('FLIP_LEFT_RIGHT', 'Flip left to right'), ('FLIP_TOP_BOTTOM', 'Flip top to bottom'), ('ROTATE_90', 'Rotate 90 degrees counter-clockwise'), ('ROTATE_270', 'Rotate 90 degrees clockwise'), ('ROTATE_180', 'Rotate 180 degrees')])),
|
||||
('color', models.FloatField(help_text='A factor of 0.0 gives a black and white image, a factor of 1.0 gives the original image.', verbose_name='color', default=1.0)),
|
||||
('brightness', models.FloatField(help_text='A factor of 0.0 gives a black image, a factor of 1.0 gives the original image.', verbose_name='brightness', default=1.0)),
|
||||
('contrast', models.FloatField(help_text='A factor of 0.0 gives a solid grey image, a factor of 1.0 gives the original image.', verbose_name='contrast', default=1.0)),
|
||||
('sharpness', models.FloatField(help_text='A factor of 0.0 gives a blurred image, a factor of 1.0 gives the original image.', verbose_name='sharpness', default=1.0)),
|
||||
('filters', models.CharField(max_length=200, help_text='Chain multiple filters using the following pattern "FILTER_ONE->FILTER_TWO->FILTER_THREE". Image filters will be applied in order. The following filters are available: BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SHARPEN, SMOOTH, SMOOTH_MORE.', blank=True, verbose_name='filters')),
|
||||
('reflection_size', models.FloatField(help_text='The height of the reflection as a percentage of the orignal image. A factor of 0.0 adds no reflection, a factor of 1.0 adds a reflection equal to the height of the orignal image.', verbose_name='size', default=0)),
|
||||
('reflection_strength', models.FloatField(help_text='The initial opacity of the reflection gradient.', verbose_name='strength', default=0.6)),
|
||||
('background_color', models.CharField(max_length=7, help_text='The background color of the reflection gradient. Set this to match the background color of your page.', verbose_name='color', default='#FFFFFF')),
|
||||
('is_public', models.BooleanField(default=True, help_text='Public galleries will be displayed in the default views.', verbose_name='is public')),
|
||||
('photos', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Photo', verbose_name='photos')),
|
||||
('tags', models.ManyToManyField(blank=True, related_name='galleries', to='photologue.Tag', verbose_name='tags')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'photo effect',
|
||||
'verbose_name_plural': 'photo effects',
|
||||
'verbose_name': 'gallery',
|
||||
'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,
|
||||
),
|
||||
]
|
||||
|
|
|
|||
21
photologue/migrations/0002_auto_20220130_1020.py
Normal file
21
photologue/migrations/0002_auto_20220130_1020.py
Normal 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',
|
||||
),
|
||||
]
|
||||
|
|
@ -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),
|
||||
]
|
||||
|
|
@ -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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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,
|
||||
),
|
||||
]
|
||||
|
|
@ -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',
|
||||
),
|
||||
]
|
||||
|
|
@ -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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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',
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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',
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -136,8 +136,6 @@ class TagField(models.CharField):
|
|||
|
||||
|
||||
class Gallery(models.Model):
|
||||
date_added = models.DateTimeField(_('date published'),
|
||||
default=now)
|
||||
title = models.CharField(_('title'),
|
||||
max_length=250,
|
||||
unique=True)
|
||||
|
|
@ -145,8 +143,23 @@ class Gallery(models.Model):
|
|||
unique=True,
|
||||
max_length=250,
|
||||
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
|
||||
date_start = models.DateField(
|
||||
default=now,
|
||||
verbose_name=_("start date"),
|
||||
)
|
||||
date_end = models.DateField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("end date"),
|
||||
)
|
||||
description = models.TextField(_('description'),
|
||||
blank=True)
|
||||
tags = models.ManyToManyField(
|
||||
'photologue.Tag',
|
||||
related_name='galleries',
|
||||
verbose_name=_('tags'),
|
||||
blank=True,
|
||||
)
|
||||
is_public = models.BooleanField(_('is public'),
|
||||
default=True,
|
||||
help_text=_('Public galleries will be displayed '
|
||||
|
|
@ -157,13 +170,13 @@ class Gallery(models.Model):
|
|||
blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-date_added']
|
||||
get_latest_by = 'date_added'
|
||||
ordering = ['-date_start']
|
||||
get_latest_by = 'date_start'
|
||||
verbose_name = _('gallery')
|
||||
verbose_name_plural = _('galleries')
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
return f"{ self.title } ({self.date_start})"
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('photologue:pl-gallery', args=[self.slug])
|
||||
|
|
@ -478,6 +491,16 @@ class Photo(ImageModel):
|
|||
blank=True)
|
||||
date_added = models.DateTimeField(_('date added'),
|
||||
default=now)
|
||||
owner = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("owner"),
|
||||
)
|
||||
license = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
verbose_name=_("license"),
|
||||
)
|
||||
is_public = models.BooleanField(_('is public'),
|
||||
default=True,
|
||||
help_text=_('Public photographs will be displayed in the default views.'))
|
||||
|
|
@ -657,3 +680,25 @@ def init_size_method_map():
|
|||
{'base_name': '_get_size_url', 'size': size}
|
||||
size_method_map['get_%s_filename' % size] = \
|
||||
{'base_name': '_get_size_filename', 'size': size}
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(
|
||||
max_length=250,
|
||||
unique=True,
|
||||
verbose_name=_('name'),
|
||||
)
|
||||
slug = models.SlugField(
|
||||
unique=True,
|
||||
max_length=250,
|
||||
verbose_name=_('slug'),
|
||||
help_text=_('A "slug" is a unique URL-friendly title for an object.'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
verbose_name = _('tag')
|
||||
verbose_name_plural = _('tags')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
|
@ -38,10 +38,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% if gallery.extended.date_start %}<p class="text-muted small">{{ gallery.extended.date_start }}{% if gallery.extended.date_end and gallery.extended.date_end != gallery.extended.date_start %} {% trans "to" %} {{ gallery.extended.date_end }}{% endif %}</p>{% endif %}
|
||||
{% if gallery.extended.tags.all %}
|
||||
{% if gallery.date_start %}<p class="text-muted small">{{ gallery.date_start }}{% if gallery.date_end and gallery.date_end != gallery.date_start %} {% trans "to" %} {{ gallery.date_end }}{% endif %}</p>{% endif %}
|
||||
{% if gallery.tags.all %}
|
||||
<p class="text-muted">
|
||||
Tags : {% for tag in gallery.extended.tags.all %}
|
||||
Tags : {% for tag in gallery.tags.all %}
|
||||
<a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'photologue:tag-detail' tag.slug %}">{{ tag }}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
|
@ -70,7 +70,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
<div class="card-body row" id="lightgallery">
|
||||
{% for photo in photos %}
|
||||
<a class="col-6 col-md-3 mb-2 text-center" href="{{ photo.get_absolute_url }}" data-src="{{ photo.get_display_url }}" data-download-url="{{ photo.image.url }}" data-slide-name="{{ photo.id }}">
|
||||
<img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.extended.owner.get_full_name }}{% if photo.extended.license %} - {{ photo.extended.license }}{% endif %}">
|
||||
<img src="{{ photo.get_thumbnail_url }}" loading="lazy" class="img-thumbnail p-0" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %} - {{ photo.owner.get_full_name }}{% if photo.license %} - {{ photo.license }}{% endif %}">
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
{% endfor %}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ gallery.title }}</h5>
|
||||
{% if gallery.extended.date_start %}<p class="card-text text-muted small mb-0">{{ gallery.extended.date_start }}{% if gallery.extended.date_end and gallery.extended.date_end != gallery.extended.date_start %} - {{ gallery.extended.date_end }}{% endif %}</p>{% endif %}
|
||||
{% if gallery.date_start %}<p class="card-text text-muted small mb-0">{{ gallery.date_start }}{% if gallery.date_end and gallery.date_end != gallery.date_start %} - {{ gallery.date_end }}{% endif %}</p>{% endif %}
|
||||
<a href="{{ gallery.get_absolute_url }}" class="stretched-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
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'
|
||||
urlpatterns = [
|
||||
path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'),
|
||||
|
|
@ -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.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):
|
||||
queryset = Gallery.objects.filter(is_public=True)
|
||||
date_field = 'extended__date_start'
|
||||
date_field = 'date_start'
|
||||
uses_datetime_field = False # Fix related object access
|
||||
allow_empty = True
|
||||
|
||||
|
|
@ -22,3 +40,125 @@ class GalleryYearArchiveView(GalleryDateView, YearArchiveView):
|
|||
|
||||
class PhotoDetailView(LoginRequiredMixin, DetailView):
|
||||
queryset = Photo.objects.filter(is_public=True)
|
||||
|
||||
|
||||
class TagDetail(LoginRequiredMixin, DetailView):
|
||||
model = Tag
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Insert the single object into the context dict.
|
||||
"""
|
||||
current_tag = self.get_object().slug
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['galleries'] = Gallery.objects.filter(is_public=True) \
|
||||
.filter(tags__slug=current_tag) \
|
||||
.order_by('-date_start')
|
||||
return context
|
||||
|
||||
|
||||
class CustomGalleryDetailView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Custom gallery detail view to filter on photo owner
|
||||
"""
|
||||
queryset = Gallery.objects.filter(is_public=True)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
# Query with owner to reduce database lag
|
||||
context['photos'] = self.object.public().select_related('owner')
|
||||
|
||||
# List owners
|
||||
context['owners'] = []
|
||||
for photo in context['photos']:
|
||||
if photo.owner not in context['owners']:
|
||||
context['owners'].append(photo.owner)
|
||||
|
||||
# Filter on owner
|
||||
if 'owner' in self.kwargs:
|
||||
context['photos'] = context['photos'].filter(owner__id=self.kwargs['owner'])
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class GalleryDownload(LoginRequiredMixin, DetailView):
|
||||
model = Gallery
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Download a zip file of the gallery on GET request.
|
||||
"""
|
||||
# Create zip file with pictures
|
||||
gallery = self.get_object()
|
||||
byte_data = BytesIO()
|
||||
zip_file = zipfile.ZipFile(byte_data, "w")
|
||||
for photo in gallery.public():
|
||||
filename = os.path.basename(os.path.normpath(photo.image.path))
|
||||
zip_file.write(photo.image.path, filename)
|
||||
zip_file.close()
|
||||
|
||||
# Return zip file
|
||||
response = HttpResponse(byte_data.getvalue(), content_type='application/x-zip-compressed')
|
||||
response['Content-Disposition'] = f"attachment; filename={gallery.slug}.zip"
|
||||
return response
|
||||
|
||||
|
||||
class GalleryUpload(PermissionRequiredMixin, FormView):
|
||||
"""
|
||||
Form to upload new photos in a gallery
|
||||
"""
|
||||
form_class = UploadForm
|
||||
template_name = "photologue/upload.html"
|
||||
success_url = reverse_lazy("photologue:pl-gallery-upload")
|
||||
permission_required = 'photologue.add_gallery'
|
||||
|
||||
def form_valid(self, form):
|
||||
# Upload photos
|
||||
# We take files from the request to support multiple upload
|
||||
files = self.request.FILES.getlist('file_field')
|
||||
gallery = form.get_or_create_gallery()
|
||||
gallery_year = Path(str(gallery.date_start.year))
|
||||
gallery_dir = gallery_year / gallery.slug
|
||||
failed_upload = 0
|
||||
for photo_file in files:
|
||||
# Check that we have a valid image
|
||||
try:
|
||||
opened = Image.open(photo_file)
|
||||
opened.verify()
|
||||
except Exception:
|
||||
# Pillow doesn't recognize it as an image, skip it
|
||||
messages.error(self.request, f"{photo_file.name} was not recognized as an image")
|
||||
failed_upload += 1
|
||||
continue
|
||||
|
||||
title = f"{gallery.title} - {photo_file.name}"
|
||||
try:
|
||||
photo = Photo(
|
||||
title=title,
|
||||
slug=slugify(title),
|
||||
owner=self.request.user,
|
||||
)
|
||||
photo_name = str(gallery_dir / photo_file.name)
|
||||
photo.image.save(photo_name, photo_file)
|
||||
photo.save()
|
||||
photo.galleries.set([gallery])
|
||||
except IntegrityError:
|
||||
messages.error(self.request, f"{photo_file.name} was not uploaded. Maybe the photo was already uploaded.")
|
||||
failed_upload += 1
|
||||
|
||||
# Notify user then managers
|
||||
if not failed_upload:
|
||||
messages.success(self.request, "All photos has been successfully uploaded.")
|
||||
else:
|
||||
n_success = len(files) - failed_upload
|
||||
messages.warning(self.request, f"Only {n_success} photos were successfully uploaded !")
|
||||
|
||||
gallery_title = form.cleaned_data['gallery'] or form.cleaned_data.get('new_gallery_title', '')
|
||||
photos = ", ".join(f.name for f in files)
|
||||
mail_managers(
|
||||
subject="New photos upload",
|
||||
message=f"{self.request.user.username} has uploaded in `{gallery_title}`: {photos}",
|
||||
)
|
||||
|
||||
return super().form_valid(form)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PhotologueCustomConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.AutoField'
|
||||
name = 'photologue_custom'
|
||||
|
|
@ -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',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
django-allauth>=0.44
|
||||
django-crispy-forms~=1.7
|
||||
django-taggit>=1.5.0
|
||||
Django>=2.2.20
|
||||
ExifRead>=2.1.2
|
||||
git+https://gitlab.crans.org/bde/allauth-note-kfet.git
|
||||
|
|
|
|||
4
tox.ini
4
tox.ini
|
|
@ -12,7 +12,7 @@ deps =
|
|||
-r{toxinidir}/requirements.txt
|
||||
coverage
|
||||
commands =
|
||||
coverage run --omit='photo21/wsgi.py' --source=photo21,photologue,photologue_custom ./manage.py test
|
||||
coverage run --omit='photo21/wsgi.py' --source=photo21,photologue ./manage.py test
|
||||
coverage report -m
|
||||
|
||||
[testenv:linters]
|
||||
|
|
@ -26,7 +26,7 @@ deps =
|
|||
pep8-naming
|
||||
pyflakes
|
||||
commands =
|
||||
flake8 photo21 photologue photologue_custom
|
||||
flake8 photo21 photologue
|
||||
|
||||
[flake8]
|
||||
ignore = W503, I100, I101
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue