v1.1
This commit is contained in:
parent
7a7979c91a
commit
5d1d3f8eff
29 changed files with 756 additions and 138 deletions
|
@ -34,7 +34,7 @@ urlpatterns += i18n_patterns(
|
|||
path("logout", auth_views.LogoutView.as_view(next_page="home"), name="logout"),
|
||||
path(
|
||||
"login",
|
||||
auth_views.LoginView.as_view(template_name="gestion/login.html"),
|
||||
gestion_views.MyLoginView.as_view(),
|
||||
name="login"
|
||||
),
|
||||
path('agenda/', include('calendrier.urls',namespace='calendrier')),
|
||||
|
|
|
@ -2,14 +2,13 @@ from django.db import models
|
|||
import uuid
|
||||
from gestion.models import ErnestoUser
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from colorful.fields import RGBColorField
|
||||
|
||||
ANSWERS = (
|
||||
('oui', _('Oui')),
|
||||
('non', _('Non')),
|
||||
('pe', _('Peut-etre')),
|
||||
)
|
||||
|
||||
|
||||
class Event(models.Model):
|
||||
nom = models.CharField(max_length=100)
|
||||
nomcourt = models.CharField(max_length=9, verbose_name=_("Nom court"))
|
||||
|
|
|
@ -13,10 +13,23 @@
|
|||
<!-- One -->
|
||||
<section class="wrapper style1" >
|
||||
<div class="inner">
|
||||
<p align="center"><div><span class="image right"><img src="{% static 'images/agenda.jpg' %}" alt="" />
|
||||
|
||||
<p align="center"><div><span class="image right">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/agenda.jpg' %}" alt="" />
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="https://evarin.fr/" target="_blank" class="icon fa-copyright"> Evarin</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span></div>
|
||||
<h2 align="center">{% trans "Découvrez les différents évènements de l'Ernestophone !" %}</h2>
|
||||
|
||||
|
@ -33,15 +46,7 @@
|
|||
<a href="mailto:fanfare@ens.fr" class="icon fa-envelope fa-5x "><span
|
||||
|
||||
class="label">Mail</span></a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a target="_blank" href="https://www.youtube.com/channel/UC_RahrdZLoBGpJQHwegV4tQ"
|
||||
|
||||
class="icon fa-youtube-play fa-5x"><span class="label">Youtube</span></a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,7 +103,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<h1> {% blocktrans count counter=events_passe|length %}Evénement passé :{% plural %}Evénements passés :{% endblocktrans %} </h1>
|
||||
|
||||
<div class="table-wrapper" color="white">
|
||||
|
@ -154,4 +159,5 @@
|
|||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
{% load i18n %}
|
||||
{% load translate %}
|
||||
{% load autotranslate %}
|
||||
|
||||
{% get_current_language as current_language %}
|
||||
|
||||
{% block extrahead %}
|
||||
|
@ -16,28 +17,28 @@
|
|||
|
||||
<div id="main">
|
||||
<section class="wrapper style1">
|
||||
|
||||
<div class="inner">
|
||||
<span class="image fit">
|
||||
<img src="{% static 'images/home.png' %}"/>
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="" class="icon fa-copyright">lor_tie</a>
|
||||
</div>
|
||||
</span>
|
||||
{% if actu %}
|
||||
<div class="box" style="background-color:rgba(228,82,47,0.5)">
|
||||
|
||||
<h4> {% blocktrans count counter=actu|length %}Actualité des chef·fe·s:{% plural %}Actualités des chef·fe·s:{% endblocktrans %}</h4>
|
||||
<ul>
|
||||
{% for a in actu %}
|
||||
<li>{% autotranslate current_language a.text a.text_en %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="instagram-feed" class="instagram_feed"> </div>
|
||||
|
||||
|
||||
|
||||
<h4>{% trans "Agenda :" %} </h4>
|
||||
<div class="row">
|
||||
<div class="6u 12u$(small)">
|
||||
|
||||
{% if actu %}
|
||||
<div class="box" style="background-color:rgba(228,82,47,0.5)">
|
||||
|
||||
<h4> {% blocktrans count counter=actu|length %}Actualité des chef·fe·s:{% plural %}Actualités des chef·fe·s:{% endblocktrans %}</h4>
|
||||
<ul>
|
||||
{% for a in actu %}
|
||||
<li>{% autotranslate current_language a.text a.text_en %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h4>{% trans "Agenda :" %} </h4>
|
||||
<table width="100%" style="background-color:#e4522f" >
|
||||
<tr>
|
||||
<td width="20%" align="left">
|
||||
|
@ -71,6 +72,7 @@
|
|||
</div>
|
||||
<div class="1u 12u$(small)"><p></p></div>
|
||||
<div class="5u 12u$(small)">
|
||||
|
||||
{% if events_a_venir_not_answered %}
|
||||
<div class="box" style="background-color:#e4522f">
|
||||
<h1> {% blocktrans count counter=events_a_venir_not_answered|length %}Doodle à remplir !!!! :{% plural %}Doodles à remplir !!!! :{% endblocktrans %} </h1>
|
||||
|
@ -181,11 +183,47 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<span class="image fit">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/home.png' %}"/>
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<div class="icon fa-copyright"> lor_tie</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'js/InstagramFeed.min.js' %}"></script>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
new InstagramFeed({
|
||||
'username': 'ernestophone',
|
||||
'container': "#instagram-feed" ,
|
||||
'display_profile': false,
|
||||
'display_biography': false,
|
||||
'display_gallery': true,
|
||||
'display_captions': true,
|
||||
'callback': null,
|
||||
'styling': true,
|
||||
'items': 8,
|
||||
'items_per_row': 8,
|
||||
'margin': 1
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -21,6 +21,7 @@ from calendrier.calend import EventCalendar
|
|||
from calendar import monthrange
|
||||
from calendrier.forms import ModifEventForm, EventForm, ParticipantsForm, ChangeDoodleName
|
||||
from calendrier.models import Event, Participants
|
||||
from gestion.models import Photo
|
||||
from partitions.decorators import chef_required
|
||||
|
||||
|
||||
|
@ -34,9 +35,10 @@ def liste(request):
|
|||
if request.user.is_authenticated :
|
||||
return redirect('calendrier:home')
|
||||
lToday = datetime.today()
|
||||
events_a_venir = Event.objects.filter(date__gte = lToday).exclude(calendrier__iexact = 'F')
|
||||
events_passe = Event.objects.filter(date__lt = lToday).filter(calendrier__iexact = 'H')
|
||||
return render(request, 'calendrier/agenda.html', {"events_a_venir": events_a_venir,"events_passe":events_passe})
|
||||
photo = Photo.objects.filter(cat='liste').order_by('?').first()
|
||||
events_a_venir = Event.objects.filter(date__gte = lToday).exclude(calendrier__iexact = 'F').order_by("-date")
|
||||
events_passe = Event.objects.filter(date__lt = lToday).filter(calendrier__iexact = 'H').order_by("-date")
|
||||
return render(request, 'calendrier/agenda.html', {"events_a_venir": events_a_venir,"events_passe":events_passe,"photo":photo})
|
||||
|
||||
|
||||
def named_month(pMonthNumber):
|
||||
|
@ -50,6 +52,7 @@ def home(request):
|
|||
@login_required
|
||||
def calendar(request, pYear, pMonth):
|
||||
actu = Actu.objects.all()
|
||||
photo = Photo.objects.filter(cat='home').order_by('?').first()
|
||||
lToday = datetime.now()
|
||||
lYear = int(pYear)
|
||||
lMonth = int(pMonth)
|
||||
|
@ -104,7 +107,7 @@ def calendar(request, pYear, pMonth):
|
|||
"events_a_venir_answered_pe": events_a_venir_answered_pe,
|
||||
"events_a_venir_not_answered": events_a_venir_not_answered,
|
||||
"actu" : actu,
|
||||
|
||||
"photo" : photo,
|
||||
})
|
||||
|
||||
@login_required
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.contrib import admin
|
|||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
|
||||
from gestion.models import ErnestoUser, VideoGallery
|
||||
from gestion.models import ErnestoUser, VideoGallery, Photo
|
||||
from actu.models import Actu
|
||||
|
||||
|
||||
|
@ -115,4 +115,5 @@ class UserProfileAdmin(UserAdmin):
|
|||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserProfileAdmin)
|
||||
admin.site.register(VideoGallery)
|
||||
admin.site.register(Photo)
|
||||
admin.site.register(Actu)
|
||||
|
|
31
gestion/migrations/0003_photo.py
Normal file
31
gestion/migrations/0003_photo.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 2.2.17 on 2021-03-31 13:50
|
||||
|
||||
import colorful.fields
|
||||
from django.db import migrations, models
|
||||
import gestion.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gestion', '0002_auto_20200908_2222'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Photo',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=127)),
|
||||
('cat', models.CharField(choices=[('home_join', 'Rejoignez nous'), ('home_contact', 'Nous Contacter'), ('home_rep', "Répertoire de l'acceuil"), ('login', 'Connexion'), ('change_membre', 'Modification du profil'), ('inscription_membre', 'Inscription'), ('home', 'Calendrier connecté'), ('liste', 'Agenda public'), ('part', 'Répertoire'), ('n', "N'apparait pas")], default='n', max_length=127)),
|
||||
('auteur', models.CharField(blank=True, max_length=127, null=True, verbose_name="Auteur de l'image")),
|
||||
('url', models.URLField(blank=True, null=True, verbose_name="Lien vers le site de l'auteur")),
|
||||
('color', colorful.fields.RGBColorField(default='#ffffff', verbose_name="Couleur du nom de l'auteur")),
|
||||
('image', models.ImageField(default=None, upload_to='deco', validators=[gestion.models.Photo.validate_image])),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Photo',
|
||||
'verbose_name_plural': 'Photos',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -4,6 +4,40 @@ from django.db import models
|
|||
from colorful.fields import RGBColorField
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
class Photo(models.Model):
|
||||
PHOTO_PLACEMENT = (
|
||||
('home_join', _("Rejoignez nous")),
|
||||
('home_contact', _("Nous Contacter")),
|
||||
('home_rep', _("Répertoire de l'acceuil")),
|
||||
('login', _('Connexion')),
|
||||
('change_membre', _('Modification du profil')),
|
||||
('inscription_membre', _('Inscription')),
|
||||
('home', _('Calendrier connecté')),
|
||||
('liste', _('Agenda public')),
|
||||
('part', _('Répertoire')),
|
||||
('n',_("N'apparait pas"))
|
||||
)
|
||||
|
||||
def validate_image(fieldfile_obj):
|
||||
filesize = fieldfile_obj.file.size
|
||||
megabyte_limit = 1.0
|
||||
if filesize > megabyte_limit*1024*1024:
|
||||
raise ValidationError("Max file size is %sMB" % str(megabyte_limit))
|
||||
|
||||
name = models.CharField(max_length=127)
|
||||
cat = models.CharField(max_length = 127,choices = PHOTO_PLACEMENT,default='n')
|
||||
auteur = models.CharField(max_length=127,verbose_name=_("Auteur de l'image"),null=True,blank=True)
|
||||
url = models.URLField(verbose_name=_("Lien vers le site de l'auteur"),null=True,blank=True)
|
||||
color = RGBColorField(_("Couleur du nom de l'auteur"),default='#ffffff')
|
||||
image = models.ImageField(upload_to='deco',default=None,validators=[validate_image])
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Photo")
|
||||
verbose_name_plural = _("Photos")
|
||||
|
||||
|
||||
class ErnestoUser(models.Model):
|
||||
|
|
36
gestion/static/css/bootstrap.css
vendored
36
gestion/static/css/bootstrap.css
vendored
|
@ -545,7 +545,7 @@ pre code {
|
|||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.container {
|
||||
.container_carousel {
|
||||
width: 100%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
|
@ -554,25 +554,25 @@ pre code {
|
|||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.container {
|
||||
.container_carousel {
|
||||
max-width: 540px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
.container_carousel {
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.container {
|
||||
.container_carousel {
|
||||
max-width: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.container {
|
||||
.container_carousel {
|
||||
max-width: 1140px;
|
||||
}
|
||||
}
|
||||
|
@ -4157,7 +4157,7 @@ input[type="button"].btn-block {
|
|||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.navbar > .container,
|
||||
.navbar > .container_carousel,
|
||||
.navbar > .container-fluid {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
|
@ -4242,7 +4242,7 @@ input[type="button"].btn-block {
|
|||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.navbar-expand-sm > .container,
|
||||
.navbar-expand-sm > .container_carousel,
|
||||
.navbar-expand-sm > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -4267,7 +4267,7 @@ input[type="button"].btn-block {
|
|||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.navbar-expand-sm > .container,
|
||||
.navbar-expand-sm > .container_carousel,
|
||||
.navbar-expand-sm > .container-fluid {
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -4284,7 +4284,7 @@ input[type="button"].btn-block {
|
|||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.navbar-expand-md > .container,
|
||||
.navbar-expand-md > .container_carousel,
|
||||
.navbar-expand-md > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -4309,7 +4309,7 @@ input[type="button"].btn-block {
|
|||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.navbar-expand-md > .container,
|
||||
.navbar-expand-md > .container_carousel,
|
||||
.navbar-expand-md > .container-fluid {
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -4326,7 +4326,7 @@ input[type="button"].btn-block {
|
|||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.navbar-expand-lg > .container,
|
||||
.navbar-expand-lg > .container_carousel,
|
||||
.navbar-expand-lg > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -4351,7 +4351,7 @@ input[type="button"].btn-block {
|
|||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.navbar-expand-lg > .container,
|
||||
.navbar-expand-lg > .container_carousel,
|
||||
.navbar-expand-lg > .container-fluid {
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -4368,7 +4368,7 @@ input[type="button"].btn-block {
|
|||
}
|
||||
|
||||
@media (max-width: 1199.98px) {
|
||||
.navbar-expand-xl > .container,
|
||||
.navbar-expand-xl > .container_carousel,
|
||||
.navbar-expand-xl > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -4393,7 +4393,7 @@ input[type="button"].btn-block {
|
|||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
.navbar-expand-xl > .container,
|
||||
.navbar-expand-xl > .container_carousel,
|
||||
.navbar-expand-xl > .container-fluid {
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -4416,7 +4416,7 @@ input[type="button"].btn-block {
|
|||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.navbar-expand > .container,
|
||||
.navbar-expand > .container_carousel,
|
||||
.navbar-expand > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -4436,7 +4436,7 @@ input[type="button"].btn-block {
|
|||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-expand > .container,
|
||||
.navbar-expand > .container_carousel,
|
||||
.navbar-expand > .container-fluid {
|
||||
-ms-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
|
@ -10001,7 +10001,7 @@ a.text-dark:hover, a.text-dark:focus {
|
|||
body {
|
||||
min-width: 992px !important;
|
||||
}
|
||||
.container {
|
||||
.container_carousel {
|
||||
min-width: 992px !important;
|
||||
}
|
||||
.navbar {
|
||||
|
@ -10035,4 +10035,4 @@ a.text-dark:hover, a.text-dark:focus {
|
|||
border-color: #dee2e6;
|
||||
}
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap.css.map */
|
||||
/*# sourceMappingURL=bootstrap.css.map */
|
||||
|
|
2
gestion/static/css/bootstrap.min.css
vendored
2
gestion/static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -9,8 +9,33 @@
|
|||
Released for free under the Creative Commons Attribution 3.0 license (templated.co/license)
|
||||
*/
|
||||
|
||||
/* Instagram feed*/
|
||||
#app{
|
||||
display: grid;
|
||||
grid-gap: var(--spacing);
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
min-height: calc(100vh - var(--spacing)*2);
|
||||
}
|
||||
|
||||
|
||||
a.error{
|
||||
grid-column: 1 / -1;
|
||||
justify-self: center;
|
||||
}
|
||||
a.loading{
|
||||
width: calc(var(--spacing)*3);
|
||||
height: calc(var(--spacing)*3);
|
||||
grid-column: 1 / -1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
background: var(--color);
|
||||
animation: load var(--speed) infinite ease-in-out;
|
||||
transform: rotate(0);
|
||||
}
|
||||
@keyframes load{
|
||||
100%{
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
/* Reset */
|
||||
|
||||
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
|
||||
|
@ -219,6 +244,7 @@
|
|||
|
||||
}
|
||||
|
||||
|
||||
/* Grid */
|
||||
|
||||
.row {
|
||||
|
@ -3950,3 +3976,16 @@ div.spoiler
|
|||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
#rudr_instafeed{
|
||||
list-style:none
|
||||
}
|
||||
#rudr_instafeed li{
|
||||
float:left;
|
||||
width:200px;
|
||||
height:200px;
|
||||
margin:10px
|
||||
}
|
||||
#rudr_instafeed li img{
|
||||
max-width:100%;
|
||||
max-height:100%;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
/***** Top content *****/
|
||||
|
||||
.top-content { width: 100%; padding: 60px 0 120px 0; }
|
||||
|
||||
.top-content .carousel { box-shadow: 0 0 15px 0 #444; }
|
||||
.top-content .carousel { box-shadow: 0 0 15px 0 #444; }
|
||||
.top-content .carousel-control-prev { left: -110px; border-bottom: 0; }
|
||||
.top-content .carousel-control-next { right: -110px; border-bottom: 0; }
|
||||
.top-content .carousel-indicators { bottom: -80px; }
|
||||
.top-content .carousel-indicators li { width: 16px; height: 16px; margin-left: 5px; margin-right: 5px; border-radius: 50%; }
|
||||
.top-content .carousel-indicators { bottom: -80px; }
|
||||
.top-content .carousel-indicators li { width: 16px; height: 16px; margin-left: 5px; margin-right: 5px; border-radius: 50%; }
|
||||
|
|
278
gestion/static/js/InstagramFeed.js
Normal file
278
gestion/static/js/InstagramFeed.js
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* InstagramFeed
|
||||
*
|
||||
* @version 1.4.0
|
||||
*
|
||||
* @author jsanahuja <bannss1@gmail.com>
|
||||
* @contributor csanahuja <csanahuja10@gmail.com>
|
||||
*
|
||||
* https://github.com/jsanahuja/InstagramFeed
|
||||
*
|
||||
*/
|
||||
(function(root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define([], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.InstagramFeed = factory();
|
||||
}
|
||||
}(this, function() {
|
||||
var defaults = {
|
||||
'host': "https://www.instagram.com/",
|
||||
'username': '',
|
||||
'tag': '',
|
||||
'container': '',
|
||||
'display_profile': true,
|
||||
'display_biography': true,
|
||||
'display_gallery': true,
|
||||
'display_captions': false,
|
||||
'display_igtv': false,
|
||||
'callback': null,
|
||||
'styling': true,
|
||||
'items': 8,
|
||||
'items_per_row': 4,
|
||||
'margin': 0.5,
|
||||
'image_size': 640,
|
||||
'lazy_load': false,
|
||||
'on_error': console.error
|
||||
};
|
||||
|
||||
var image_sizes = {
|
||||
"150": 0,
|
||||
"240": 1,
|
||||
"320": 2,
|
||||
"480": 3,
|
||||
"640": 4
|
||||
};
|
||||
|
||||
var escape_map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '='
|
||||
};
|
||||
function escape_string(str){
|
||||
return str.replace(/[&<>"'`=\/]/g, function (char) {
|
||||
return escape_map[char];
|
||||
});
|
||||
}
|
||||
|
||||
return function(opts) {
|
||||
this.options = Object.assign({}, defaults);
|
||||
this.options = Object.assign(this.options, opts);
|
||||
this.is_tag = this.options.username == "";
|
||||
|
||||
this.valid = true;
|
||||
if (this.options.username == "" && this.options.tag == "") {
|
||||
this.options.on_error("InstagramFeed: Error, no username or tag defined.", 1);
|
||||
this.valid = false;
|
||||
}
|
||||
if (typeof this.options.get_data !== "undefined") {
|
||||
console.warn("InstagramFeed: options.get_data is deprecated, options.callback is always called if defined");
|
||||
}
|
||||
if (this.options.callback == null && this.options.container == "") {
|
||||
this.options.on_error("InstagramFeed: Error, neither container found nor callback defined.", 2);
|
||||
this.valid = false;
|
||||
}
|
||||
|
||||
this.get = function(callback) {
|
||||
var url = this.is_tag ? this.options.host + "explore/tags/" + this.options.tag + "/" : this.options.host + this.options.username + "/",
|
||||
xhr = new XMLHttpRequest();
|
||||
|
||||
var _this = this;
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
try{
|
||||
var data = xhr.responseText.split("window._sharedData = ")[1].split("<\/script>")[0];
|
||||
}catch(error){
|
||||
_this.options.on_error("InstagramFeed: It looks like the profile you are trying to fetch is age restricted. See https://github.com/jsanahuja/InstagramFeed/issues/26", 3);
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(data.substr(0, data.length - 1));
|
||||
data = data.entry_data.ProfilePage || data.entry_data.TagPage;
|
||||
if(typeof data === "undefined"){
|
||||
_this.options.on_error("InstagramFeed: It looks like YOUR network has been temporary banned because of too many requests. See https://github.com/jsanahuja/jquery.instagramFeed/issues/25", 4);
|
||||
return;
|
||||
}
|
||||
data = data[0].graphql.user || data[0].graphql.hashtag;
|
||||
callback(data, _this);
|
||||
} else {
|
||||
_this.options.on_error("InstagramFeed: Unable to fetch the given user/tag. Instagram responded with the status code: " + xhr.statusText, 5);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open("GET", url, true);
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
this.parse_caption = function(igobj, data) {
|
||||
if (
|
||||
typeof igobj.node.edge_media_to_caption.edges[0] !== "undefined" &&
|
||||
typeof igobj.node.edge_media_to_caption.edges[0].node !== "undefined" &&
|
||||
typeof igobj.node.edge_media_to_caption.edges[0].node.text !== "undefined" &&
|
||||
igobj.node.edge_media_to_caption.edges[0].node.text !== null
|
||||
) {
|
||||
return igobj.node.edge_media_to_caption.edges[0].node.text;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof igobj.node.title !== "undefined" &&
|
||||
igobj.node.title !== null &&
|
||||
igobj.node.title.length != 0
|
||||
) {
|
||||
return igobj.node.title;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof igobj.node.accessibility_caption !== "undefined" &&
|
||||
igobj.node.accessibility_caption !== null &&
|
||||
igobj.node.accessibility_caption.length != 0
|
||||
) {
|
||||
return igobj.node.accessibility_caption;
|
||||
}
|
||||
return (this.is_tag ? data.name : data.username) + " image ";
|
||||
}
|
||||
|
||||
this.display = function(data) {
|
||||
// Styling
|
||||
var html = "",
|
||||
styles;
|
||||
if (this.options.styling) {
|
||||
var width = (100 - this.options.margin * 2 * this.options.items_per_row) / this.options.items_per_row;
|
||||
styles = {
|
||||
'profile_container': " style='text-align:center;'",
|
||||
'profile_image': " style='border-radius:10em;width:15%;max-width:125px;min-width:50px;'",
|
||||
'profile_name': " style='font-size:1.2em;'",
|
||||
'profile_biography': " style='font-size:1em;'",
|
||||
'gallery_image': " style='width:100%;'",
|
||||
'gallery_image_link': " style='width:" + width + "%; margin:" + this.options.margin + "%; position:relative; display: inline-block; height: 100%;'"
|
||||
};
|
||||
// Caption Styling
|
||||
if(this.options.display_captions){
|
||||
html += "<style>\
|
||||
a[data-caption]:hover::after {\
|
||||
content: attr(data-caption);\
|
||||
text-align: center;\
|
||||
font-size: 0.8rem;\
|
||||
color: black;\
|
||||
position: absolute;\
|
||||
left: 0;\
|
||||
right: 0;\
|
||||
bottom: 0;\
|
||||
padding: 1%;\
|
||||
max-height: 100%;\
|
||||
overflow-y: auto;\
|
||||
overflow-x: hidden;\
|
||||
background-color: hsla(0, 100%, 100%, 0.8);\
|
||||
}\
|
||||
</style>";
|
||||
}
|
||||
} else {
|
||||
styles = {
|
||||
'profile_container': "",
|
||||
'profile_image': "",
|
||||
'profile_name': "",
|
||||
'profile_biography': "",
|
||||
'gallery_image': "",
|
||||
'gallery_image_link': ""
|
||||
};
|
||||
}
|
||||
|
||||
// Profile
|
||||
if (this.options.display_profile) {
|
||||
html += "<div class='instagram_profile'" + styles.profile_container + ">";
|
||||
html += "<img class='instagram_profile_image'" + (this.options.lazy_load ? " loading='lazy'" : '') + " src='" + data.profile_pic_url + "' alt='" + (this.is_tag ? data.name + " tag pic" : data.username + " profile pic") + " profile pic'" + styles.profile_image + " />";
|
||||
if (this.is_tag)
|
||||
html += "<p class='instagram_tag'" + styles.profile_name + "><a href='https://www.instagram.com/explore/tags/" + this.options.tag + "' rel='noopener' target='_blank'>#" + this.options.tag + "</a></p>";
|
||||
else
|
||||
html += "<p class='instagram_username'" + styles.profile_name + ">@" + data.full_name + " (<a href='https://www.instagram.com/" + this.options.username + "' rel='noopener' target='_blank'>@" + this.options.username + "</a>)</p>";
|
||||
|
||||
if (!this.is_tag && this.options.display_biography)
|
||||
html += "<p class='instagram_biography'" + styles.profile_biography + ">" + data.biography + "</p>";
|
||||
|
||||
html += "</div>";
|
||||
}
|
||||
|
||||
// Gallery
|
||||
if (this.options.display_gallery) {
|
||||
var image_index = typeof image_sizes[this.options.image_size] !== "undefined" ? image_sizes[this.options.image_size] : image_sizes[640];
|
||||
|
||||
if (typeof data.is_private !== "undefined" && data.is_private === true) {
|
||||
html += "<p class='instagram_private'><strong>This profile is private</strong></p>";
|
||||
} else {
|
||||
var imgs = (data.edge_owner_to_timeline_media || data.edge_hashtag_to_media).edges;
|
||||
max = (imgs.length > this.options.items) ? this.options.items : imgs.length;
|
||||
|
||||
html += "<div class='instagram_gallery'>";
|
||||
for (var i = 0; i < max; i++) {
|
||||
var url = "https://www.instagram.com/p/" + imgs[i].node.shortcode,
|
||||
image, type_resource,
|
||||
caption = escape_string(this.parse_caption(imgs[i], data));
|
||||
|
||||
switch (imgs[i].node.__typename) {
|
||||
case "GraphSidecar":
|
||||
type_resource = "sidecar"
|
||||
image = imgs[i].node.thumbnail_resources[image_index].src;
|
||||
break;
|
||||
case "GraphVideo":
|
||||
type_resource = "video";
|
||||
image = imgs[i].node.thumbnail_src
|
||||
break;
|
||||
default:
|
||||
type_resource = "image";
|
||||
image = imgs[i].node.thumbnail_resources[image_index].src;
|
||||
}
|
||||
|
||||
if (this.is_tag) data.username = '';
|
||||
html += "<a href='" + url + (this.options.display_captions? "' data-caption='" + caption : "") + "' class='instagram-" + type_resource + "' rel='noopener' target='_blank'" + styles.gallery_image_link + ">";
|
||||
html += "<img" + (this.options.lazy_load ? " loading='lazy'" : '') + " src='" + image + "' alt='" + caption + "'" + styles.gallery_image + " />";
|
||||
html += "</a>";
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
// IGTV
|
||||
if (this.options.display_igtv && typeof data.edge_felix_video_timeline !== "undefined") {
|
||||
var igtv = data.edge_felix_video_timeline.edges,
|
||||
max = (igtv.length > this.options.items) ? this.options.items : igtv.length
|
||||
if (igtv.length > 0) {
|
||||
html += "<div class='instagram_igtv'>";
|
||||
for (var i = 0; i < max; i++) {
|
||||
var url = "https://www.instagram.com/p/" + igtv[i].node.shortcode,
|
||||
caption = escape_string(this.parse_caption(igtv[i], data));
|
||||
|
||||
html += "<a href='" + url + (this.options.display_captions? "' data-caption='" + caption : "") + "' rel='noopener' target='_blank'" + styles.gallery_image_link + ">";
|
||||
html += "<img" + (this.options.lazy_load ? " loading='lazy'" : '') + " src='" + igtv[i].node.thumbnail_src + "' alt='" + caption + "'" + styles.gallery_image + " />";
|
||||
html += "</a>";
|
||||
}
|
||||
html += "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
this.options.container.innerHTML = html;
|
||||
};
|
||||
|
||||
this.run = function() {
|
||||
this.get(function(data, instance) {
|
||||
if(instance.options.container != ""){
|
||||
instance.display(data);
|
||||
}
|
||||
if(typeof instance.options.callback === "function"){
|
||||
instance.options.callback(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.valid) {
|
||||
this.run();
|
||||
}
|
||||
};
|
||||
}));
|
1
gestion/static/js/InstagramFeed.min.js
vendored
Normal file
1
gestion/static/js/InstagramFeed.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
22
gestion/static/js/bootstrap.js
vendored
22
gestion/static/js/bootstrap.js
vendored
|
@ -1265,7 +1265,7 @@ if (typeof jQuery === 'undefined') {
|
|||
title: '',
|
||||
delay: 0,
|
||||
html: false,
|
||||
container: false,
|
||||
container_carousel: false,
|
||||
viewport: {
|
||||
selector: 'body',
|
||||
padding: 0
|
||||
|
@ -1407,7 +1407,7 @@ if (typeof jQuery === 'undefined') {
|
|||
.addClass(placement)
|
||||
.data('bs.' + this.type, this)
|
||||
|
||||
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
|
||||
this.options.container_carousel ? $tip.appendTo(this.options.container_carousel) : $tip.insertAfter(this.$element)
|
||||
|
||||
var pos = this.getPosition()
|
||||
var actualWidth = $tip[0].offsetWidth
|
||||
|
@ -1415,13 +1415,13 @@ if (typeof jQuery === 'undefined') {
|
|||
|
||||
if (autoPlace) {
|
||||
var orgPlacement = placement
|
||||
var $container = this.options.container ? $(this.options.container) : this.$element.parent()
|
||||
var containerDim = this.getPosition($container)
|
||||
var $container_carousel = this.options.container_carousel ? $(this.options.container_carousel) : this.$element.parent()
|
||||
var container_carouselDim = this.getPosition($container_carousel)
|
||||
|
||||
placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
|
||||
placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
|
||||
placement = placement == 'bottom' && pos.bottom + actualHeight > container_carouselDim.bottom ? 'top' :
|
||||
placement == 'top' && pos.top - actualHeight < container_carouselDim.top ? 'bottom' :
|
||||
placement == 'right' && pos.right + actualWidth > container_carouselDim.width ? 'left' :
|
||||
placement == 'left' && pos.left - actualWidth < container_carouselDim.left ? 'right' :
|
||||
placement
|
||||
|
||||
$tip
|
||||
|
@ -2051,11 +2051,11 @@ if (typeof jQuery === 'undefined') {
|
|||
})
|
||||
}
|
||||
|
||||
Tab.prototype.activate = function (element, container, callback) {
|
||||
var $active = container.find('> .active')
|
||||
Tab.prototype.activate = function (element, container_carousel, callback) {
|
||||
var $active = container_carousel.find('> .active')
|
||||
var transition = callback
|
||||
&& $.support.transition
|
||||
&& (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
|
||||
&& (($active.length && $active.hasClass('fade')) || !!container_carousel.find('> .fade').length)
|
||||
|
||||
function next() {
|
||||
$active
|
||||
|
|
4
gestion/static/js/bootstrap.min.js
vendored
4
gestion/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
22
gestion/static/js/bootstrap_carousel.js
vendored
22
gestion/static/js/bootstrap_carousel.js
vendored
|
@ -2755,7 +2755,7 @@
|
|||
selector: '(string|boolean)',
|
||||
placement: '(string|function)',
|
||||
offset: '(number|string|function)',
|
||||
container: '(string|element|boolean)',
|
||||
container_carousel: '(string|element|boolean)',
|
||||
fallbackPlacement: '(string|array)',
|
||||
boundary: '(string|element)',
|
||||
sanitize: 'boolean',
|
||||
|
@ -2779,7 +2779,7 @@
|
|||
selector: false,
|
||||
placement: 'top',
|
||||
offset: 0,
|
||||
container: false,
|
||||
container_carousel: false,
|
||||
fallbackPlacement: 'flip',
|
||||
boundary: 'scrollParent',
|
||||
sanitize: true,
|
||||
|
@ -2957,12 +2957,12 @@
|
|||
|
||||
this.addAttachmentClass(attachment);
|
||||
|
||||
var container = this._getContainer();
|
||||
var container_carousel = this._getcontainer_carousel();
|
||||
|
||||
$(tip).data(this.constructor.DATA_KEY, this);
|
||||
|
||||
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
|
||||
$(tip).appendTo(container);
|
||||
$(tip).appendTo(container_carousel);
|
||||
}
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.INSERTED);
|
||||
|
@ -3153,16 +3153,16 @@
|
|||
return offset;
|
||||
};
|
||||
|
||||
_proto._getContainer = function _getContainer() {
|
||||
if (this.config.container === false) {
|
||||
_proto._getcontainer_carousel = function _getcontainer_carousel() {
|
||||
if (this.config.container_carousel === false) {
|
||||
return document.body;
|
||||
}
|
||||
|
||||
if (Util.isElement(this.config.container)) {
|
||||
return $(this.config.container);
|
||||
if (Util.isElement(this.config.container_carousel)) {
|
||||
return $(this.config.container_carousel);
|
||||
}
|
||||
|
||||
return $(document).find(this.config.container);
|
||||
return $(document).find(this.config.container_carousel);
|
||||
};
|
||||
|
||||
_proto._getAttachment = function _getAttachment(placement) {
|
||||
|
@ -4060,10 +4060,10 @@
|
|||
} // Private
|
||||
;
|
||||
|
||||
_proto._activate = function _activate(element, container, callback) {
|
||||
_proto._activate = function _activate(element, container_carousel, callback) {
|
||||
var _this2 = this;
|
||||
|
||||
var activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? $(container).find(Selector$9.ACTIVE_UL) : $(container).children(Selector$9.ACTIVE);
|
||||
var activeElements = container_carousel && (container_carousel.nodeName === 'UL' || container_carousel.nodeName === 'OL') ? $(container_carousel).find(Selector$9.ACTIVE_UL) : $(container_carousel).children(Selector$9.ACTIVE);
|
||||
var active = activeElements[0];
|
||||
var isTransitioning = callback && active && $(active).hasClass(ClassName$9.FADE);
|
||||
|
||||
|
|
2
gestion/static/js/bootstrap_carousel.min.js
vendored
2
gestion/static/js/bootstrap_carousel.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -13,9 +13,10 @@
|
|||
<meta charset="utf-8" />
|
||||
<title>{% block titre %}{% endblock %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{% block extrahead %}{% endblock %}
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}" />
|
||||
{% block extrahead %}{% endblock %}
|
||||
|
||||
</head>
|
||||
<body class="subpage">
|
||||
|
||||
|
@ -47,6 +48,7 @@
|
|||
<li><a href="{% url 'partitions:liste' %}">{% trans "Répertoire" %}</a></li>
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="{% url 'trombonoscope:view' %}">{% trans "Trombonoscope" %}</a></li>
|
||||
<li><a href="https://photos.cof.ens.fr/index.php/Clubs-du-COF/L'Ernestophone" target="_blank">{% trans "Galerie Photo" %}</a></li>
|
||||
<li><a href="{% url 'pads:list' %}">{% trans "Pads" %}</a></li>
|
||||
{% if user.is_superuser or user.profile.is_chef %}
|
||||
<li><a href="/admin/">{% trans "Administration" %}</a></li>
|
||||
|
@ -82,17 +84,22 @@
|
|||
<li><a target="_blank" href="https://www.youtube.com/channel/UC_RahrdZLoBGpJQHwegV4tQ"
|
||||
|
||||
class="icon fa-youtube-play"><span class="label">Youtube</span></a></li>
|
||||
|
||||
<li><a target="_blank" href="https://www.instagram.com/ernestophone/" class="icon fa-instagram"><span class="label">Instagram</span></a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Scripts -->
|
||||
|
||||
|
||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.scrolly.min.js' %}"></script>
|
||||
<script src="{% static 'js/skel.min.js' %}"></script>
|
||||
<script src="{% static 'js/util.js' %}"></script>
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/InstagramFeed.js' %}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function ouvrirFermerSpoiler(div)
|
||||
|
|
|
@ -52,9 +52,20 @@
|
|||
</div>
|
||||
<div class ="6u 12u (small)">
|
||||
<span class="image fit">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/registration.png' %}" alt="" />
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="" class="icon fa-copyright">lor_tie</a>
|
||||
<div href="" class="icon fa-copyright"> lor_tie</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{% load i18n %}
|
||||
{% load halflength %}
|
||||
{% block titre %}{% trans "L'Ernestophone : La fanfare de l'Ecole normale supérieure de Paris" %}{% endblock %}
|
||||
{% block extrahead %} <link rel="stylesheet" href="{% static 'css/style.css' %}" />{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Banner -->
|
||||
|
@ -32,8 +33,12 @@
|
|||
grâce à notre énergie et notre répertoire oscillant entre pop et
|
||||
brass band.{% endblocktrans %}</p>
|
||||
</header>
|
||||
<!-- 2 Column Video Section -->
|
||||
|
||||
<!-- Carousel row -->
|
||||
<div class="top-content">
|
||||
<div class="container_carousel">
|
||||
<div class="row">
|
||||
<div class="col col-md-10 offset-md-1 col-lg-8 offset-lg-2">
|
||||
<!-- Carousel -->
|
||||
{% if videos %}
|
||||
<div id="carousel" class="carousel slide">
|
||||
|
@ -72,8 +77,14 @@
|
|||
<span class="sr-only">Next</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- End carousel -->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
@ -83,6 +94,7 @@
|
|||
<div class="inner">
|
||||
<header>
|
||||
<h2>{% trans "L'Ernestophone in a nutshell" %}</h2>
|
||||
<div id="instagram-feed" class="instagram_feed"> </div>
|
||||
<p style="color:white">{% blocktrans %}L'Ernestophone, c'est la fanfare de l'École
|
||||
normale supérieure, (re)créée en septembre 2011, pour le plus
|
||||
grand bonheur des petits. Et des grands.{% endblocktrans %}</p>
|
||||
|
@ -155,10 +167,20 @@
|
|||
{% endfor %}
|
||||
<a href="{% url 'partitions:liste' %}" class="button alt fit" >{% trans "Voir le répertoire présent et passé" %}</a>
|
||||
<span class="image fit">
|
||||
{% if photo_rep %}
|
||||
<img src="{{photo_rep.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo_rep.url %}
|
||||
<a href="{{photo_rep.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo_rep.color}}"> {% if photo_rep.auteur %}{{photo_rep.auteur}}{%endif%}</a>
|
||||
{% elif photo_rep.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo_rep.color}}" > {{photo_rep.auteur}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/repertoire.jpg' %}" alt="" />
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="" class="icon fa-copyright"> Lucas Gierzack</a>
|
||||
<div class="icon fa-copyright"> Lucas Gierzack</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
@ -176,10 +198,21 @@
|
|||
Nous prêtons des instruments à tout membre du COF souhaitant découvrir les joies de la fanfaronnerie !{% endblocktrans %}</p>
|
||||
</div>
|
||||
<div class="5u 12u$(small)"><div>
|
||||
<span class="image fit"><img src="{% static 'images/join.jpg' %}" alt="" />
|
||||
<span class="image fit">
|
||||
{% if photo_join %}
|
||||
<img src="{{photo_join.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo_join.url %}
|
||||
<a href="{{photo_join.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo_join.color}}"> {% if photo_join.auteur %}{{photo_join.auteur}}{%endif%}</a>
|
||||
{% elif photo_join.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo_join.color}}" > {{photo_join.auteur}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/join.jpg' %}" alt="" />
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="https://evarin.fr/" target="_blank" class="icon fa-copyright"> Evarin</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -192,10 +225,21 @@
|
|||
<div class="tab tab-3 flex flex-3 active">
|
||||
<div class="box">
|
||||
<div><span class="image right">
|
||||
{% if photo_contact %}
|
||||
<img src="{{photo_contact.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo_contact.url %}
|
||||
<a href="{{photo_contact.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo_contact.color}}"> {% if photo_contact.auteur %}{{photo_contact.auteur}}{%endif%}</a>
|
||||
{% elif photo_contact.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo_contact.color}}" > {{photo_contact.auteur}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/contact.jpg' %}" alt=""/>
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="https://evarin.fr/" target="_blank" class="icon fa-copyright"> Evarin</a>
|
||||
</div></span></div><p style="color:white" align="center">{% blocktrans %}Vous préparez un
|
||||
</div>
|
||||
{% endif %}
|
||||
</span></div><p style="color:white" align="center">{% blocktrans %}Vous préparez un
|
||||
évenement et vous recherchez une fanfare dynamique ? N'hésitez
|
||||
pas à nous envoyer un message nous serons ravis de venir
|
||||
fanfaronner pour vous !! Vérifiez notre{% endblocktrans %} <a href="{% url 'calendrier:liste' %}" class="text">{% trans "agenda" %}</a>. <br />
|
||||
|
@ -211,10 +255,12 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
<a target="_blank" href="https://www.youtube.com/channel/UC_RahrdZLoBGpJQHwegV4tQ"
|
||||
|
||||
class="icon fa-youtube-play fa-5x"><span class="label">Youtube</span></a>
|
||||
class="icon fa-youtube-play fa-5x"><span class="label">Youtube</span> </a>
|
||||
|
||||
<a target="_blank" href="https://www.instagram.com/ernestophone/" class="icon fa-instagram fa-5x"><span class="label">Instagram</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -223,7 +269,25 @@
|
|||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script src="{% static 'js/InstagramFeed.min.js' %}"></script>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
new InstagramFeed({
|
||||
'username': 'ernestophone',
|
||||
'container': "#instagram-feed" ,
|
||||
'display_profile': false,
|
||||
'display_biography': false,
|
||||
'display_gallery': true,
|
||||
'display_captions': true,
|
||||
'callback': null,
|
||||
'styling': true,
|
||||
'items': 8,
|
||||
'items_per_row': 8,
|
||||
'margin': 1
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery-migrate-3.0.0.min.js' %}"></script>
|
||||
<script src="{% static 'js/popper.min.js' %}"></script>
|
||||
|
|
|
@ -34,9 +34,22 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="6u 12u$(xsmall)">
|
||||
<span class="image fit"><img src="{% static 'images/connect.jpg' %}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<span class="image fit">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/connect.jpg' %}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="https://evarin.fr/" target="_blank" class="icon fa-copyright"> Evarin</a>
|
||||
</div></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<section class="wrapper style1" >
|
||||
<div class="inner">
|
||||
<h2>{% trans "Créer un compte :" %}</h2>
|
||||
{% if error %}
|
||||
{% if error %}
|
||||
<p>{{ error }}</p>
|
||||
{% endif %}
|
||||
<div class="row uniform">
|
||||
|
@ -48,10 +48,21 @@
|
|||
</div>
|
||||
<div class ="6u 12u (small)">
|
||||
<span class="image fit">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/registration.png' %}" alt="" />
|
||||
<div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="" class="icon fa-copyright">lor_tie</a>
|
||||
<div href="" class="icon fa-copyright"> lor_tie</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,13 +3,14 @@ from partitions.models import Category
|
|||
from gestion.forms import ChangeMembreForm, ChangeFormUser,RegistrationFormUser, InscriptionMembreForm
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponseRedirect
|
||||
from gestion.models import ErnestoUser, VideoGallery
|
||||
from gestion.models import ErnestoUser, VideoGallery, Photo
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from calendrier.forms import ChangeDoodleName
|
||||
from partitions.decorators import chef_required
|
||||
from gestion.mixins import ChefRequiredMixin
|
||||
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import render_to_response
|
||||
import string
|
||||
|
@ -27,7 +28,21 @@ def home(request):
|
|||
return redirect('calendrier:home')
|
||||
categories = Category.objects.filter(name = "Partitions actives").prefetch_related("partitionset_set")
|
||||
videos = VideoGallery.objects.all()
|
||||
return render_to_response( 'gestion/index.html', {"request":request,"categories": categories,'videos':videos})
|
||||
photo_rep = Photo.objects.filter(cat='home_rep').order_by('?').first()
|
||||
photo_join = Photo.objects.filter(cat='home_join').order_by('?').first()
|
||||
photo_contact = Photo.objects.filter(cat='home_contact').order_by('?').first()
|
||||
return render_to_response( 'gestion/index.html', {"request":request,"categories": categories,'videos':videos,'photo_rep':photo_rep,'photo_join':photo_join,'photo_contact':photo_contact})
|
||||
|
||||
class MyLoginView(LoginView):
|
||||
|
||||
template_name = 'gestion/login.html'
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
photo = Photo.objects.filter(cat='login').order_by('?').first()
|
||||
context['photo'] = photo
|
||||
return context
|
||||
|
||||
|
||||
def thanks(request):
|
||||
|
||||
return render(request, 'gestion/thanks.html', locals())
|
||||
|
@ -50,6 +65,7 @@ def changename(request):
|
|||
|
||||
@login_required
|
||||
def change_membre(request):
|
||||
photo = Photo.objects.filter(cat='change_membre').order_by('?').first()
|
||||
if request.method == 'POST':
|
||||
comp_form = ChangeMembreForm(request.POST, instance=request.user.profile)
|
||||
user_form = ChangeFormUser(request.POST, instance=request.user)
|
||||
|
@ -82,6 +98,7 @@ def change_password(request):
|
|||
|
||||
|
||||
def inscription_membre(request):
|
||||
photo = Photo.objects.filter(cat='inscription_membre').order_by('?').first()
|
||||
if request.method == 'POST':
|
||||
|
||||
user_form = RegistrationFormUser(request.POST)
|
||||
|
|
37
partitions/migrations/0004_auto_20210331_1350.py
Normal file
37
partitions/migrations/0004_auto_20210331_1350.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 2.2.17 on 2021-03-31 13:50
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.functions.text
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('partitions', '0003_auto_20200716_1749'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='partition',
|
||||
options={'ordering': (django.db.models.functions.text.Lower('nom'),), 'verbose_name': 'Morceau', 'verbose_name_plural': 'Morceaux'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='partitionset',
|
||||
options={'ordering': (django.db.models.functions.text.Lower('nom'),), 'verbose_name': 'Morceau', 'verbose_name_plural': 'Morceaux'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='partitionset',
|
||||
name='download_unlogged',
|
||||
field=models.CharField(choices=[('n', 'Non'), ('o', 'Oui')], default='n', max_length=1, verbose_name='Téléchargeable non connecté ?'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='partitionset',
|
||||
name='infos',
|
||||
field=models.TextField(blank=True, default='', verbose_name='Infos utiles'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='partitionset',
|
||||
name='infos_en',
|
||||
field=models.TextField(blank=True, default='', verbose_name='Infos utiles en anglais'),
|
||||
),
|
||||
]
|
|
@ -2,10 +2,12 @@ import os
|
|||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from colorful.fields import RGBColorField
|
||||
from django.db.models.functions import Lower
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=127)
|
||||
order = models.IntegerField(verbose_name=_("ordre"))
|
||||
|
@ -45,9 +47,9 @@ class PartitionSet(models.Model):
|
|||
on_delete=models.PROTECT,
|
||||
verbose_name=_("Type de partition")
|
||||
)
|
||||
|
||||
infos = models.TextField(_("Infos utiles"), null=True, blank=True)
|
||||
infos_en = models.TextField("Infos utiles en anglais", null=True, blank=True)
|
||||
download_unlogged = models.CharField(_("Téléchargeable non connecté ?"),default='n',choices = [('n',_('Non')),('o',_('Oui'))],max_length=1)
|
||||
infos = models.TextField(_("Infos utiles"), null=False, blank=True,default="")
|
||||
infos_en = models.TextField("Infos utiles en anglais", null=False, blank=True,default="")
|
||||
url = models.URLField(_("Url d'une video youtube"),null=True,blank=True, help_text= _("Dans Youtube cliquer sur partager puis importer pour récuperer la bonne adresse"))
|
||||
def __str__(self):
|
||||
return("%s - %s [%s]" % (self.nom, self.auteur, self.category))
|
||||
|
|
|
@ -8,8 +8,20 @@
|
|||
<div id="main">
|
||||
<section class="wrapper style1">
|
||||
<div class="inner">
|
||||
<span class="image fit"><img src="{% static 'images/all_repertoire.jpg' %}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<a href="" class="icon fa-copyright" style="color:#000000"> Lucas Gierzack</a>
|
||||
|
||||
<span class="image fit">
|
||||
{% if photo %}
|
||||
<img src="{{photo.image.url}}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
{% if photo.url %}
|
||||
<a href="{{photo.url}}" target="_blank" class="icon fa-copyright" style="color: {{photo.color}}"> {% if photo.auteur %}{{photo.auteur}}{%endif%}</a>
|
||||
{% elif photo.auteur %}
|
||||
<div class="icon fa-copyright" style="color: {{photo.color}}" > {{photo.auteur}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{% static 'images/all_repertoire.jpg' %}" alt="" /> <div style="position:absolute;z-index:1;right:0;bottom:0">
|
||||
<div class="icon fa-copyright" style="color:#000000"> Lucas Gierzack</div>
|
||||
{% endif %}
|
||||
</div></span>
|
||||
{% if user.profile.is_chef %}
|
||||
<a href="{% url "partitions:ajouter_morceau" %}" class="button alt big">{% trans "Ajouter un morceau" %}</a>   <a href="{% url "partitions:download_musecores" %}" class="button alt big">{% trans "Télécharger tous les musecores actifs" %}</a>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.shortcuts import render, HttpResponse, get_object_or_404, redirect, reverse
|
||||
from partitions.models import Category, Partition, PartitionSet
|
||||
from gestion.models import Photo
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from partitions.forms import UploadFileForm, UploadMorceauForm
|
||||
from django.forms.models import modelform_factory
|
||||
|
@ -54,14 +55,15 @@ def download_musecores(request):
|
|||
|
||||
def liste(request):
|
||||
categories = Category.objects.prefetch_related("partitionset_set").order_by("order")
|
||||
return render(request, 'partitions/repertoire.html', {"categories": categories})
|
||||
photo = Photo.objects.filter(cat='part').order_by('?').first()
|
||||
return render(request, 'partitions/repertoire.html', {"categories": categories,"photo":photo})
|
||||
|
||||
@login_required
|
||||
def listepart(request, nom, auteur):
|
||||
p = get_object_or_404(PartitionSet, nom=nom, auteur=auteur)
|
||||
part = p.partition_set.all().order_by('nom')
|
||||
ChefEditForm = modelform_factory(PartitionSet,
|
||||
fields=("category", "infos","url","infos_en"))
|
||||
fields=("category","download_unlogged", "infos","url","infos_en"))
|
||||
if request.method == "POST" and request.user.profile.is_chef:
|
||||
form = ChefEditForm(request.POST, instance=p)
|
||||
if form.is_valid():
|
||||
|
@ -95,30 +97,35 @@ def upload(request, nom, auteur):
|
|||
form = UploadFileForm()
|
||||
return render(request, 'partitions/upload.html', locals())
|
||||
|
||||
@login_required
|
||||
|
||||
def see(request, nom, auteur, partition_id):
|
||||
partition = get_object_or_404(Partition, id=partition_id)
|
||||
_, extension = os.path.splitext(partition.part.path)
|
||||
if ".pdf" == extension:
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse(content=myfile.read())
|
||||
response["Content-Type"] = "application/pdf"
|
||||
response["Content-Disposition"] = "inline; filename=%s_%s_%s.pdf" % (
|
||||
slugify(nom), slugify(auteur), slugify(partition.nom))
|
||||
return response
|
||||
elif ".mp3" == extension:
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse()
|
||||
response.write(myfile.read())
|
||||
response["Content-Type"] = "audio/mp3"
|
||||
response["Content-Length"] = myfile.size
|
||||
return response
|
||||
else:
|
||||
p = get_object_or_404(PartitionSet, nom=nom, auteur=auteur)
|
||||
part = p.partition_set.all()
|
||||
return render(request, 'partitions/part.html', locals())
|
||||
download_unlogged = partition.morceau.download_unlogged
|
||||
if(download_unlogged == 'o' or request.user.is_authenticated):
|
||||
if ".pdf" == extension:
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse(content=myfile.read())
|
||||
response["Content-Type"] = "application/pdf"
|
||||
response["Content-Disposition"] = "inline; filename=%s_%s_%s.pdf" % (
|
||||
slugify(nom), slugify(auteur), slugify(partition.nom))
|
||||
return response
|
||||
elif ".mp3" == extension:
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse()
|
||||
response.write(myfile.read())
|
||||
response["Content-Type"] = "audio/mp3"
|
||||
response["Content-Length"] = myfile.size
|
||||
return response
|
||||
else:
|
||||
p = get_object_or_404(PartitionSet, nom=nom, auteur=auteur)
|
||||
part = p.partition_set.all()
|
||||
return render(request, 'partitions/part.html', locals())
|
||||
else :
|
||||
return redirect('login')
|
||||
|
||||
@chef_required
|
||||
def delete(request, nom, auteur, id):
|
||||
p = get_object_or_404(PartitionSet, nom=nom, auteur=auteur)
|
||||
|
@ -189,14 +196,19 @@ def conf_delete_morc(request, nom, auteur):
|
|||
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
def download(request, nom, auteur, partition_id):
|
||||
|
||||
partition = get_object_or_404(Partition, id=partition_id)
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse(content=myfile.read())
|
||||
typ = os.path.splitext(myfile.name)[1][1:]
|
||||
response['Content-Type'] = 'application/%s' % (typ, )
|
||||
response['Content-Disposition'] = 'attachment; filename=%s_%s_%s.%s' % (
|
||||
slugify(nom), slugify(auteur), slugify(partition.nom), typ)
|
||||
return response
|
||||
download_unlogged = partition.morceau.download_unlogged
|
||||
if(download_unlogged == 'o' or request.user.is_authenticated):
|
||||
with open(partition.part.path, 'rb') as f:
|
||||
myfile = File(f)
|
||||
response = HttpResponse(content=myfile.read())
|
||||
typ = os.path.splitext(myfile.name)[1][1:]
|
||||
response['Content-Type'] = 'application/%s' % (typ, )
|
||||
response['Content-Disposition'] = 'attachment; filename=%s_%s_%s.%s' % (
|
||||
slugify(nom), slugify(auteur), slugify(partition.nom), typ)
|
||||
return response
|
||||
else :
|
||||
return redirect('login')
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
|
||||
<span class="image fit">
|
||||
{% if request.user|has_avatar %}
|
||||
{% if user_tromb.user|has_avatar %}
|
||||
{% avatar user_tromb.user 250 %}
|
||||
{% else %}
|
||||
<img src="{% static "images/Ernestophone_logo.png" %}">
|
||||
|
@ -62,7 +62,7 @@
|
|||
|
||||
|
||||
<span class="image fit">
|
||||
{% if request.user|has_avatar %}
|
||||
{% if user_tromb.user|has_avatar %}
|
||||
{% avatar user_tromb.user 250 %}
|
||||
{% else %}
|
||||
<img src="{% static "images/Ernestophone_logo.png" %}">
|
||||
|
|
Loading…
Add table
Reference in a new issue