Merge branch 'front_fixes' into 'master'

Front fixes

See merge request bde/photo21!10
This commit is contained in:
erdnaxe 2021-10-13 20:55:51 +02:00
commit b806c4579b
17 changed files with 415 additions and 53 deletions

4
docs/backup-photos Normal file
View file

@ -0,0 +1,4 @@
#!/bin/sh
# Backup script to put in /etc/cron.montly/
rsync -avP /var/www/photos/photo21 /backup/
sudo -u postgres pg_dump photo21 > /backup/pg_dump_photo21.sql

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-10-11 18:38+0000\n" "POT-Creation-Date: 2021-10-13 13:52+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -30,19 +30,31 @@ msgstr ""
msgid "Must end with `@crans.org` or `@ens-paris-saclay.fr`." msgid "Must end with `@crans.org` or `@ens-paris-saclay.fr`."
msgstr "Doit finir par `@crans.org` ou `@ens-paris-saclay.fr`." msgstr "Doit finir par `@crans.org` ou `@ens-paris-saclay.fr`."
#: photo21/settings.py:143 #: photo21/hashers.py:42
msgid "algorithm"
msgstr ""
#: photo21/hashers.py:43
msgid "salt"
msgstr ""
#: photo21/hashers.py:44
msgid "hash"
msgstr ""
#: photo21/settings.py:153
msgid "German" msgid "German"
msgstr "" msgstr ""
#: photo21/settings.py:144 #: photo21/settings.py:154
msgid "English" msgid "English"
msgstr "" msgstr ""
#: photo21/settings.py:145 #: photo21/settings.py:155
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: photo21/settings.py:146 #: photo21/settings.py:156
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -90,6 +102,73 @@ msgid ""
"soon. You can now drink a beer." "soon. You can now drink a beer."
msgstr "" msgstr ""
#: photo21/templates/account/email.html:6
#: photo21/templates/account/email.html:14
#: photo21/templates/socialaccount/connections.html:14
msgid "E-mail Addresses"
msgstr ""
#: photo21/templates/account/email.html:9 photo21/templates/base.html:51
#: photo21/templates/socialaccount/connections.html:9
msgid "Account"
msgstr "Compte"
#: photo21/templates/account/email.html:17
#: photo21/templates/socialaccount/connections.html:17
msgid "Social connections"
msgstr "Connexions sociales"
#: photo21/templates/account/email.html:23
msgid "The following e-mail addresses are associated with your account:"
msgstr ""
#: photo21/templates/account/email.html:34
msgid "Verified"
msgstr ""
#: photo21/templates/account/email.html:36
msgid "Unverified"
msgstr ""
#: photo21/templates/account/email.html:38
msgid "Primary"
msgstr ""
#: photo21/templates/account/email.html:44
msgid "Make Primary"
msgstr ""
#: photo21/templates/account/email.html:45
msgid "Re-send Verification"
msgstr ""
#: photo21/templates/account/email.html:46
#: photo21/templates/socialaccount/connections.html:45
msgid "Remove"
msgstr ""
#: photo21/templates/account/email.html:51
msgid "Warning:"
msgstr ""
#: photo21/templates/account/email.html:51
msgid ""
"You currently do not have any e-mail address set up. You should really add "
"an e-mail address so you can receive notifications, reset your password, etc."
msgstr ""
#: photo21/templates/account/email.html:55
msgid "Add E-mail Address"
msgstr ""
#: photo21/templates/account/email.html:60
msgid "Add E-mail"
msgstr ""
#: photo21/templates/account/email.html:68
msgid "Do you really want to remove the selected e-mail address?"
msgstr ""
#: photo21/templates/account/login.html:6 #: photo21/templates/account/login.html:6
#: photo21/templates/account/login.html:11 #: photo21/templates/account/login.html:11
#: photo21/templates/account/login.html:39 #: photo21/templates/account/login.html:39
@ -154,27 +233,50 @@ msgstr "Galeries"
msgid "Manage" msgid "Manage"
msgstr "Gestion" msgstr "Gestion"
#: photo21/templates/base.html:50 #: photo21/templates/base.html:60
msgid "Account"
msgstr "Compte"
#: photo21/templates/base.html:59
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: photo21/templates/base.html:69 #: photo21/templates/base.html:70
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
#: photo21/templates/base.html:78 #: photo21/templates/base.html:79
msgid "Sign up" msgid "Sign up"
msgstr "Inscription" msgstr "Inscription"
#: photo21/templates/index.html:53 #: photo21/templates/index.html:50
msgid "Connected as" msgid "Connected as"
msgstr "Connecté en tant que" msgstr "Connecté en tant que"
#: photologue_custom/models.py:39 #: photo21/templates/socialaccount/connections.html:6
msgid "Account Connections"
msgstr ""
#: photo21/templates/socialaccount/connections.html:23
msgid ""
"You can sign in to your account using any of the following third party "
"accounts:"
msgstr ""
#: photo21/templates/socialaccount/connections.html:51
msgid ""
"You currently have no social network accounts connected to this account."
msgstr ""
#: photo21/templates/socialaccount/connections.html:54
msgid "Add a 3rd Party Account"
msgstr ""
#: photologue_custom/models.py:23
msgid "start date"
msgstr "date de début"
#: photologue_custom/models.py:28
msgid "end date"
msgstr "date de fin"
#: photologue_custom/models.py:51
msgid "owner" msgid "owner"
msgstr "propriétaire" msgstr "propriétaire"
@ -205,16 +307,22 @@ msgstr ""
msgid "No galleries were found." msgid "No galleries were found."
msgstr "" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:25 #: photologue_custom/templates/photologue/gallery_detail.html:35
#: photologue_custom/templates/photologue/includes/gallery_sample.html:9 msgid "to"
msgstr "au"
#: photologue_custom/templates/photologue/gallery_detail.html:49
msgid "All pictures"
msgstr "Toutes les photos"
#: photologue_custom/templates/photologue/gallery_detail.html:63
msgid "Download all gallery"
msgstr "Télécharger toute la galerie"
#: photologue_custom/templates/photologue/photo_detail.html:10 #: photologue_custom/templates/photologue/photo_detail.html:10
msgid "Published" msgid "Published"
msgstr "" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:41
msgid "Download all gallery"
msgstr "Télécharger toute la galerie"
#: photologue_custom/templates/photologue/photo_detail.html:22 #: photologue_custom/templates/photologue/photo_detail.html:22
msgid "This photo is found in the following galleries" msgid "This photo is found in the following galleries"
msgstr "" msgstr ""

View file

@ -156,6 +156,9 @@ LANGUAGES = [
('fr', _('French')), ('fr', _('French')),
] ]
# Allow more fields in URL to edit large galleries
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/ # https://docs.djangoproject.com/en/2.2/howto/static-files/

View file

@ -0,0 +1,79 @@
{% extends "account/base.html" %}
{% comment %}
SPDX-License-Identifier: GPL-2.0-or-later
{% endcomment %}
{% load i18n crispy_forms_tags %}
{% block head_title %}{% trans "E-mail Addresses" %}{% endblock %}
{% block content %}
<h1>{% trans "Account" %}</h1>
<div class="card">
<div class="card-header pb-0 border-bottom-0">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" href="{% url 'account_email' %}">{% trans "E-mail Addresses" %}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'socialaccount_connections' %}">{% trans "Social connections" %}</a>
</li>
</ul>
</div>
<div class="card-body">
{% if user.emailaddress_set.all %}
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
<form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset class="blockLabels">
{% for emailaddress in user.emailaddress_set.all %}
<div class="ctrlHolder">
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
{{ emailaddress.email }}
{% if emailaddress.verified %}
<span class="badge bg-success">{% trans "Verified" %}</span>
{% else %}
<span class="badge bg-danger">{% trans "Unverified" %}</span>
{% endif %}
{% if emailaddress.primary %}<span class="badge bg-secondary">{% trans "Primary" %}</span>{% endif %}
</label>
</div>
{% endfor %}
<div class="mt-2 mb-4">
<button class="btn btn-sm btn-secondary" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
<button class="btn btn-sm btn-secondary" type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button>
<button class="btn btn-sm btn-danger" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
</div>
</fieldset>
</form>
{% else %}
<p><strong>{% trans 'Warning:'%}</strong> {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}</p>
{% endif %}
{% if can_add_email %}
<h2>{% trans "Add E-mail Address" %}</h2>
<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}
{{ form|crispy }}
<button name="action_add" type="submit" class="btn btn-success mt-2">{% trans "Add E-mail" %}</button>
</form>
{% endif %}
</div>
</div>
<script type="text/javascript">
(function() {
var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}";
var actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function(e) {
if (! confirm(message)) {
e.preventDefault();
}
});
}
})();
</script>
{% endblock %}

View file

@ -43,7 +43,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<ul class="navbar-nav"> <ul class="navbar-nav">
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'account_email' %}"> {% url 'account_email' as url %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10z"/> <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10z"/>
</svg> </svg>

View file

@ -18,8 +18,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
photo est nécessaire avant toute republication sur un autre site.</b> photo est nécessaire avant toute republication sur un autre site.</b>
</p> </p>
Si vous souhaitez qu'une photo soit supprimée, signalez le nous : <p>
<a href="mailto:photos@crans.org?subject=[ABUS] Nouvelle requête" class="btn btn-dark btn-sm">Signaler un abus</a> Si vous souhaitez qu'une photo soit supprimée, signalez le nous :
<a href="mailto:photos@crans.org?subject=[ABUS] Nouvelle requête" class="btn btn-dark btn-sm">Signaler un abus</a>
</p>
<p>
Si vous souhaitez obtenir les droits photographes pour téléverser vos photos, signalez le nous :
<a href="mailto:photos@crans.org?subject=[Photographe] Demande de droits photographe" class="btn btn-dark btn-sm">Devenir photographe</a>
</p>
<h3>Dernières galeries</h3> <h3>Dernières galeries</h3>
<div class="row mb-2"> <div class="row mb-2">
@ -30,18 +36,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endfor %} {% endfor %}
</div> </div>
<h3>Nouveau venu ?</h3>
<p>
Si tu accèdes à ce service depuis le réseau du
<a href="https://www.crans.org/">Crans</a>, tu peux naviguer sur le
serveur comme tu le souhaites.
Par contre dès que tu essairas d'accéder au serveur photos depuis une
connexion Internet externe tu devras te créer un compte utilisateur.
</p>
<h3>Participez et faites vivre le serveur photos :</h3> <h3>Participez et faites vivre le serveur photos :</h3>
<ul> <ul>
<li>Devenez photographe en vous créant un compte ou en upgradant votre compte utilisateur</li> <li>Devenez photographe et téléversez vos photos</li>
<li>Reportez-nous toute anomalie dans la nouvelle interface ou toute difficulté rencontrée : fautes d'orthographes, bugs, mauvaise ergonomie…</li> <li>Reportez-nous toute anomalie dans la nouvelle interface ou toute difficulté rencontrée : fautes d'orthographes, bugs, mauvaise ergonomie…</li>
<li>Envoyez-nous vos suggestions à <a href="mailto:photos@crans.org">photos@crans.org</a>.</li> <li>Envoyez-nous vos suggestions à <a href="mailto:photos@crans.org">photos@crans.org</a>.</li>
<li>Si l'amélioration ou la gestion du serveur vous intéresse et pour en savoir plus, <a href="mailto:photos@crans.org">contactez-nous</a>.</li> <li>Si l'amélioration ou la gestion du serveur vous intéresse et pour en savoir plus, <a href="mailto:photos@crans.org">contactez-nous</a>.</li>
@ -66,4 +63,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
</select> </select>
<noscript><input type="submit"></noscript> <noscript><input type="submit"></noscript>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,63 @@
{% extends "socialaccount/base.html" %}
{% comment %}
SPDX-License-Identifier: GPL-2.0-or-later
{% endcomment %}
{% load i18n %}
{% block head_title %}{% trans "Account Connections" %}{% endblock %}
{% block content %}
<h1>{% trans "Account" %}</h1>
<div class="card">
<div class="card-header pb-0 border-bottom-0">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link" href="{% url 'account_email' %}">{% trans "E-mail Addresses" %}</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'socialaccount_connections' %}">{% trans "Social connections" %}</a>
</li>
</ul>
</div>
<div class="card-body">
{% if form.accounts %}
<p>{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}</p>
<form method="post" action="{% url 'socialaccount_connections' %}">
{% csrf_token %}
<fieldset>
{% if form.non_field_errors %}
<div id="errorMsg">{{ form.non_field_errors }}</div>
{% endif %}
{% for base_account in form.accounts %}
{% with base_account.get_provider_account as account %}
<div>
<label for="id_account_{{ base_account.id }}">
<input id="id_account_{{ base_account.id }}" type="radio" name="account" value="{{ base_account.id }}"/>
<span class="socialaccount_provider {{ base_account.provider }} {{ account.get_brand.id }}">{{account.get_brand.name}}</span>
{{ account }}
</label>
</div>
{% endwith %}
{% endfor %}
<div>
<button type="submit">{% trans 'Remove' %}</button>
</div>
</fieldset>
</form>
{% else %}
<p>{% trans 'You currently have no social network accounts connected to this account.' %}</p>
{% endif %}
<h2>{% trans 'Add a 3rd Party Account' %}</h2>
<ul class="socialaccount_providers">
{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
</ul>
{% include "socialaccount/snippets/login_extra.html" %}
</div>
</div>
{% endblock %}

View file

@ -17,6 +17,8 @@ class GalleryAdmin(GalleryAdminDefault):
model. model.
""" """
inlines = [GalleryExtendedInline, ] inlines = [GalleryExtendedInline, ]
autocomplete_fields = ['photos', ]
search_fields = ['title', ]
class PhotoExtendedInline(admin.StackedInline): class PhotoExtendedInline(admin.StackedInline):

View file

@ -0,0 +1,24 @@
# Generated by Django 2.2.24 on 2021-10-13 15:07
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('photologue_custom', '0002_auto_20211011_1956'),
]
operations = [
migrations.AlterField(
model_name='galleryextended',
name='gallery',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extended', to='photologue.Gallery'),
),
migrations.AlterField(
model_name='photoextended',
name='photo',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='extended', to='photologue.Photo'),
),
]

View file

@ -40,7 +40,7 @@ class PhotoExtended(models.Model):
# Extend Photologue Photo model. # Extend Photologue Photo model.
photo = models.OneToOneField( photo = models.OneToOneField(
Photo, Photo,
related_name='extented', related_name='extended',
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )

View file

@ -23,7 +23,7 @@
{% if latest %} {% if latest %}
<div class="row mb-2"> <div class="row mb-2">
{% for gallery in latest|slice:":32" %} {% for gallery in latest|slice:":32" %}
<div class="col-md-3 mb-2"> <div class="col-6 col-md-3 mb-2">
{% include "photologue/includes/gallery_sample.html" %} {% include "photologue/includes/gallery_sample.html" %}
</div> </div>
{% endfor %} {% endfor %}

View file

@ -17,7 +17,7 @@
{% if object_list %} {% if object_list %}
<div class="row mb-2"> <div class="row mb-2">
{% for gallery in object_list %} {% for gallery in object_list %}
<div class="col-md-3 mb-2"> <div class="col-6 col-md-3 mb-2">
{% include "photologue/includes/gallery_sample.html" %} {% include "photologue/includes/gallery_sample.html" %}
</div> </div>
{% endfor %} {% endfor %}

View file

@ -21,22 +21,57 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>{{ gallery.title }}</h1> <h1>
<p class="text-muted small">{% trans "Published" %} {{ gallery.date_added|date }}</p> {{ gallery.title }}
{% if request.user.is_staff and perms.photologue.change_gallery %}
<a href="{% url 'admin:photologue_gallery_change' gallery.id %}">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#555" class="bi bi-gear" viewBox="0 0 16 18">
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/>
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/>
</svg>
</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.extended.tags.all %}
<p class="text-muted"> <p class="text-muted">
Tags : {% for tag in gallery.extended.tags.all %} Tags : {% for tag in gallery.extended.tags.all %}
<a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'tag-detail' tag %}">{{ tag }}</a> <a class="badge rounded-pill bg-dark text-decoration-none" href="{% url 'tag-detail' tag.slug %}">{{ tag }}</a>
{% endfor %} {% endfor %}
</p> </p>
{% endif %} {% endif %}
{% if gallery.description %}<p>{{ gallery.description|safe }}</p>{% endif %} {% if gallery.description %}<p>{{ gallery.description|safe }}</p>{% endif %}
<div class="gallery-list mb-3" id="lightgallery">
{% for photo in gallery.public %} <div class="card">
<a href="{{ photo.get_absolute_url }}" data-src="{{ photo.get_display_url }}" data-download-url="{{ photo.image.url }}"> <div class="card-header pb-0 border-bottom-0">
<img src="{{ photo.get_thumbnail_url }}" class="img-thumbnail" alt="{{ photo.title }}{% if photo.date_taken %} - {{ photo.date_taken|date }} {{ photo.date_taken|time }}{% endif %}"> <ul class="nav nav-tabs">
</a> <li class="nav-item">
{% endfor %} {% url 'pl-gallery' gallery.slug as url %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
{% trans "All pictures" %}
</a>
</li>
{% for owner in owners %}
<li class="nav-item">
{% url 'pl-gallery-owner' slug=gallery.slug owner=owner.id as url %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}">
{% if owner.get_full_name %}{{ owner.get_full_name }}{% else %}{{ owner.username }}{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="card-body">
<div id="lightgallery">
{% for photo in photos %}
<a href="{{ photo.get_absolute_url }}" data-src="{{ photo.get_display_url }}" data-download-url="{{ photo.image.url }}">
<img src="{{ photo.get_thumbnail_url }}" 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 }}">
</a>
{% endfor %}
</div>
</div>
<div class="card-footer">
<a href="{% url 'gallery-download' gallery.slug %}" class="btn btn-secondary btn-sm">{% trans 'Download all gallery' %}</a>
</div>
</div> </div>
<a href="{% url 'gallery-download' gallery.slug %}" class="btn btn-secondary">{% trans 'Download all gallery' %}</a>
{% endblock %} {% endblock %}

View file

@ -6,8 +6,8 @@
{% endfor %} {% endfor %}
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ gallery.title }}</h5> <h5 class="card-title">{{ gallery.title }}</h5>
<p class="card-text text-muted small mb-0">{% trans "Published" %} {{ gallery.date_added|date }}</p> {% 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.description %}<p class="card-text small mb-0">{{ gallery.description|safe }}</p>{% endif %} {% if gallery.description %}<p class="card-text small mb-0">{{ gallery.description|safe }}</p>{% endif %}
<a href="{{ gallery.get_absolute_url }}" class="stretched-link"></a> <a href="{{ gallery.get_absolute_url }}" class="stretched-link"></a>
</div> </div>
</div> </div>

View file

@ -8,7 +8,7 @@
<div class="row mb-2"> <div class="row mb-2">
{% for gallery in galleries %} {% for gallery in galleries %}
<div class="col-md-3"> <div class="col-6 col-md-3 mb-2">
{% include "photologue/includes/gallery_sample.html" %} {% include "photologue/includes/gallery_sample.html" %}
</div> </div>
{% endfor %} {% endfor %}

View file

@ -1,8 +1,14 @@
from django.urls import path from django.urls import path, re_path
from .views import TagDetail, GalleryDownload from .views import (CustomGalleryArchiveIndexView,
CustomGalleryYearArchiveView, CustomGalleryDetailView,
GalleryDownload, TagDetail)
urlpatterns = [ urlpatterns = [
path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'), path('tag/<slug:slug>/', TagDetail.as_view(), name='tag-detail'),
path('gallery/', CustomGalleryArchiveIndexView.as_view(), name='pl-gallery-archive'),
re_path(r'^gallery/(?P<year>\d{4})/$', CustomGalleryYearArchiveView.as_view(), name='pl-gallery-archive-year'),
path('gallery/<slug:slug>/', CustomGalleryDetailView.as_view(), name='pl-gallery'),
path('gallery/<slug:slug>/<int:owner>/', CustomGalleryDetailView.as_view(), name='pl-gallery-owner'),
path('gallery/<slug:slug>/download/', GalleryDownload.as_view(), name='gallery-download'), path('gallery/<slug:slug>/download/', GalleryDownload.as_view(), name='gallery-download'),
] ]

View file

@ -9,6 +9,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse from django.http import HttpResponse
from django.views.generic import DetailView from django.views.generic import DetailView
from photologue.models import Gallery from photologue.models import Gallery
from photologue.views import GalleryArchiveIndexView, GalleryYearArchiveView
from taggit.models import Tag from taggit.models import Tag
@ -22,7 +23,46 @@ class TagDetail(LoginRequiredMixin, DetailView):
current_tag = self.get_object().slug current_tag = self.get_object().slug
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['galleries'] = Gallery.objects.on_site().is_public() \ context['galleries'] = Gallery.objects.on_site().is_public() \
.filter(extended__tags__name=current_tag) .filter(extended__tags__slug=current_tag)
return context
class CustomGalleryArchiveIndexView(GalleryArchiveIndexView):
"""
Override to use event date
"""
date_field = 'extended__date_start'
uses_datetime_field = False # Fix related object access
class CustomGalleryYearArchiveView(GalleryYearArchiveView):
"""
Override to use event date
"""
date_field = 'extended__date_start'
uses_datetime_field = False # Fix related object access
class CustomGalleryDetailView(DetailView):
"""
Custom gallery detail view to filter on photo owner
"""
queryset = Gallery.objects.on_site().is_public()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['photos'] = self.object.public()
# List owners
context['owners'] = []
for photo in context['photos']:
if 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 return context