forked from DGNum/gestioCOF
Compare commits
108 commits
Production
...
master
Author | SHA1 | Date | |
---|---|---|---|
1677e4088e | |||
0cebc8828b | |||
5bc57493f7 | |||
fa427c1c8d | |||
12b48dc8f6 | |||
6fa7b88b5d | |||
342ab6f141 | |||
d0941c8dca | |||
6977cdf282 | |||
415c0055bf | |||
e308258e40 | |||
08adfc8404 | |||
2eaf3542db | |||
b13ed3c169 | |||
897ee5dc17 | |||
4747c773ea | |||
0721cd4e45 | |||
705bc4395c | |||
48bbd0f0db | |||
94dafa5c5b | |||
c0db1ba8e4 | |||
635b3d1607 | |||
7709f8a652 | |||
db4d1264c1 | |||
a9be316aaa | |||
622770aec4 | |||
7acc4609d3 | |||
65c483e935 | |||
|
e6930d3ebb | ||
8d7ccccc9b | |||
4d4b536781 | |||
0674217526 | |||
bf30a9e510 | |||
47cf999359 | |||
e1bd6bc6ad | |||
9178511005 | |||
3997f48eb8 | |||
bb80716cb4 | |||
2ec15ad2d5 | |||
81af13a216 | |||
1a8fe48d05 | |||
18c0f0f699 | |||
2d1357c4ff | |||
853611556c | |||
29b1581ab7 | |||
2fb4c6a95c | |||
e1c9a4474d | |||
e74e03d36b | |||
c8dc0ca34e | |||
982c82ba31 | |||
531ba575c8 | |||
def9b68a90 | |||
7cd4338a3a | |||
f5bfbddfbe | |||
9e185c8cb5 | |||
b8c963779a | |||
57ff8fe131 | |||
0fdc1a2a46 | |||
aafbab29ff | |||
fb66c064cd | |||
2126224e15 | |||
d6109a9312 | |||
a69bd0426f | |||
81a4dbef7c | |||
4fedf3453d | |||
8607d77c84 | |||
bc55a3067e | |||
dd68ad91cd | |||
2f71246509 | |||
a20a1c11d6 | |||
0e1ff1765a | |||
177d413f4c | |||
01dd16c795 | |||
84c87c1b4b | |||
1b143b322f | |||
f9456e3c29 | |||
dd1350f1c2 | |||
8f4cb68d31 | |||
3a3f96a8df | |||
7d8926e459 | |||
3fee014384 | |||
cd351786bb | |||
7362c2fa2a | |||
efbc947145 | |||
0c45262fbc | |||
cabd277b4a | |||
4108efe8c9 | |||
693e4252d5 | |||
3aa928e8f0 | |||
5e2e68960b | |||
91e9beee11 | |||
ac286209ed | |||
b03cda5962 | |||
4feb567af7 | |||
1ac47885d0 | |||
071c810605 | |||
7f00ce0ff1 | |||
95136cb4eb | |||
e299997aa8 | |||
40f34926bb | |||
69976a878a | |||
6621ae3950 | |||
9288daaf9e | |||
e92c500940 | |||
|
d75eaf583f | ||
55bd3ab51d | |||
|
f640a25f59 | ||
|
f881c7cd8b |
161 changed files with 3330 additions and 827 deletions
1
.credentials/HCAPTCHA_SECRET
Normal file
1
.credentials/HCAPTCHA_SECRET
Normal file
|
@ -0,0 +1 @@
|
|||
0x0000000000000000000000000000000000000000
|
1
.credentials/HCAPTCHA_SITEKEY
Normal file
1
.credentials/HCAPTCHA_SITEKEY
Normal file
|
@ -0,0 +1 @@
|
|||
10000000-ffff-ffff-ffff-000000000001
|
1
.credentials/KFETOPEN_TOKEN
Normal file
1
.credentials/KFETOPEN_TOKEN
Normal file
|
@ -0,0 +1 @@
|
|||
k-feste_token
|
1
.credentials/SECRET_KEY
Normal file
1
.credentials/SECRET_KEY
Normal file
|
@ -0,0 +1 @@
|
|||
insecure-key
|
1
.credentials/SYMPA_PASSWORD
Normal file
1
.credentials/SYMPA_PASSWORD
Normal file
|
@ -0,0 +1 @@
|
|||
toto
|
1
.credentials/SYMPA_USERNAME
Normal file
1
.credentials/SYMPA_USERNAME
Normal file
|
@ -0,0 +1 @@
|
|||
sympa
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,3 +21,4 @@ media/
|
|||
# VSCode
|
||||
.vscode/
|
||||
.direnv
|
||||
.static
|
||||
|
|
|
@ -43,13 +43,21 @@ variables:
|
|||
# Keep this disabled for now, as it may kill GitLab...
|
||||
# coverage: '/TOTAL.*\s(\d+\.\d+)\%$/'
|
||||
|
||||
kfettest:
|
||||
stage: test
|
||||
extends: .test_template
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod"
|
||||
script:
|
||||
- coverage run manage.py test kfet
|
||||
|
||||
coftest:
|
||||
stage: test
|
||||
extends: .test_template
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod"
|
||||
script:
|
||||
- coverage run manage.py test gestioncof bda kfet petitscours shared --parallel
|
||||
- coverage run manage.py test gestioncof bda petitscours shared --parallel
|
||||
|
||||
bdstest:
|
||||
stage: test
|
||||
|
|
|
@ -23,6 +23,9 @@ adhérents ni des cotisations.
|
|||
|
||||
## TODO Prod
|
||||
|
||||
- Lancer `python manage.py update_translation_fields` après la migration
|
||||
- Mettre à jour les units systemd `daphne.service` et `worker.service`
|
||||
|
||||
- Créer un compte hCaptcha (https://www.hcaptcha.com/), au COF, et remplacer les secrets associés
|
||||
|
||||
## Version ??? - ??/??/????
|
||||
|
@ -65,6 +68,8 @@ adhérents ni des cotisations.
|
|||
|
||||
- Fixe un problème de rendu causé par l'agrandissement du menu
|
||||
|
||||
- Mise à jour vers Channels 3.x et Django 3.2
|
||||
|
||||
## Version 0.12 - 17/06/2022
|
||||
|
||||
### K-Fêt
|
||||
|
|
12
README.md
12
README.md
|
@ -18,7 +18,7 @@ Il vous faudra installer pip, les librairies de développement de python ainsi
|
|||
que sqlite3, un moteur de base de données léger et simple d'utilisation. Sous
|
||||
Debian et dérivées (Ubuntu, ...) :
|
||||
|
||||
sudo apt-get install python3-pip python3-dev python3-venv sqlite3
|
||||
sudo apt-get install python3-pip python3-dev python3-venv sqlite3 libsasl2-dev python-dev-is-python3 libldap2-dev libssl-dev
|
||||
|
||||
Si vous décidez d'utiliser un environnement virtuel Python (virtualenv;
|
||||
fortement conseillé), déplacez-vous dans le dossier où est installé GestioCOF
|
||||
|
@ -30,7 +30,15 @@ Pour l'activer, il faut taper
|
|||
|
||||
. venv/bin/activate
|
||||
|
||||
depuis le même dossier.
|
||||
depuis le même dossier. Pour préparer l'environnement à l'utilisation de `./manage.py`
|
||||
(qui permet de faire des tests en local), il faut également taper
|
||||
|
||||
export CREDENTIALS_DIRECTORY=$(realpath .credentials)
|
||||
export DJANGO_SETTINGS_MODULE=gestioasso.settings.local
|
||||
export GESTIOCOF_DEBUG=true
|
||||
export GESTIOCOF_STATIC_ROOT=$(realpath .static)
|
||||
export GESTIOBDS_DEBUG=true
|
||||
export GESTIOBDS_STATIC_ROOT=$(realpath .static)
|
||||
|
||||
Vous pouvez maintenant installer les dépendances Python depuis le fichier
|
||||
`requirements-devel.txt` :
|
||||
|
|
23
bda/migrations/0019_auto_20220630_1245.py
Normal file
23
bda/migrations/0019_auto_20220630_1245.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.13 on 2022-06-30 10:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bda", "0018_auto_20201021_1818"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="choixspectacle",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="choixspectacle",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("participant", "spectacle"), name="unique_participation"
|
||||
),
|
||||
),
|
||||
]
|
18
bda/migrations/0019_auto_20240707_1359.py
Normal file
18
bda/migrations/0019_auto_20240707_1359.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.28 on 2024-07-07 11:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bda', '0018_auto_20201021_1818'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='attribution',
|
||||
name='paymenttype',
|
||||
field=models.CharField(blank=True, choices=[('cash', 'Cash'), ('cb', 'CB'), ('cheque', 'Chèque'), ('virement', 'Virement'), ('autre', 'Autre')], max_length=8, verbose_name='Moyen de paiement'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
# Generated by Django 4.2.16 on 2025-02-26 08:23
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bda", "0019_auto_20220630_1245"),
|
||||
("bda", "0019_auto_20240707_1359"),
|
||||
]
|
||||
|
||||
operations = []
|
|
@ -151,6 +151,7 @@ PAYMENT_TYPES = (
|
|||
("cash", "Cash"),
|
||||
("cb", "CB"),
|
||||
("cheque", "Chèque"),
|
||||
("virement", "Virement"),
|
||||
("autre", "Autre"),
|
||||
)
|
||||
|
||||
|
@ -163,7 +164,7 @@ class Attribution(models.Model):
|
|||
given = models.BooleanField("Donnée", default=False)
|
||||
paid = models.BooleanField("Payée", default=False)
|
||||
paymenttype = models.CharField(
|
||||
"Moyen de paiement", max_length=6, choices=PAYMENT_TYPES, blank=True
|
||||
"Moyen de paiement", max_length=8, choices=PAYMENT_TYPES, blank=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -253,7 +254,11 @@ class ChoixSpectacle(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ("priority",)
|
||||
unique_together = (("participant", "spectacle"),)
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["participant", "spectacle"], name="unique_participation"
|
||||
)
|
||||
]
|
||||
verbose_name = "voeu"
|
||||
verbose_name_plural = "voeux"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>État des inscriptions BdA</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{% static 'vendor/jquery/jquery-ui.min.js' %}" ></script>
|
||||
|
|
|
@ -6,26 +6,24 @@ pour les spectacles suivants :
|
|||
- 1 place pour {{ place }}{% endfor %}
|
||||
|
||||
*Paiement*
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant et AVANT
|
||||
vendredi prochain, au bureau du COF pendant les heures de permanences (du lundi au vendredi
|
||||
entre 12h et 14h, et entre 18h et 20h). Des facilités de paiement sont bien
|
||||
évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement,
|
||||
ou bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas
|
||||
venir payer au bureau, merci de nous contacter par mail.
|
||||
Au burô :
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant, au bureau du COF pendant les heures de permanences (lundi, mardi, jeudi entre 12h et 14h et entre 18h30 et 19h30, mercredi entre 18h30 et 19h30, vendredi entre 12h et 14h). Les places sont à régler AVANT les représentations. Si vous êtes en vacances, vous pourrez venir les régler dès votre retour. Il est demandé à chacun·e de prendre garde à honorer l’ensemble des places qui lui sont attribuées et de s'engager de fait à payer la ou les place(s) qui lui sont attribuées.
|
||||
|
||||
Par virements :
|
||||
L'intégralité de ces places de spectacles est à régler dès maintenant par virements. Il vous sera demandé d'envoyer une confirmation de l'envoi de virement à bda@ens.fr.
|
||||
IBAN AEENS : FR76 4255 9100 0008 0263 8331 927
|
||||
Motif de virements : AVR25(ou MAI25)-tirageprintemps-NOM-prénom
|
||||
|
||||
Les places sont à régler AVANT les représentations. Il est demandé à chacun·e de prendre garde à honorer l’ensemble des places qui lui sont attribuées et de s'engager de fait à payer la ou les place(s) qui lui sont attribuées.
|
||||
Des facilités de paiement sont bien évidemment possibles : nous pouvons ne pas encaisser le chèque immédiatement, ou bien découper votre paiement en deux fois. Pour ceux qui ne pourraient pas venir payer au bureau, merci de nous contacter par mail.
|
||||
|
||||
*Mode de retrait des places*
|
||||
Au moment du paiement, certaines places vous seront remises directement,
|
||||
d'autres seront à récupérer au cours de l'année, d'autres encore seront
|
||||
nominatives et à retirer le soir même dans les théâtres correspondants.
|
||||
Pour chaque spectacle, vous recevrez un mail quelques jours avant la
|
||||
représentation vous indiquant le mode de retrait.
|
||||
|
||||
Nous vous rappelons que l'obtention de places du BdA vous engage à
|
||||
respecter les règles de fonctionnement :
|
||||
Au moment du paiement, certaines places vous seront remises directement, d'autres seront à récupérer au cours de l'année, d'autres encore seront nominatives et à retirer le soir même dans les théâtres correspondants. Pour chaque spectacle, vous recevrez un mail quelques jours avant la représentation vous indiquant le mode de retrait.
|
||||
Nous vous rappelons que l'obtention de places du BdA vous engage à respecter les règles de fonctionnement :
|
||||
https://bda.ens.fr/lequipe/charte-bda/
|
||||
Un système de revente des places via les mails BdA-revente est disponible
|
||||
directement sur votre compte GestioCOF.
|
||||
|
||||
Un système de revente des places via les mails BdA-revente est disponible directement sur votre compte GestioCOF. Pour pouvoir l'utiliser, il faut que vous ayez payé vos places en amont.
|
||||
|
||||
En vous souhaitant de très beaux spectacles tout au long de l'année,
|
||||
--
|
||||
Le Bureau des Arts
|
||||
Le Bureau des Arts
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>{{ spectacle }}</h2>
|
||||
|
|
|
@ -11,9 +11,19 @@
|
|||
<td>{{place.spectacle.date}}</td>
|
||||
<td>{% if place.double %}deux places{%else%}une place{% endif %}</td>
|
||||
<td>{% if place.spectacle.listing %}sur listing{% else %}place physique{% endif %}</td>
|
||||
<td>
|
||||
{% if place.unpaid == 0 %}
|
||||
Payé
|
||||
{% elif place.unpaid == 1 %}
|
||||
Une place à payer ({{place.unpaid_price|floatformat}}€)
|
||||
{% else %}
|
||||
Deux places à payer ({{place.unpaid_price|floatformat}}€)
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<h4 class="bda-prix">Reste à payer : {{ unpaid|floatformat }}€</h4>
|
||||
<h4 class="bda-prix">Total à payer : {{ total|floatformat }}€</h4>
|
||||
<br/>
|
||||
<p>Ne manque pas un spectacle avec le
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
|
||||
{%block realcontent %}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscription à une revente</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles%}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscriptions pour BdA-Revente</h2>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />
|
||||
|
|
42
bda/urls.py
42
bda/urls.py
|
@ -1,74 +1,80 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
|
||||
from bda import views
|
||||
from bda.views import SpectacleListView
|
||||
from gestioncof.decorators import buro_required
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
re_path(
|
||||
r"^inscription/(?P<tirage_id>\d+)$",
|
||||
views.inscription,
|
||||
name="bda-tirage-inscription",
|
||||
),
|
||||
url(r"^places/(?P<tirage_id>\d+)$", views.places, name="bda-places-attribuees"),
|
||||
url(r"^etat-places/(?P<tirage_id>\d+)$", views.etat_places, name="bda-etat-places"),
|
||||
url(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"),
|
||||
url(
|
||||
re_path(r"^places/(?P<tirage_id>\d+)$", views.places, name="bda-places-attribuees"),
|
||||
re_path(
|
||||
r"^etat-places/(?P<tirage_id>\d+)$", views.etat_places, name="bda-etat-places"
|
||||
),
|
||||
re_path(r"^tirage/(?P<tirage_id>\d+)$", views.tirage, name="bda-tirage"),
|
||||
re_path(
|
||||
r"^spectacles/(?P<tirage_id>\d+)$",
|
||||
buro_required(SpectacleListView.as_view()),
|
||||
name="bda-liste-spectacles",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$",
|
||||
views.spectacle,
|
||||
name="bda-spectacle",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^spectacles/unpaid/(?P<tirage_id>\d+)$",
|
||||
views.UnpaidParticipants.as_view(),
|
||||
name="bda-unpaid",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^spectacles/autocomplete$",
|
||||
views.spectacle_autocomplete,
|
||||
name="bda-spectacle-autocomplete",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^participants/autocomplete$",
|
||||
views.participant_autocomplete,
|
||||
name="bda-participant-autocomplete",
|
||||
),
|
||||
# Urls BdA-Revente
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<tirage_id>\d+)/manage$",
|
||||
views.revente_manage,
|
||||
name="bda-revente-manage",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<tirage_id>\d+)/subscribe$",
|
||||
views.revente_subscribe,
|
||||
name="bda-revente-subscribe",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<tirage_id>\d+)/tirages$",
|
||||
views.revente_tirages,
|
||||
name="bda-revente-tirages",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<spectacle_id>\d+)/buy$",
|
||||
views.revente_buy,
|
||||
name="bda-revente-buy",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<revente_id>\d+)/confirm$",
|
||||
views.revente_confirm,
|
||||
name="bda-revente-confirm",
|
||||
),
|
||||
url(
|
||||
re_path(
|
||||
r"^revente/(?P<tirage_id>\d+)/shotgun$",
|
||||
views.revente_shotgun,
|
||||
name="bda-revente-shotgun",
|
||||
),
|
||||
url(r"^mails-rappel/(?P<spectacle_id>\d+)$", views.send_rappel, name="bda-rappels"),
|
||||
url(r"^catalogue/(?P<request_type>[a-z]+)$", views.catalogue, name="bda-catalogue"),
|
||||
re_path(
|
||||
r"^mails-rappel/(?P<spectacle_id>\d+)$", views.send_rappel, name="bda-rappels"
|
||||
),
|
||||
re_path(
|
||||
r"^catalogue/(?P<request_type>[a-z]+)$", views.catalogue, name="bda-catalogue"
|
||||
),
|
||||
]
|
||||
|
|
10
bda/views.py
10
bda/views.py
|
@ -114,6 +114,7 @@ def places(request, tirage_id):
|
|||
"spectacle__date", "spectacle"
|
||||
).select_related("spectacle", "spectacle__location")
|
||||
total = sum(place.spectacle.price for place in places)
|
||||
unpaid = 0
|
||||
filtered_places = []
|
||||
places_dict = {}
|
||||
spectacles = []
|
||||
|
@ -124,6 +125,8 @@ def places(request, tirage_id):
|
|||
places_dict[place.spectacle].double = True
|
||||
else:
|
||||
place.double = False
|
||||
place.unpaid = 0
|
||||
place.unpaid_price = 0
|
||||
places_dict[place.spectacle] = place
|
||||
spectacles.append(place.spectacle)
|
||||
filtered_places.append(place)
|
||||
|
@ -132,6 +135,12 @@ def places(request, tirage_id):
|
|||
warning = True
|
||||
else:
|
||||
dates.append(date)
|
||||
|
||||
if not place.paid:
|
||||
unpaid += place.spectacle.price
|
||||
places_dict[place.spectacle].unpaid += 1
|
||||
places_dict[place.spectacle].unpaid_price += place.spectacle.price
|
||||
|
||||
# On prévient l'utilisateur s'il a deux places à la même date
|
||||
if warning:
|
||||
messages.warning(
|
||||
|
@ -147,6 +156,7 @@ def places(request, tirage_id):
|
|||
"places": filtered_places,
|
||||
"tirage": tirage,
|
||||
"total": total,
|
||||
"unpaid": unpaid,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default_app_config = "bds.apps.BdsConfig"
|
|
@ -1,5 +1,4 @@
|
|||
from django import apps as global_apps
|
||||
from django.apps import AppConfig
|
||||
from django.apps import AppConfig, apps as global_apps
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% load bulma_utils %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
|
|
@ -31,7 +31,7 @@ class TestHomeView(TestCase):
|
|||
user, backend="django.contrib.auth.backends.ModelBackend"
|
||||
)
|
||||
resp = self.client.get(reverse("bds:home"))
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
class TestRegistrationView(TestCase):
|
||||
|
@ -48,12 +48,12 @@ class TestRegistrationView(TestCase):
|
|||
# Logged-in but unprivileged GET
|
||||
client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
|
||||
resp = client.get(url)
|
||||
self.assertEquals(resp.status_code, 403)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
|
||||
# Burô user GET
|
||||
give_bds_buro_permissions(user)
|
||||
resp = client.get(url)
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@mock.patch("gestioncof.signals.messages")
|
||||
def test_get(self, mock_messages):
|
||||
|
@ -68,9 +68,9 @@ class TestRegistrationView(TestCase):
|
|||
# Logged-in but unprivileged GET
|
||||
client.force_login(user, backend="django.contrib.auth.backends.ModelBackend")
|
||||
resp = client.get(url)
|
||||
self.assertEquals(resp.status_code, 403)
|
||||
self.assertEqual(resp.status_code, 403)
|
||||
|
||||
# Burô user GET
|
||||
give_bds_buro_permissions(user)
|
||||
resp = client.get(url)
|
||||
self.assertEquals(resp.status_code, 200)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.urls import path
|
||||
|
||||
from bds import views
|
||||
from shared.views import SympaListView
|
||||
|
||||
app_name = "bds"
|
||||
urlpatterns = [
|
||||
|
@ -21,4 +22,10 @@ urlpatterns = [
|
|||
name="members.expired",
|
||||
),
|
||||
path("members/reset", views.ResetMembershipView.as_view(), name="members.reset"),
|
||||
# Sympa export view
|
||||
path(
|
||||
"sympa/members/",
|
||||
SympaListView.as_view(filters={"bds__is_member": True}),
|
||||
name="export.sympa",
|
||||
),
|
||||
]
|
||||
|
|
63
events/migrations/0005_auto_20220630_1239.py
Normal file
63
events/migrations/0005_auto_20220630_1239.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# Generated by Django 3.2.13 on 2022-06-30 10:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("events", "0004_unique_constraints"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="extrafield",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="extrafieldcontent",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="option",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="optionchoice",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="registration",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="extrafield",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("event", "name"), name="unique_extra_field"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="extrafieldcontent",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("field", "registration"), name="unique_extra_field_content"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="option",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("event", "name"), name="unique_event_option"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="optionchoice",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("option", "choice"), name="unique_option_choice"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="registration",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("event", "user"), name="unique_registration"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -72,9 +72,13 @@ class Option(models.Model):
|
|||
multi_choices = models.BooleanField(_("choix multiples"), default=False)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["event", "name"], name="unique_event_option"
|
||||
)
|
||||
]
|
||||
verbose_name = _("option d'événement")
|
||||
verbose_name_plural = _("options d'événement")
|
||||
unique_together = [["event", "name"]]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -87,9 +91,13 @@ class OptionChoice(models.Model):
|
|||
choice = models.CharField(_("choix"), max_length=200)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["option", "choice"], name="unique_option_choice"
|
||||
)
|
||||
]
|
||||
verbose_name = _("choix d'option d'événement")
|
||||
verbose_name_plural = _("choix d'option d'événement")
|
||||
unique_together = [["option", "choice"]]
|
||||
|
||||
def __str__(self):
|
||||
return self.choice
|
||||
|
@ -118,7 +126,9 @@ class ExtraField(models.Model):
|
|||
field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE)
|
||||
|
||||
class Meta:
|
||||
unique_together = [["event", "name"]]
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["event", "name"], name="unique_extra_field")
|
||||
]
|
||||
|
||||
|
||||
class ExtraFieldContent(models.Model):
|
||||
|
@ -137,9 +147,13 @@ class ExtraFieldContent(models.Model):
|
|||
)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["field", "registration"], name="unique_extra_field_content"
|
||||
)
|
||||
]
|
||||
verbose_name = _("contenu d'un champ événement supplémentaire")
|
||||
verbose_name_plural = _("contenus d'un champ événement supplémentaire")
|
||||
unique_together = [["field", "registration"]]
|
||||
|
||||
def __str__(self):
|
||||
max_length = 50
|
||||
|
@ -163,9 +177,13 @@ class Registration(models.Model):
|
|||
)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["event", "user"], name="unique_registration"
|
||||
)
|
||||
]
|
||||
verbose_name = _("inscription à un événement")
|
||||
verbose_name_plural = _("inscriptions à un événement")
|
||||
unique_together = [["event", "user"]]
|
||||
|
||||
def __str__(self):
|
||||
return "inscription de {} à {}".format(self.user, self.event)
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
"""
|
||||
ASGI entrypoint. Configures Django and then runs the application
|
||||
defined in the ASGI_APPLICATION setting.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from channels.asgi import get_channel_layer
|
||||
import django
|
||||
from channels.routing import get_default_application
|
||||
|
||||
if "DJANGO_SETTINGS_MODULE" not in os.environ:
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local")
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
django.setup()
|
||||
application = get_default_application()
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
from channels.routing import include
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from django.core.asgi import get_asgi_application
|
||||
from django.urls import path
|
||||
|
||||
routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")]
|
||||
from kfet.routing import KFRouter
|
||||
|
||||
application = ProtocolTypeRouter(
|
||||
{
|
||||
# WebSocket chat handler
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
[
|
||||
path("ws/k-fet", KFRouter),
|
||||
]
|
||||
)
|
||||
),
|
||||
"http": get_asgi_application(),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -67,8 +67,8 @@ INSTALLED_APPS = (
|
|||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail.core",
|
||||
"wagtail.contrib.modeladmin",
|
||||
"wagtail",
|
||||
# "wagtail.contrib.modeladmin",
|
||||
"wagtail.contrib.routable_page",
|
||||
"wagtailmenus",
|
||||
"modelcluster",
|
||||
|
@ -85,7 +85,6 @@ MIDDLEWARE = (
|
|||
+ MIDDLEWARE
|
||||
+ [
|
||||
"djconfig.middleware.DjConfigMiddleware",
|
||||
"wagtail.core.middleware.SiteMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
)
|
||||
|
@ -109,6 +108,8 @@ MEDIA_URL = "/gestion/media/"
|
|||
CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.ens.fr")
|
||||
|
||||
|
||||
ASGI_APPLICATION = "gestioasso.routing.application"
|
||||
|
||||
# ---
|
||||
# Auth-related stuff
|
||||
# ---
|
||||
|
@ -147,7 +148,7 @@ CACHES = {
|
|||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "asgi_redis.RedisChannelLayer",
|
||||
"BACKEND": "shared.channels.ChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [
|
||||
(
|
||||
|
@ -160,11 +161,9 @@ CHANNEL_LAYERS = {
|
|||
)
|
||||
]
|
||||
},
|
||||
"ROUTING": "gestioasso.routing.routing",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# ---
|
||||
# reCAPTCHA settings
|
||||
# https://github.com/praekelt/django-recaptcha
|
||||
|
@ -206,7 +205,7 @@ MAIL_DATA = {
|
|||
"REPLYTO": "cof@ens.fr",
|
||||
},
|
||||
"rappels": {"FROM": "Le BdA <bda@ens.fr>", "REPLYTO": "Le BdA <bda@ens.fr>"},
|
||||
"rappel_negatif": {
|
||||
"kfet": {
|
||||
"FROM": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
"REPLYTO": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
},
|
||||
|
|
|
@ -101,7 +101,7 @@ TEMPLATES = [
|
|||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": DBNAME,
|
||||
"USER": DBUSER,
|
||||
"PASSWORD": DBPASSWD,
|
||||
|
@ -111,6 +111,7 @@ DATABASES = {
|
|||
|
||||
SITE_ID = 1
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
# ---
|
||||
# Internationalization
|
||||
|
|
|
@ -27,6 +27,9 @@ ALLOWED_HOSTS = []
|
|||
DEBUG = True
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
SYMPA_PASSWORD = b"sympa"
|
||||
SYMPA_USERNAME = b"sympa"
|
||||
|
||||
if TESTING:
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
|
@ -47,8 +50,7 @@ CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"
|
|||
# Use the default in memory asgi backend for local development
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "asgiref.inmemory.ChannelLayer",
|
||||
"ROUTING": "gestioasso.routing.routing",
|
||||
"BACKEND": "channels.layers.InMemoryChannelLayer",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
197
gestioasso/settings_bds.py
Normal file
197
gestioasso/settings_bds.py
Normal file
|
@ -0,0 +1,197 @@
|
|||
"""
|
||||
Django settings for the gestioBDS project.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from loadcredential import Credentials
|
||||
|
||||
credentials = Credentials(env_prefix="GESTIOBDS_")
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = credentials["SECRET_KEY"]
|
||||
|
||||
# WARNING: don't run with debug turned on in production!
|
||||
DEBUG = credentials.get_json("DEBUG", False)
|
||||
|
||||
ALLOWED_HOSTS = credentials.get_json("ALLOWED_HOSTS", [])
|
||||
ADMINS = credentials.get_json("ADMINS", [])
|
||||
|
||||
SERVER_EMAIL = credentials.get("SERVER_EMAIL")
|
||||
EMAIL_HOST = credentials.get("EMAIL_HOST")
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
SYMPA_PASSWORD = credentials["SYMPA_PASSWORD"].encode()
|
||||
SYMPA_USERNAME = credentials["SYMPA_USERNAME"].encode()
|
||||
|
||||
|
||||
##
|
||||
# Installed Apps configuration
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"shared",
|
||||
# Must be before 'django.contrib.admin'.
|
||||
# https://django-autocomplete-light.readthedocs.io/en/master/install.html
|
||||
"dal",
|
||||
"dal_select2",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.sites",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.admindocs",
|
||||
"gestioasso.apps.IgnoreSrcStaticFilesConfig",
|
||||
"django_cas_ng",
|
||||
"bootstrapform",
|
||||
"widget_tweaks",
|
||||
"bds",
|
||||
"events",
|
||||
"clubs",
|
||||
"authens",
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# Middleware configuration
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# URL configuration
|
||||
|
||||
ROOT_URLCONF = "gestioasso.urls"
|
||||
|
||||
|
||||
##
|
||||
# Templates configuration
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"django.template.context_processors.i18n",
|
||||
"django.template.context_processors.media",
|
||||
"django.template.context_processors.static",
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# Database configuration
|
||||
|
||||
DATABASES = credentials.get_json(
|
||||
"DATABASES",
|
||||
default={
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": (BASE_DIR / "db.sqlite3"),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
CACHES = credentials.get_json(
|
||||
"CACHES",
|
||||
default={
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
CORS_ORIGIN_WHITELIST = credentials.get("CORS_ORIGIN_WHITELIST", [])
|
||||
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
|
||||
###
|
||||
# Staticfiles configuration
|
||||
|
||||
STATIC_ROOT = credentials["STATIC_ROOT"]
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = credentials.get("MEDIA_ROOT", (BASE_DIR / "media"))
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
|
||||
##
|
||||
# Authens and Authentication configuration
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"authens.backends.ENSCASBackend",
|
||||
"authens.backends.OldCASBackend",
|
||||
]
|
||||
|
||||
AUTHENS_USE_OLDCAS = False
|
||||
|
||||
LOGIN_URL = "authens:login"
|
||||
LOGIN_REDIRECT_URL = "bds:home"
|
||||
LOGOUT_REDIRECT_URL = "bds:home"
|
||||
|
||||
|
||||
# ---
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
# ---
|
||||
|
||||
LANGUAGE_CODE = "fr-fr"
|
||||
TIME_ZONE = "Europe/Paris"
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
LANGUAGES = (("fr", "Français"), ("en", "English"))
|
||||
FORMAT_MODULE_PATH = "gestioasso.locale"
|
||||
|
||||
##
|
||||
# Development configuration
|
||||
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
def show_toolbar(request):
|
||||
"""
|
||||
On active la debug-toolbar en mode développement local sauf :
|
||||
- dans l'admin où ça ne sert pas à grand chose;
|
||||
- si la variable d'environnement DJANGO_NO_DDT est à 1 → ça permet de la désactiver
|
||||
sans modifier ce fichier en exécutant `export DJANGO_NO_DDT=1` dans le terminal
|
||||
qui lance `./manage.py runserver`.
|
||||
|
||||
Autre side effect de cette fonction : on ne fait pas la vérification de INTERNAL_IPS
|
||||
que ferait la debug-toolbar par défaut, ce qui la fait fonctionner aussi à
|
||||
l'intérieur de Vagrant (comportement non testé depuis un moment…)
|
||||
"""
|
||||
|
||||
env_no_ddt = bool(os.environ.get("DJANGO_NO_DDT", None))
|
||||
return not (env_no_ddt or request.path.startswith("/admin/"))
|
||||
|
||||
##
|
||||
# Django Debug Toolbar configuration
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar}
|
||||
INSTALLED_APPS += ["debug_toolbar"]
|
||||
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
|
324
gestioasso/settings_cof.py
Normal file
324
gestioasso/settings_cof.py
Normal file
|
@ -0,0 +1,324 @@
|
|||
"""
|
||||
Django settings for the gestioCOF project.
|
||||
"""
|
||||
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from loadcredential import Credentials
|
||||
|
||||
credentials = Credentials(env_prefix="GESTIOCOF_")
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = credentials["SECRET_KEY"]
|
||||
|
||||
# WARNING: don't run with debug turned on in production!
|
||||
DEBUG = credentials.get_json("DEBUG", False)
|
||||
|
||||
ALLOWED_HOSTS = credentials.get_json("ALLOWED_HOSTS", [])
|
||||
ADMINS = credentials.get_json("ADMINS", [])
|
||||
|
||||
SERVER_EMAIL = credentials.get("SERVER_EMAIL")
|
||||
EMAIL_HOST = credentials.get("EMAIL_HOST")
|
||||
|
||||
LDAP_SERVER_URL = credentials.get("LDAP_SERVER_URL")
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
SYMPA_PASSWORD = credentials["SYMPA_PASSWORD"].encode()
|
||||
SYMPA_USERNAME = credentials["SYMPA_USERNAME"].encode()
|
||||
|
||||
|
||||
##
|
||||
# Installed Apps configuration
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"gestioncof",
|
||||
# Must be before django admin
|
||||
# https://github.com/infoportugal/wagtail-modeltranslation/issues/193
|
||||
"wagtail_modeltranslation",
|
||||
"wagtail_modeltranslation.makemigrations",
|
||||
"wagtail_modeltranslation.migrate",
|
||||
"modeltranslation",
|
||||
"shared",
|
||||
# Must be before 'django.contrib.admin'.
|
||||
# https://django-autocomplete-light.readthedocs.io/en/master/install.html
|
||||
"dal",
|
||||
"dal_select2",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.sites",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.admindocs",
|
||||
"gestioasso.apps.IgnoreSrcStaticFilesConfig",
|
||||
"django_cas_ng",
|
||||
"bootstrapform",
|
||||
"widget_tweaks",
|
||||
"bda",
|
||||
"petitscours",
|
||||
"hcaptcha",
|
||||
"kfet",
|
||||
"kfet.open",
|
||||
"channels",
|
||||
"djconfig",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.embeds",
|
||||
"wagtail.sites",
|
||||
"wagtail.users",
|
||||
"wagtail.snippets",
|
||||
"wagtail.documents",
|
||||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail",
|
||||
# "wagtail.contrib.modeladmin",
|
||||
"wagtail.contrib.routable_page",
|
||||
"wagtailmenus",
|
||||
"modelcluster",
|
||||
"taggit",
|
||||
"kfet.auth",
|
||||
"kfet.cms",
|
||||
"gestioncof.cms",
|
||||
"django_js_reverse",
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# Middleware configuration
|
||||
|
||||
MIDDLEWARE = [
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"djconfig.middleware.DjConfigMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# URL configuration
|
||||
|
||||
ROOT_URLCONF = "gestioasso.urls"
|
||||
|
||||
|
||||
##
|
||||
# Templates configuration
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"wagtailmenus.context_processors.wagtailmenus",
|
||||
"djconfig.context_processors.config",
|
||||
"gestioncof.shared.context_processor",
|
||||
"kfet.auth.context_processors.temporary_auth",
|
||||
"kfet.context_processors.config",
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"django.template.context_processors.i18n",
|
||||
"django.template.context_processors.media",
|
||||
"django.template.context_processors.static",
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
##
|
||||
# Wagtail configuration
|
||||
|
||||
WAGTAIL_SITE_NAME = "GestioCOF"
|
||||
WAGTAIL_ENABLE_UPDATE_CHECK = False
|
||||
TAGGIT_CASE_INSENSITIVE = True
|
||||
|
||||
##
|
||||
# Django-js-reverse settings
|
||||
|
||||
JS_REVERSE_JS_VAR_NAME = "django_urls"
|
||||
# Quand on aura namespace les urls...
|
||||
# JS_REVERSE_INCLUDE_ONLY_NAMESPACES = ['k-fet']
|
||||
|
||||
##
|
||||
# K-Fêt history configuration
|
||||
|
||||
# L'historique n'est accesible que d'aujourd'hui
|
||||
# à aujourd'hui - KFET_HISTORY_DATE_LIMIT
|
||||
KFET_HISTORY_DATE_LIMIT = timedelta(days=7)
|
||||
|
||||
# Limite plus longue pour les chefs/trez
|
||||
# (qui ont la permission kfet.access_old_history)
|
||||
KFET_HISTORY_LONG_DATE_LIMIT = timedelta(days=30)
|
||||
|
||||
# These accounts don't represent actual people and can be freely accessed
|
||||
# Identification based on trigrammes
|
||||
KFET_HISTORY_NO_DATE_LIMIT_TRIGRAMMES = ["LIQ", "#13"]
|
||||
KFET_HISTORY_NO_DATE_LIMIT = datetime(1794, 10, 30) # AKA the distant past
|
||||
|
||||
|
||||
##
|
||||
# Database configuration
|
||||
|
||||
DATABASES = credentials.get_json(
|
||||
"DATABASES",
|
||||
default={
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": (BASE_DIR / "db.sqlite3"),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
CACHES = credentials.get_json(
|
||||
"CACHES",
|
||||
default={
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
CHANNEL_LAYERS = credentials.get_json(
|
||||
"CHANNEL_LAYERS",
|
||||
default={
|
||||
"default": {
|
||||
"BACKEND": "channels.layers.InMemoryChannelLayer",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
ASGI_APPLICATION = "gestioasso.routing.application"
|
||||
|
||||
CORS_ALLOWED_ORIGINS = credentials.get("CORS_ALLOWED_ORIGINS", [])
|
||||
CSRF_TRUSTED_ORIGINS = [f"https://{host}" for host in ALLOWED_HOSTS]
|
||||
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
|
||||
###
|
||||
# Staticfiles configuration
|
||||
|
||||
STATIC_ROOT = credentials["STATIC_ROOT"]
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = credentials.get("MEDIA_ROOT", (BASE_DIR / "media"))
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
|
||||
##
|
||||
# Authentication configuration
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"kfet.auth.backends.BlockFrozenAccountBackend", # Must be in first
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"gestioncof.shared.COFCASBackend",
|
||||
"kfet.auth.backends.GenericBackend",
|
||||
]
|
||||
|
||||
LOGIN_URL = "cof-login"
|
||||
LOGIN_REDIRECT_URL = reverse_lazy("home")
|
||||
|
||||
# FIXME: Switch to authens
|
||||
CAS_SERVER_URL = "https://cas.eleves.ens.fr/"
|
||||
CAS_VERSION = "2"
|
||||
CAS_LOGIN_MSG = None
|
||||
CAS_IGNORE_REFERER = True
|
||||
CAS_REDIRECT_URL = "/"
|
||||
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
|
||||
|
||||
|
||||
##
|
||||
# h-captcha configuration
|
||||
|
||||
HCAPTCHA_SITEKEY = credentials["HCAPTCHA_SITEKEY"]
|
||||
HCAPTCHA_SECRET = credentials["HCAPTCHA_SECRET"]
|
||||
|
||||
##
|
||||
# K-Fêt token for the openness indicator
|
||||
|
||||
KFETOPEN_TOKEN = credentials["KFETOPEN_TOKEN"]
|
||||
|
||||
|
||||
##
|
||||
# Mail configuration
|
||||
|
||||
MAIL_DATA = {
|
||||
"petits_cours": {
|
||||
"FROM": "Le COF <cof@ens.fr>",
|
||||
"BCC": "archivescof@gmail.com",
|
||||
"REPLYTO": "cof@ens.fr",
|
||||
},
|
||||
"rappels": {
|
||||
"FROM": "Le BdA <bda@ens.fr>",
|
||||
"REPLYTO": "Le BdA <bda@ens.fr>",
|
||||
},
|
||||
"kfet": {
|
||||
"FROM": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
"REPLYTO": "La K-Fêt <chefs-k-fet@ens.fr>",
|
||||
},
|
||||
"revente": {
|
||||
"FROM": "BdA-Revente <bda-revente@ens.fr>",
|
||||
"REPLYTO": "BdA-Revente <bda-revente@ens.fr>",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# ---
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
# ---
|
||||
|
||||
LANGUAGE_CODE = "fr-fr"
|
||||
TIME_ZONE = "Europe/Paris"
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
LANGUAGES = (("fr", "Français"), ("en", "English"))
|
||||
FORMAT_MODULE_PATH = "gestioasso.locale"
|
||||
|
||||
##
|
||||
# Development configuration
|
||||
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
def show_toolbar(request):
|
||||
"""
|
||||
On active la debug-toolbar en mode développement local sauf :
|
||||
- dans l'admin où ça ne sert pas à grand chose;
|
||||
- si la variable d'environnement DJANGO_NO_DDT est à 1 → ça permet de la désactiver
|
||||
sans modifier ce fichier en exécutant `export DJANGO_NO_DDT=1` dans le terminal
|
||||
qui lance `./manage.py runserver`.
|
||||
|
||||
Autre side effect de cette fonction : on ne fait pas la vérification de INTERNAL_IPS
|
||||
que ferait la debug-toolbar par défaut, ce qui la fait fonctionner aussi à
|
||||
l'intérieur de Vagrant (comportement non testé depuis un moment…)
|
||||
"""
|
||||
|
||||
env_no_ddt = bool(os.environ.get("DJANGO_NO_DDT", None))
|
||||
return not (env_no_ddt or request.path.startswith("/admin/"))
|
||||
|
||||
##
|
||||
# Django Debug Toolbar configuration
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar}
|
||||
INSTALLED_APPS += ["debug_toolbar"]
|
||||
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
|
|
@ -1,6 +1,7 @@
|
|||
"""
|
||||
Fichier principal de configuration des urls du projet GestioCOF
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from django.conf.urls.static import static
|
||||
|
@ -56,12 +57,13 @@ if settings.DEBUG:
|
|||
# Si on est en production, MEDIA_ROOT est servi par Apache.
|
||||
# Il faut dire à Django de servir MEDIA_ROOT lui-même en développement.
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
|
||||
# Wagtail URLs (wagtail.core urls must be last, as catch-all)
|
||||
if "wagtail.core" in settings.INSTALLED_APPS:
|
||||
# Wagtail URLs (wagtail urls must be last, as catch-all)
|
||||
if "wagtail" in settings.INSTALLED_APPS:
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.core import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
urlpatterns += [
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default_app_config = "gestioncof.apps.GestioncofConfig"
|
|
@ -6,7 +6,7 @@ from django.contrib.auth.models import Group, Permission, User
|
|||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from gestioncof.models import (
|
||||
Club,
|
||||
|
@ -130,15 +130,33 @@ class UserProfileAdmin(UserAdmin):
|
|||
is_buro.short_description = "Membre du Buro"
|
||||
is_buro.boolean = True
|
||||
|
||||
def is_chef(self, obj):
|
||||
try:
|
||||
return obj.profile.is_chef
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_chef.short_description = "Chef K-Fêt"
|
||||
is_chef.boolean = True
|
||||
|
||||
def is_cof(self, obj):
|
||||
try:
|
||||
return obj.profile.is_cof
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_cof.short_description = "Membre du COF"
|
||||
is_cof.short_description = "Membre COF"
|
||||
is_cof.boolean = True
|
||||
|
||||
def is_kfet(self, obj):
|
||||
try:
|
||||
return obj.profile.is_kfet
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_kfet.short_description = "Membre K-Fêt"
|
||||
is_kfet.boolean = True
|
||||
|
||||
list_display = UserAdmin.list_display + (
|
||||
"profile_phone",
|
||||
"profile_occupation",
|
||||
|
@ -146,7 +164,9 @@ class UserProfileAdmin(UserAdmin):
|
|||
"profile_mailing_bda",
|
||||
"profile_mailing_bda_revente",
|
||||
"is_cof",
|
||||
"is_kfet",
|
||||
"is_buro",
|
||||
"is_chef",
|
||||
)
|
||||
list_display_links = ("username", "email", "first_name", "last_name")
|
||||
list_filter = UserAdmin.list_filter + (
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default_app_config = "gestioncof.cms.apps.COFCMSAppConfig"
|
|
@ -3,9 +3,9 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.blocks
|
||||
import wagtail.contrib.routable_page.models
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.fields
|
||||
import wagtail.images.blocks
|
||||
from django.db import migrations, models
|
||||
|
||||
|
@ -72,18 +72,14 @@ class Migration(migrations.Migration):
|
|||
blank=True, null=True, verbose_name="Description rapide"
|
||||
),
|
||||
),
|
||||
("body", wagtail.core.fields.RichTextField(verbose_name="Contenu")),
|
||||
("body", wagtail.fields.RichTextField(verbose_name="Contenu")),
|
||||
(
|
||||
"body_fr",
|
||||
wagtail.core.fields.RichTextField(
|
||||
null=True, verbose_name="Contenu"
|
||||
),
|
||||
wagtail.fields.RichTextField(null=True, verbose_name="Contenu"),
|
||||
),
|
||||
(
|
||||
"body_en",
|
||||
wagtail.core.fields.RichTextField(
|
||||
null=True, verbose_name="Contenu"
|
||||
),
|
||||
wagtail.fields.RichTextField(null=True, verbose_name="Contenu"),
|
||||
),
|
||||
(
|
||||
"is_event",
|
||||
|
@ -138,46 +134,40 @@ class Migration(migrations.Migration):
|
|||
to="wagtailcore.Page",
|
||||
),
|
||||
),
|
||||
("body", wagtail.core.fields.RichTextField(verbose_name="Description")),
|
||||
("body", wagtail.fields.RichTextField(verbose_name="Description")),
|
||||
(
|
||||
"body_fr",
|
||||
wagtail.core.fields.RichTextField(
|
||||
null=True, verbose_name="Description"
|
||||
),
|
||||
wagtail.fields.RichTextField(null=True, verbose_name="Description"),
|
||||
),
|
||||
(
|
||||
"body_en",
|
||||
wagtail.core.fields.RichTextField(
|
||||
null=True, verbose_name="Description"
|
||||
),
|
||||
wagtail.fields.RichTextField(null=True, verbose_name="Description"),
|
||||
),
|
||||
(
|
||||
"links",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(required=True),
|
||||
wagtail.blocks.URLBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(
|
||||
required=True
|
||||
),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -186,31 +176,29 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"links_fr",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(required=True),
|
||||
wagtail.blocks.URLBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(
|
||||
required=True
|
||||
),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -220,31 +208,29 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"links_en",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(required=True),
|
||||
wagtail.blocks.URLBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(
|
||||
required=True
|
||||
),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -286,17 +272,17 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"introduction",
|
||||
wagtail.core.fields.RichTextField(verbose_name="Introduction"),
|
||||
wagtail.fields.RichTextField(verbose_name="Introduction"),
|
||||
),
|
||||
(
|
||||
"introduction_fr",
|
||||
wagtail.core.fields.RichTextField(
|
||||
wagtail.fields.RichTextField(
|
||||
null=True, verbose_name="Introduction"
|
||||
),
|
||||
),
|
||||
(
|
||||
"introduction_en",
|
||||
wagtail.core.fields.RichTextField(
|
||||
wagtail.fields.RichTextField(
|
||||
null=True, verbose_name="Introduction"
|
||||
),
|
||||
),
|
||||
|
@ -329,27 +315,27 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"body",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"heading",
|
||||
wagtail.core.blocks.CharBlock(classname="full title"),
|
||||
wagtail.blocks.CharBlock(classname="full title"),
|
||||
),
|
||||
("paragraph", wagtail.core.blocks.RichTextBlock()),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(
|
||||
wagtail.blocks.URLBlock(
|
||||
"Adresse de la page"
|
||||
),
|
||||
),
|
||||
(
|
||||
"height",
|
||||
wagtail.core.blocks.CharBlock(
|
||||
wagtail.blocks.CharBlock(
|
||||
"Hauteur (en pixels)"
|
||||
),
|
||||
),
|
||||
|
@ -361,27 +347,27 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"body_fr",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"heading",
|
||||
wagtail.core.blocks.CharBlock(classname="full title"),
|
||||
wagtail.blocks.CharBlock(classname="full title"),
|
||||
),
|
||||
("paragraph", wagtail.core.blocks.RichTextBlock()),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(
|
||||
wagtail.blocks.URLBlock(
|
||||
"Adresse de la page"
|
||||
),
|
||||
),
|
||||
(
|
||||
"height",
|
||||
wagtail.core.blocks.CharBlock(
|
||||
wagtail.blocks.CharBlock(
|
||||
"Hauteur (en pixels)"
|
||||
),
|
||||
),
|
||||
|
@ -394,27 +380,27 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"body_en",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"heading",
|
||||
wagtail.core.blocks.CharBlock(classname="full title"),
|
||||
wagtail.blocks.CharBlock(classname="full title"),
|
||||
),
|
||||
("paragraph", wagtail.core.blocks.RichTextBlock()),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"url",
|
||||
wagtail.core.blocks.URLBlock(
|
||||
wagtail.blocks.URLBlock(
|
||||
"Adresse de la page"
|
||||
),
|
||||
),
|
||||
(
|
||||
"height",
|
||||
wagtail.core.blocks.CharBlock(
|
||||
wagtail.blocks.CharBlock(
|
||||
"Hauteur (en pixels)"
|
||||
),
|
||||
),
|
||||
|
@ -448,17 +434,17 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"introduction",
|
||||
wagtail.core.fields.RichTextField(verbose_name="Introduction"),
|
||||
wagtail.fields.RichTextField(verbose_name="Introduction"),
|
||||
),
|
||||
(
|
||||
"introduction_fr",
|
||||
wagtail.core.fields.RichTextField(
|
||||
wagtail.fields.RichTextField(
|
||||
null=True, verbose_name="Introduction"
|
||||
),
|
||||
),
|
||||
(
|
||||
"introduction_en",
|
||||
wagtail.core.fields.RichTextField(
|
||||
wagtail.fields.RichTextField(
|
||||
null=True, verbose_name="Introduction"
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Generated by Django 2.2.8 on 2019-12-20 16:22
|
||||
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -14,26 +14,26 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -44,26 +44,26 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_en",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -75,26 +75,26 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_fr",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Generated by Django 2.2.15 on 2020-08-29 21:14
|
||||
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -14,35 +14,35 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.core.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.core.blocks.CharBlock(required=True)),
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -53,35 +53,35 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_en",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.core.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.core.blocks.CharBlock(required=True)),
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -93,35 +93,35 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_fr",
|
||||
field=wagtail.core.fields.StreamField(
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.core.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"email",
|
||||
wagtail.core.blocks.EmailBlock(required=True),
|
||||
wagtail.blocks.EmailBlock(required=True),
|
||||
),
|
||||
("texte", wagtail.core.blocks.CharBlock()),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.core.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.core.blocks.CharBlock(required=True)),
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-19 12:27
|
||||
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
import wagtail.images.blocks
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("cofcms", "0004_auto_20200829_2314"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("email", wagtail.blocks.EmailBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_en",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("email", wagtail.blocks.EmailBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
null=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofdirectoryentrypage",
|
||||
name="links_fr",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"lien",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"contact",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("email", wagtail.blocks.EmailBlock(required=True)),
|
||||
("texte", wagtail.blocks.CharBlock()),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"info",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("nom", wagtail.blocks.CharBlock(required=False)),
|
||||
("texte", wagtail.blocks.CharBlock(required=True)),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
null=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofpage",
|
||||
name="body",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
("heading", wagtail.blocks.CharBlock(form_classname="full title")),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock("Adresse de la page")),
|
||||
(
|
||||
"height",
|
||||
wagtail.blocks.CharBlock("Hauteur (en pixels)"),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofpage",
|
||||
name="body_en",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
("heading", wagtail.blocks.CharBlock(form_classname="full title")),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock("Adresse de la page")),
|
||||
(
|
||||
"height",
|
||||
wagtail.blocks.CharBlock("Hauteur (en pixels)"),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
null=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofpage",
|
||||
name="body_fr",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
("heading", wagtail.blocks.CharBlock(form_classname="full title")),
|
||||
("paragraph", wagtail.blocks.RichTextBlock()),
|
||||
("image", wagtail.images.blocks.ImageChooserBlock()),
|
||||
(
|
||||
"iframe",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("url", wagtail.blocks.URLBlock("Adresse de la page")),
|
||||
(
|
||||
"height",
|
||||
wagtail.blocks.CharBlock("Hauteur (en pixels)"),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
null=True,
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,12 +1,11 @@
|
|||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
from django.db import models
|
||||
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
|
||||
from wagtail import blocks
|
||||
from wagtail.admin.panels import FieldPanel
|
||||
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
||||
from wagtail.core import blocks
|
||||
from wagtail.core.fields import RichTextField, StreamField
|
||||
from wagtail.core.models import Page
|
||||
from wagtail.fields import RichTextField, StreamField
|
||||
from wagtail.images.blocks import ImageChooserBlock
|
||||
from wagtail.images.edit_handlers import ImageChooserPanel
|
||||
from wagtail.models import Page
|
||||
|
||||
|
||||
# Page pouvant afficher des actualités
|
||||
|
@ -69,10 +68,11 @@ class COFPage(Page):
|
|||
("paragraph", blocks.RichTextBlock()),
|
||||
("image", ImageChooserBlock()),
|
||||
("iframe", IFrameBlock()),
|
||||
]
|
||||
],
|
||||
use_json_field=True,
|
||||
)
|
||||
|
||||
content_panels = Page.content_panels + [StreamFieldPanel("body")]
|
||||
content_panels = Page.content_panels + [FieldPanel("body")]
|
||||
|
||||
subpage_types = ["COFDirectoryPage", "COFPage"]
|
||||
parent_page_types = ["COFPage", "COFRootPage"]
|
||||
|
@ -127,7 +127,7 @@ class COFActuPage(RoutablePageMixin, Page):
|
|||
all_day = models.BooleanField("Toute la journée", default=False, blank=True)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
ImageChooserPanel("image"),
|
||||
FieldPanel("image"),
|
||||
FieldPanel("chapo"),
|
||||
FieldPanel("body", classname="full"),
|
||||
FieldPanel("is_event"),
|
||||
|
@ -204,6 +204,7 @@ class COFDirectoryEntryPage(Page):
|
|||
),
|
||||
],
|
||||
blank=True,
|
||||
use_json_field=True,
|
||||
)
|
||||
|
||||
image = models.ForeignKey(
|
||||
|
@ -216,9 +217,9 @@ class COFDirectoryEntryPage(Page):
|
|||
)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
ImageChooserPanel("image"),
|
||||
FieldPanel("image"),
|
||||
FieldPanel("body", classname="full"),
|
||||
StreamFieldPanel("links"),
|
||||
FieldPanel("links"),
|
||||
]
|
||||
|
||||
subpage_types = []
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
|
||||
<section class="actulist">
|
||||
{% if actus.has_previous %}
|
||||
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
|
||||
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
|
||||
{% endif %}
|
||||
{% if actus.has_next %}
|
||||
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
|
||||
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% for actu in page.actus %}
|
||||
|
@ -44,10 +44,10 @@
|
|||
{% endfor %}
|
||||
|
||||
{% if actus.has_previous %}
|
||||
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
|
||||
<a class="block prev-actus" href="?page={{ actus.previous_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus récentes" %}</a>
|
||||
{% endif %}
|
||||
{% if actus.has_next %}
|
||||
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% ifnotequal key 'page' %}&{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
|
||||
<a class="block next-actus" href="?page={{ actus.next_page_number }}{% for key,value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,7 +2,7 @@ from datetime import date, timedelta
|
|||
|
||||
from django import template
|
||||
from django.utils import formats, timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from ..models import COFActuPage, COFRootPage
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
from django.shortcuts import render
|
||||
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||
|
||||
from gestioncof.cms.forms import CaptchaForm
|
||||
|
||||
|
||||
@xframe_options_sameorigin
|
||||
def raw_calendar_view(request, year, month):
|
||||
return render(request, "cofcms/calendar_raw.html", {"month": month, "year": year})
|
||||
|
||||
|
||||
@xframe_options_sameorigin
|
||||
def sympa_captcha_form_view(request):
|
||||
if request.method == "POST":
|
||||
form = CaptchaForm(request.POST)
|
||||
|
|
|
@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def cof_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du COF.
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre COF.
|
||||
|
||||
- Si l'utilisteur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
|
@ -33,6 +33,31 @@ def cof_required(view_func):
|
|||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def kfet_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre K-Fêt.
|
||||
|
||||
- Si l'utilisteur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
- Si l'utilisateur est connecté mais pas membre K-Fêt, il obtient une
|
||||
page d'erreur lui demandant de s'inscrire à la K-Fêt
|
||||
"""
|
||||
|
||||
def is_kfet(user):
|
||||
try:
|
||||
return user.profile.is_cof or user.profile.is_kfet
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if is_kfet(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return render(request, "kfet-denied.html", status=403)
|
||||
|
||||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def buro_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du burô.
|
||||
|
||||
|
@ -58,6 +83,33 @@ def buro_required(view_func):
|
|||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def chef_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du burô ou chef K-fêt.
|
||||
|
||||
- Si l'utilisateur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
- Si l'utilisateur est connecté mais pas membre du burô ou chef, il obtient une
|
||||
page d'erreur 403 Forbidden
|
||||
"""
|
||||
|
||||
def is_chef(user):
|
||||
try:
|
||||
return user.profile.is_chef or user.profile.is_buro
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if is_chef(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return render(
|
||||
request, "buro-denied.html", status=403
|
||||
) # TODO: reservé au burô ou au chef
|
||||
|
||||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
class CofRequiredMixin(PermissionRequiredMixin):
|
||||
def has_permission(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
|
@ -79,3 +131,18 @@ class BuroRequiredMixin(PermissionRequiredMixin):
|
|||
"L'utilisateur %s n'a pas de profil !", self.request.user.username
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
class ChefRequiredMixin(PermissionRequiredMixin):
|
||||
def has_permission(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
try:
|
||||
return (
|
||||
self.request.user.profile.is_chef or self.request.user.profile.is_buro
|
||||
)
|
||||
except AttributeError:
|
||||
logger.error(
|
||||
"L'utilisateur %s n'a pas de profil !", self.request.user.username
|
||||
)
|
||||
return False
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model
|
|||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.forms.formsets import BaseFormSet, formset_factory
|
||||
from django.forms.widgets import CheckboxSelectMultiple, RadioSelect
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from djconfig.forms import ConfigForm
|
||||
|
||||
from bda.models import Spectacle
|
||||
|
@ -276,12 +276,15 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
self.fields["mailing_bda_revente"].initial = True
|
||||
self.fields["mailing_unernestaparis"].initial = True
|
||||
|
||||
self.fields.keyOrder = [
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = [
|
||||
"login_clipper",
|
||||
"phone",
|
||||
"occupation",
|
||||
"departement",
|
||||
"is_cof",
|
||||
"is_kfet",
|
||||
"type_cotiz",
|
||||
"mailing_cof",
|
||||
"mailing_bda",
|
||||
|
@ -290,21 +293,18 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
"comments",
|
||||
]
|
||||
|
||||
|
||||
class RegistrationKFProfileForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = (
|
||||
fields = [
|
||||
"login_clipper",
|
||||
"phone",
|
||||
"occupation",
|
||||
"departement",
|
||||
"is_cof",
|
||||
"type_cotiz",
|
||||
"mailing_cof",
|
||||
"mailing_bda",
|
||||
"mailing_bda_revente",
|
||||
"mailing_unernestaparis",
|
||||
"is_kfet",
|
||||
"comments",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
STATUS_CHOICES = (
|
||||
|
@ -458,3 +458,20 @@ class GestioncofConfigForm(ConfigForm):
|
|||
max_length=2048,
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
||||
# ----
|
||||
# Formulaire pour les adhésions self-service
|
||||
# ----
|
||||
|
||||
|
||||
class SubscribForm(forms.Form):
|
||||
accept_ri = forms.BooleanField(
|
||||
label="Lu et accepte le réglement intérieur de l'AEENS (COF).", required=True
|
||||
)
|
||||
accept_status = forms.BooleanField(
|
||||
label="Lu et accepte les status de l'AEENS (COF).", required=True
|
||||
)
|
||||
accept_charte_kf = forms.BooleanField(
|
||||
label="Lu et accepte la charte de la K-Fêt.", required=True
|
||||
)
|
||||
|
|
43
gestioncof/migrations/0019_auto_20220630_1241.py
Normal file
43
gestioncof/migrations/0019_auto_20220630_1241.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Generated by Django 3.2.13 on 2022-06-30 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0018_petitscours_email"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="eventregistration",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="petitcoursability",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="surveyanswer",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="eventregistration",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("user", "event"), name="unique_event_registration"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="petitcoursability",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("user", "niveau", "matiere"), name="unique_competence_level"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="surveyanswer",
|
||||
constraint=models.UniqueConstraint(
|
||||
fields=("user", "survey"), name="unique_survey_answer"
|
||||
),
|
||||
),
|
||||
]
|
13
gestioncof/migrations/0020_merge_20241218_2240.py
Normal file
13
gestioncof/migrations/0020_merge_20241218_2240.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Generated by Django 3.2.25 on 2024-12-18 21:40
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0019_auto_20220630_1241"),
|
||||
("gestioncof", "0019_cofprofile_date_adhesion"),
|
||||
]
|
||||
|
||||
operations = []
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.16 on 2024-12-24 10:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0020_merge_20241218_2240"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="is_kfet",
|
||||
field=models.BooleanField(default=False, verbose_name="Membre K-Fêt"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofprofile",
|
||||
name="is_cof",
|
||||
field=models.BooleanField(default=False, verbose_name="Membre COF"),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.16 on 2024-12-30 14:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0021_cofprofile_is_kfet_alter_cofprofile_is_cof"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="cofprofile",
|
||||
name="date_adhesion",
|
||||
field=models.DateField(
|
||||
blank=True, null=True, verbose_name="Date d'adhésion COF"
|
||||
),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="cofprofile",
|
||||
old_name="date_adhesion",
|
||||
new_name="date_adhesion_cof",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="date_adhesion_kfet",
|
||||
field=models.DateField(
|
||||
blank=True, null=True, verbose_name="Date d'adhésion K-Fêt"
|
||||
),
|
||||
),
|
||||
]
|
18
gestioncof/migrations/0023_cofprofile_is_chef.py
Normal file
18
gestioncof/migrations/0023_cofprofile_is_chef.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-21 10:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0022_rename_cofprofile_date_adhesion_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="is_chef",
|
||||
field=models.BooleanField(default=False, verbose_name="Chef K-Fêt"),
|
||||
),
|
||||
]
|
|
@ -1,8 +1,14 @@
|
|||
from datetime import date
|
||||
from smtplib import SMTPRecipientsRefused
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.mail import send_mail
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template import loader
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bda.models import Spectacle
|
||||
from shared.utils import choices_length
|
||||
|
@ -49,8 +55,12 @@ class CofProfile(models.Model):
|
|||
login_clipper = models.CharField(
|
||||
"Login clipper", max_length=32, blank=True, unique=True, null=True
|
||||
)
|
||||
is_cof = models.BooleanField("Membre du COF", default=False)
|
||||
date_adhesion = models.DateField("Date d'adhésion", blank=True, null=True)
|
||||
is_cof = models.BooleanField("Membre COF", default=False)
|
||||
is_kfet = models.BooleanField("Membre K-Fêt", default=False)
|
||||
date_adhesion_cof = models.DateField("Date d'adhésion COF", blank=True, null=True)
|
||||
date_adhesion_kfet = models.DateField(
|
||||
"Date d'adhésion K-Fêt", blank=True, null=True
|
||||
)
|
||||
phone = models.CharField("Téléphone", max_length=20, blank=True)
|
||||
occupation = models.CharField(
|
||||
_("Occupation"),
|
||||
|
@ -75,6 +85,7 @@ class CofProfile(models.Model):
|
|||
)
|
||||
comments = models.TextField("Commentaires visibles par l'utilisateur", blank=True)
|
||||
is_buro = models.BooleanField("Membre du Burô", default=False)
|
||||
is_chef = models.BooleanField("Chef K-Fêt", default=False)
|
||||
petits_cours_accept = models.BooleanField(
|
||||
"Recevoir des petits cours", default=False
|
||||
)
|
||||
|
@ -89,6 +100,46 @@ class CofProfile(models.Model):
|
|||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
def make_adh_cof(self, request, was_cof):
|
||||
if self.is_cof and not was_cof:
|
||||
notify_new_member(request, self.user)
|
||||
self.date_adhesion_cof = date.today()
|
||||
self.save()
|
||||
|
||||
def make_adh_kfet(self, request, was_kfet):
|
||||
if self.is_kfet and not was_kfet:
|
||||
notify_new_member(request, self.user)
|
||||
self.date_adhesion_kfet = date.today()
|
||||
self.save()
|
||||
|
||||
|
||||
def notify_new_member(request, member: User):
|
||||
if not member.email:
|
||||
messages.warning(
|
||||
request,
|
||||
"GestioCOF n'a pas d'adresse mail pour {}, ".format(member)
|
||||
+ "aucun email de bienvenue n'a été envoyé",
|
||||
)
|
||||
return
|
||||
|
||||
# Try to send a welcome email and report SMTP errors
|
||||
try:
|
||||
send_mail(
|
||||
"Bienvenue au COF",
|
||||
loader.render_to_string(
|
||||
"gestioncof/mails/welcome.txt", context={"member": member}
|
||||
),
|
||||
"cof@ens.fr",
|
||||
[member.email],
|
||||
)
|
||||
except SMTPRecipientsRefused:
|
||||
messages.error(
|
||||
request,
|
||||
"Error lors de l'envoi de l'email de bienvenue à {} ({})".format(
|
||||
member, member.email
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
|
@ -194,8 +245,12 @@ class EventRegistration(models.Model):
|
|||
paid = models.BooleanField("A payé", default=False)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["user", "event"], name="unique_event_registration"
|
||||
)
|
||||
]
|
||||
verbose_name = "Inscription"
|
||||
unique_together = ("user", "event")
|
||||
|
||||
def __str__(self):
|
||||
return "Inscription de {} à {}".format(self.user, self.event.title)
|
||||
|
@ -247,8 +302,12 @@ class SurveyAnswer(models.Model):
|
|||
answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by")
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["user", "survey"], name="unique_survey_answer"
|
||||
)
|
||||
]
|
||||
verbose_name = "Réponses"
|
||||
unique_together = ("user", "survey")
|
||||
|
||||
def __str__(self):
|
||||
return "Réponse de %s sondage %s" % (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_cas_ng.signals import cas_user_authenticated
|
||||
|
||||
|
||||
|
|
|
@ -701,6 +701,9 @@ header a:active {
|
|||
.user-is-cof {
|
||||
color : #ADE297;
|
||||
}
|
||||
.user-is-kfet {
|
||||
color : #FF8C00;
|
||||
}
|
||||
.user-is-not-cof {
|
||||
color: #EE8585;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Section réservée aux membres du COF -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
<h2>Section réservée aux membres COF -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
{% endblock %}
|
||||
</a>
|
||||
<div class="secondary">
|
||||
{% if user.is_authenticated %}
|
||||
<span class="hidden-xxs"> | </span>
|
||||
<span><a href="{% url "cof-logout" %}">Se déconnecter <span class="glyphicon glyphicon-log-out"></span></a></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h2 class="member-status">{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
||||
<h2 class="member-status">{%if user.is_authenticated %}{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% endif %}{% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% elif user.profile.is_kfet %}<tt class="user-is-kfet">membre K-Fêt{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
||||
</div><!-- /.container -->
|
||||
</header>
|
||||
|
||||
|
|
19
gestioncof/templates/gestioncof/carte_kf.html
Normal file
19
gestioncof/templates/gestioncof/carte_kf.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block page_size %}col-sm-8{%endblock%}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Pass K-Fêt</h2>
|
||||
|
||||
<center style="font-size: 20pt;">
|
||||
<p>Profil de {{ user.first_name }} {{ user.last_name }}</p>
|
||||
|
||||
{% if user.profile.is_cof %}
|
||||
<p>Membre COF depuis le {{ user.profile.date_adhesion_cof }}</p>
|
||||
{% else %}
|
||||
<p>Membre K-Fêt depuis le {{ user.profile.date_adhesion_kfet }}</p>
|
||||
{% endif %}
|
||||
</center>
|
||||
|
||||
{% endblock %}
|
|
@ -8,7 +8,7 @@
|
|||
<div class="container hidden-xs espace"></div>
|
||||
<div class="container">
|
||||
<div class="home-menu row">
|
||||
<div class="{% if user.profile.is_buro %}col-sm-6 {% else %}col-sm-8 col-sm-offset-2 col-xs-12 {%endif%}normal-user-hm">
|
||||
<div class="{% if user.profile.is_buro or user.profile.is_chef %}col-sm-6 {% else %}col-sm-8 col-sm-offset-2 col-xs-12 {%endif%}normal-user-hm">
|
||||
{% if open_surveys %}
|
||||
<h3 class="block-title">Sondages en cours<span class="pull-right glyphicon glyphicon-stats"></span></h3>
|
||||
<div class="hm-block">
|
||||
|
@ -50,7 +50,10 @@
|
|||
<ul>
|
||||
{# TODO: Since Django 1.9, we can store result with "as", allowing proper value management (if None) #}
|
||||
<li><a href="{% slugurl "k-fet" %}">Page d'accueil</a></li>
|
||||
<li><a href="https://cof.ens.fr/gestion/k-fet/le-calendrier/">Calendrier</a></li>
|
||||
{% if user.profile.is_cof or user.profile.is_kfet %}
|
||||
<li><a href="{% url "profile.carte" %}">Carte K-Fêt</a></li>
|
||||
{% endif %}
|
||||
<li><a href="https://cof.ens.fr/k-fet/le-calendrier/">Calendrier</a></li>
|
||||
{% if perms.kfet.is_team %}
|
||||
<li><a href="{% url 'kfet.kpsul' %}">K-Psul</a></li>
|
||||
{% endif %}
|
||||
|
@ -68,9 +71,23 @@
|
|||
{% if not user.profile.login_clipper %}
|
||||
<li><a href="{% url "password_change" %}">Changer mon mot de passe</a></li>
|
||||
{% endif %}
|
||||
{% if not user.profile.is_cof and not user.profile.is_kfet %}
|
||||
<li><a href="{% url "self.kf_registration" %}">Adhérer à la K-Fêt</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if user.profile.is_chef and not user.profile.is_buro %}
|
||||
<div class="col-sm-6 buro-user-hm">
|
||||
<h3 class="block-title">Administration<span class="pull-right glyphicon glyphicon-cog"></span></h3>
|
||||
<div class="hm-block">
|
||||
<ul>
|
||||
<h4>Général</h4>
|
||||
<li><a href="{% url "registration" %}">Inscription d'un nouveau membre</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if user.profile.is_buro %}
|
||||
<div class="col-sm-6 buro-user-hm">
|
||||
<h3 class="block-title">Administration<span class="pull-right glyphicon glyphicon-cog"></span></h3>
|
||||
|
@ -115,7 +132,7 @@
|
|||
<ul>
|
||||
<li><a href="{% url "utile_cof" %}">Liens utiles du COF</a></li>
|
||||
<li><a href="{% url "utile_bda" %}">Liens utiles BdA</a></li>
|
||||
<li><a href="{% url "reset_comptes" %}">Remise à zéro adhérents COF</a></li>
|
||||
<li><a href="{% url "reset_comptes" %}">Remise à zéro adhérent⋅e⋅s COF</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Bonjour {{ member.first_name }} et bienvenue au COF !
|
||||
|
||||
Tu trouveras plein de trucs cool sur le site du COF : https://cof.ens.fr/ et notre page Facebook : https://www.facebook.com/cof.ulm
|
||||
Tu trouveras plein de trucs cool sur le site du COF : https://cof.ens.fr/ et notre compte instagram : https://www.instagram.com/cof_ulm
|
||||
Et n'oublie pas d'aller découvrir GestioCOF, la plateforme de gestion du COF !
|
||||
Si tu as des questions, tu peux nous envoyer un mail à cof@ens.fr (on aime le spam), ou passer nous voir au Burô près de la Courô du lundi au vendredi de 12h à 14h et de 18h à 20h.
|
||||
Si tu as des questions, tu peux nous envoyer un mail à cof@ens.fr (on aime le spam), ou passer nous voir au Burô près de la Courô les lundi, mardi, jeudi et vendredi de 12h à 14h et du lundi au jeudi de 18h30 à 19h30.
|
||||
|
||||
Retrouvez les évènements de rentrée pour les conscrit.e.s et les vieux/vieilles organisés par le COF et ses clubs ici : https://cof.ens.fr/planningrentree.
|
||||
Retrouvez tout les évènements organisés par le COF et ses clubs ici : https://calendrier.dgnum.eu/.
|
||||
|
||||
Amicalement,
|
||||
|
||||
Ton COF qui t'aime.
|
||||
Ton COF qui t'aime.
|
||||
|
|
21
gestioncof/templates/gestioncof/registration_kf_form.html
Normal file
21
gestioncof/templates/gestioncof/registration_kf_form.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% load bootstrap %}
|
||||
|
||||
{% if login_clipper %}
|
||||
<h3>Inscription associée au compte clipper <tt>{{ login_clipper }}</tt></h3>
|
||||
{% elif member %}
|
||||
<h3>Inscription du compte GestioCOF existant <tt>{{ member.username }}</tt></h3>
|
||||
{% else %}
|
||||
<h3>Inscription d'un nouveau compte (extérieur ?)</h3>
|
||||
{% endif %}
|
||||
<form role="form" id="profile" method="post" action="{% url 'registration' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ user_form | bootstrap }}
|
||||
{{ profile_form | bootstrap }}
|
||||
</table>
|
||||
<hr />
|
||||
{% if login_clipper or member %}
|
||||
<input type="hidden" name="user_exists" value="1" />
|
||||
{% endif %}
|
||||
<input type="submit" class="btn btn-primary pull-right" value="Enregistrer l'inscription" />
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscription d'un nouveau membre</h2>
|
||||
<div id="form-placeholder">
|
||||
{% include "gestioncof/registration_kf_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
28
gestioncof/templates/gestioncof/self_registration.html
Normal file
28
gestioncof/templates/gestioncof/self_registration.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block page_size %}col-sm-8{% endblock %}
|
||||
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
{% if member %}
|
||||
<h3>Inscription K-Fêt du compte GestioCOF existant <tt>{{ member.username }}</tt></h3>
|
||||
{% else %}
|
||||
<h3>Inscription K-Fêt d'un nouveau compte (extérieur ?)</h3>
|
||||
{% endif %}
|
||||
<form role="form" id="profile" method="post" action="{% url 'self.kf_registration' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ user_form | bootstrap }}
|
||||
{{ profile_form | bootstrap }}
|
||||
{{ agreement_form | bootstrap }}
|
||||
</table>
|
||||
<hr />
|
||||
{% if login_clipper or member %}
|
||||
<input type="hidden" name="user_exists" value="1" />
|
||||
{% endif %}
|
||||
<input type="submit" class="btn btn-primary pull-right" value="Adhérer" />
|
||||
</form>
|
||||
{% endblock %}
|
5
gestioncof/templates/kfet-denied.html
Normal file
5
gestioncof/templates/kfet-denied.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Section réservée aux membres K-Fêt -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block page_size %}col-sm-8{% endblock %}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
<script type="text/javascript">
|
||||
var supernifty_tristate = function() {
|
||||
var
|
||||
|
|
|
@ -194,7 +194,9 @@ class RegistrationViewTests(ViewTestCaseMixin, TestCase):
|
|||
)
|
||||
|
||||
er = e.eventregistration_set.get(user=self.users["user"])
|
||||
self.assertQuerysetEqual(er.options.all(), map(repr, [oc1, oc3]), ordered=False)
|
||||
self.assertQuerysetEqual(
|
||||
er.options.all(), map(repr, [oc1, oc3]), transform=repr, ordered=False
|
||||
)
|
||||
self.assertCountEqual(
|
||||
er.comments.values_list("content", flat=True), ["comment 1"]
|
||||
)
|
||||
|
@ -299,10 +301,10 @@ class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCa
|
|||
raise ValueError("Unexpected section name: {}".format(section.name))
|
||||
|
||||
self.assertQuerysetEqual(
|
||||
others, map(str, expected_others), ordered=False, transform=str
|
||||
others, map(str, expected_others), transform=str, ordered=False
|
||||
)
|
||||
self.assertQuerysetEqual(
|
||||
members, map(str, expected_members), ordered=False, transform=str
|
||||
members, map(str, expected_members), transform=str, ordered=False
|
||||
)
|
||||
self.assertSetEqual(
|
||||
set(clippers), set(map(LDAPSearch().result_verbose_name, expected_clippers))
|
||||
|
@ -484,7 +486,7 @@ class ExportMembersViewTests(CSVResponseMixin, ViewTestCaseMixin, TestCase):
|
|||
u1.last_name = "last"
|
||||
u1.email = "user@mail.net"
|
||||
u1.save()
|
||||
u1.profile.date_adhesion = date(2023, 5, 22)
|
||||
u1.profile.date_adhesion_cof = date(2023, 5, 22)
|
||||
u1.profile.phone = "0123456789"
|
||||
u1.profile.departement = "Dept"
|
||||
u1.profile.save()
|
||||
|
@ -648,7 +650,10 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertQuerysetEqual(
|
||||
r.context["owned_clubs"], map(repr, [self.c1, self.c2]), ordered=False
|
||||
r.context["owned_clubs"],
|
||||
map(repr, [self.c1, self.c2]),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
|
||||
|
@ -950,7 +955,10 @@ class EventViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
er = self.e.eventregistration_set.get(user=self.users["user"])
|
||||
self.assertQuerysetEqual(
|
||||
er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]), ordered=False
|
||||
er.options.all(),
|
||||
map(repr, [self.oc1, self.oc3, self.oc4]),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
# TODO: Make the view care about comments.
|
||||
# self.assertQuerysetEqual(
|
||||
|
@ -975,7 +983,9 @@ class EventViewTests(ViewTestCaseMixin, TestCase):
|
|||
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||
|
||||
er.refresh_from_db()
|
||||
self.assertQuerysetEqual(er.options.all(), map(repr, [self.oc3]), ordered=False)
|
||||
self.assertQuerysetEqual(
|
||||
er.options.all(), map(repr, [self.oc3]), transform=repr, ordered=False
|
||||
)
|
||||
# TODO: Make the view care about comments.
|
||||
# self.assertQuerysetEqual(
|
||||
# er.comments.all(), map(repr, []),
|
||||
|
@ -1029,7 +1039,10 @@ class EventStatusViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertQuerysetEqual(
|
||||
r.context["user_choices"], map(repr, expected), ordered=False
|
||||
r.context["user_choices"],
|
||||
map(repr, expected),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_filter_none(self):
|
||||
|
@ -1096,7 +1109,10 @@ class SurveyViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
a = self.s.surveyanswer_set.get(user=self.users["user"])
|
||||
self.assertQuerysetEqual(
|
||||
a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]), ordered=False
|
||||
a.answers.all(),
|
||||
map(repr, [self.qa1, self.qa3, self.qa4]),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_post_edit(self):
|
||||
|
@ -1115,7 +1131,9 @@ class SurveyViewTests(ViewTestCaseMixin, TestCase):
|
|||
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||
|
||||
a.refresh_from_db()
|
||||
self.assertQuerysetEqual(a.answers.all(), map(repr, [self.qa3]), ordered=False)
|
||||
self.assertQuerysetEqual(
|
||||
a.answers.all(), map(repr, [self.qa3]), transform=repr, ordered=False
|
||||
)
|
||||
|
||||
def test_post_delete(self):
|
||||
a = self.s.surveyanswer_set.create(user=self.users["user"])
|
||||
|
@ -1196,7 +1214,10 @@ class SurveyStatusViewTests(ViewTestCaseMixin, TestCase):
|
|||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertQuerysetEqual(
|
||||
r.context["user_answers"], map(repr, expected), ordered=False
|
||||
r.context["user_answers"],
|
||||
map(repr, expected),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_filter_none(self):
|
||||
|
|
|
@ -4,6 +4,16 @@ from django.views.generic.base import TemplateView
|
|||
from django_cas_ng import views as django_cas_views
|
||||
|
||||
from gestioncof import csv_views, views
|
||||
from shared.views import SympaListView
|
||||
|
||||
sympa_patterns = [
|
||||
path(
|
||||
f"{mailing}/",
|
||||
SympaListView.as_view(filters={f"profile__mailing_{mailing}": True}),
|
||||
name=f"sympa.{mailing}",
|
||||
)
|
||||
for mailing in ["bda", "bda_revente", "cof", "unernestaparis"]
|
||||
]
|
||||
|
||||
export_patterns = [
|
||||
path("members", views.export_members, name="export.members"),
|
||||
|
@ -70,6 +80,7 @@ registration_patterns = [
|
|||
views.RegistrationAutocompleteView.as_view(),
|
||||
name="cof.registration.autocomplete",
|
||||
),
|
||||
path("self_kf", views.self_kf_registration, name="self.kf_registration"),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -89,6 +100,7 @@ urlpatterns = [
|
|||
name="cof-user-autocomplete",
|
||||
),
|
||||
path("config", views.ConfigUpdate.as_view(), name="config.edit"),
|
||||
path("carte", views.carte_kf, name="profile.carte"),
|
||||
# -----
|
||||
# Authentification
|
||||
# -----
|
||||
|
@ -162,4 +174,8 @@ urlpatterns = [
|
|||
# Clubs
|
||||
# -----
|
||||
path("clubs/", include(clubs_patterns)),
|
||||
# -----
|
||||
# Sympa export
|
||||
# -----
|
||||
path("sympa/", include(sympa_patterns)),
|
||||
]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import csv
|
||||
import uuid
|
||||
from datetime import date, timedelta
|
||||
from smtplib import SMTPRecipientsRefused
|
||||
from datetime import timedelta
|
||||
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
from django.contrib import messages
|
||||
|
@ -14,20 +13,26 @@ from django.contrib.auth.views import (
|
|||
redirect_to_login,
|
||||
)
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.mail import send_mail
|
||||
from django.db.models import Q
|
||||
from django.http import Http404, HttpResponse, HttpResponseForbidden
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.template import loader
|
||||
from django.urls import reverse_lazy
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import FormView, TemplateView
|
||||
from django_cas_ng.views import LogoutView as CasLogoutView
|
||||
from icalendar import Calendar, Event as Vevent
|
||||
|
||||
from bda.models import Spectacle, Tirage
|
||||
from gestioncof.autocomplete import cof_autocomplete
|
||||
from gestioncof.decorators import BuroRequiredMixin, buro_required, cof_required
|
||||
from gestioncof.decorators import (
|
||||
BuroRequiredMixin,
|
||||
ChefRequiredMixin,
|
||||
buro_required,
|
||||
chef_required,
|
||||
cof_required,
|
||||
kfet_required,
|
||||
)
|
||||
from gestioncof.forms import (
|
||||
CalendarForm,
|
||||
ClubsForm,
|
||||
|
@ -38,9 +43,11 @@ from gestioncof.forms import (
|
|||
GestioncofConfigForm,
|
||||
PhoneForm,
|
||||
ProfileForm,
|
||||
RegistrationKFProfileForm,
|
||||
RegistrationPassUserForm,
|
||||
RegistrationProfileForm,
|
||||
RegistrationUserForm,
|
||||
SubscribForm,
|
||||
SurveyForm,
|
||||
SurveyStatusFilterForm,
|
||||
UserForm,
|
||||
|
@ -86,7 +93,9 @@ class ResetComptes(BuroRequiredMixin, TemplateView):
|
|||
nb_adherents = CofProfile.objects.filter(is_cof=True).count()
|
||||
CofProfile.objects.update(
|
||||
is_cof=False,
|
||||
date_adhesion=None,
|
||||
is_kfet=False,
|
||||
date_adhesion_cof=None,
|
||||
date_adhesion_kfet=None,
|
||||
mailing_cof=False,
|
||||
mailing_bda=False,
|
||||
mailing_bda_revente=False,
|
||||
|
@ -421,13 +430,20 @@ def profile(request):
|
|||
return render(request, "gestioncof/profile.html", context)
|
||||
|
||||
|
||||
@kfet_required
|
||||
def carte_kf(request):
|
||||
user = request.user
|
||||
return render(request, "gestioncof/carte_kf.html", {"user": user})
|
||||
|
||||
|
||||
def registration_set_ro_fields(user_form, profile_form):
|
||||
user_form.fields["username"].widget.attrs["readonly"] = True
|
||||
profile_form.fields["login_clipper"].widget.attrs["readonly"] = True
|
||||
|
||||
|
||||
@buro_required
|
||||
@chef_required
|
||||
def registration_form2(request, login_clipper=None, username=None, fullname=None):
|
||||
is_buro = request.user.profile.is_buro
|
||||
events = Event.objects.filter(old=False).all()
|
||||
member = None
|
||||
if login_clipper:
|
||||
|
@ -449,85 +465,84 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
|
|||
user_form.fields["first_name"].initial = bits[0]
|
||||
if len(bits) > 1:
|
||||
user_form.fields["last_name"].initial = " ".join(bits[1:])
|
||||
# profile
|
||||
profile_form = RegistrationProfileForm(
|
||||
initial={"login_clipper": login_clipper}
|
||||
)
|
||||
if is_buro:
|
||||
# profile
|
||||
profile_form = RegistrationProfileForm(
|
||||
initial={"login_clipper": login_clipper}
|
||||
)
|
||||
# events & clubs
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(
|
||||
initial={"login_clipper": login_clipper}
|
||||
)
|
||||
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
# events & clubs
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
if username:
|
||||
member = get_object_or_404(User, username=username)
|
||||
(profile, _) = CofProfile.objects.get_or_create(user=member)
|
||||
# already existing, prefill
|
||||
user_form = RegistrationUserForm(instance=member)
|
||||
profile_form = RegistrationProfileForm(instance=profile)
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(instance=profile)
|
||||
# events
|
||||
current_registrations = []
|
||||
for event in events:
|
||||
try:
|
||||
current_registrations.append(
|
||||
EventRegistration.objects.get(user=member, event=event)
|
||||
)
|
||||
except EventRegistration.DoesNotExist:
|
||||
current_registrations.append(None)
|
||||
event_formset = EventFormset(
|
||||
events=events,
|
||||
prefix="events",
|
||||
current_registrations=current_registrations,
|
||||
)
|
||||
# Clubs
|
||||
clubs_form = ClubsForm(initial={"clubs": member.clubs.all()})
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(instance=profile)
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
# events
|
||||
current_registrations = []
|
||||
for event in events:
|
||||
try:
|
||||
current_registrations.append(
|
||||
EventRegistration.objects.get(user=member, event=event)
|
||||
)
|
||||
except EventRegistration.DoesNotExist:
|
||||
current_registrations.append(None)
|
||||
event_formset = EventFormset(
|
||||
events=events, prefix="events", current_registrations=current_registrations
|
||||
)
|
||||
# Clubs
|
||||
clubs_form = ClubsForm(initial={"clubs": member.clubs.all()})
|
||||
elif not login_clipper:
|
||||
# new user
|
||||
user_form = RegistrationPassUserForm()
|
||||
profile_form = RegistrationProfileForm()
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_form.html",
|
||||
{
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"event_formset": event_formset,
|
||||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def notify_new_member(request, member: User):
|
||||
if not member.email:
|
||||
messages.warning(
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm()
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm()
|
||||
if is_buro:
|
||||
return render(
|
||||
request,
|
||||
"GestioCOF n'a pas d'adresse mail pour {}, ".format(member)
|
||||
+ "aucun email de bienvenue n'a été envoyé",
|
||||
"gestioncof/registration_form.html",
|
||||
{
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"event_formset": event_formset,
|
||||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
return
|
||||
|
||||
# Try to send a welcome email and report SMTP errors
|
||||
try:
|
||||
send_mail(
|
||||
"Bienvenue au COF",
|
||||
loader.render_to_string(
|
||||
"gestioncof/mails/welcome.txt", context={"member": member}
|
||||
),
|
||||
"cof@ens.fr",
|
||||
[member.email],
|
||||
)
|
||||
except SMTPRecipientsRefused:
|
||||
messages.error(
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"Error lors de l'envoi de l'email de bienvenue à {} ({})".format(
|
||||
member, member.email
|
||||
),
|
||||
"gestioncof/registration_kf_form.html",
|
||||
{
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@buro_required
|
||||
@chef_required
|
||||
def registration(request):
|
||||
is_buro = request.user.profile.is_buro
|
||||
if request.POST:
|
||||
request_dict = request.POST.copy()
|
||||
member = None
|
||||
|
@ -541,10 +556,15 @@ def registration(request):
|
|||
user_form = RegistrationPassUserForm(request_dict)
|
||||
else:
|
||||
user_form = RegistrationUserForm(request_dict)
|
||||
profile_form = RegistrationProfileForm(request_dict)
|
||||
clubs_form = ClubsForm(request_dict)
|
||||
events = Event.objects.filter(old=False).all()
|
||||
event_formset = EventFormset(events=events, data=request_dict, prefix="events")
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(request_dict)
|
||||
clubs_form = ClubsForm(request_dict)
|
||||
events = Event.objects.filter(old=False).all()
|
||||
event_formset = EventFormset(
|
||||
events=events, data=request_dict, prefix="events"
|
||||
)
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(request_dict)
|
||||
if "user_exists" in request_dict and request_dict["user_exists"]:
|
||||
username = request_dict["username"]
|
||||
try:
|
||||
|
@ -565,63 +585,72 @@ def registration(request):
|
|||
member = user_form.save()
|
||||
profile, _ = CofProfile.objects.get_or_create(user=member)
|
||||
was_cof = profile.is_cof
|
||||
was_kfet = profile.is_kfet
|
||||
# Maintenant on remplit le formulaire de profil
|
||||
profile_form = RegistrationProfileForm(request_dict, instance=profile)
|
||||
if (
|
||||
profile_form.is_valid()
|
||||
and event_formset.is_valid()
|
||||
and clubs_form.is_valid()
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(request_dict, instance=profile)
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(request_dict, instance=profile)
|
||||
if profile_form.is_valid() and (
|
||||
not is_buro or (event_formset.is_valid() and clubs_form.is_valid())
|
||||
):
|
||||
# Enregistrement du profil
|
||||
profile = profile_form.save()
|
||||
if profile.is_cof and not was_cof:
|
||||
notify_new_member(request, member)
|
||||
profile.date_adhesion = date.today()
|
||||
profile.save()
|
||||
if is_buro:
|
||||
if profile.is_cof:
|
||||
profile.make_adh_cof(request, was_cof)
|
||||
|
||||
# Enregistrement des inscriptions aux événements
|
||||
for form in event_formset:
|
||||
if "status" not in form.cleaned_data:
|
||||
form.cleaned_data["status"] = "no"
|
||||
if form.cleaned_data["status"] == "no":
|
||||
try:
|
||||
current_registration = EventRegistration.objects.get(
|
||||
user=member, event=form.event
|
||||
)
|
||||
current_registration.delete()
|
||||
except EventRegistration.DoesNotExist:
|
||||
pass
|
||||
continue
|
||||
all_choices = get_event_form_choices(form.event, form)
|
||||
(
|
||||
current_registration,
|
||||
created_reg,
|
||||
) = EventRegistration.objects.get_or_create(
|
||||
user=member, event=form.event
|
||||
)
|
||||
update_event_form_comments(form.event, form, current_registration)
|
||||
current_registration.options.set(all_choices)
|
||||
current_registration.paid = form.cleaned_data["status"] == "paid"
|
||||
current_registration.save()
|
||||
# if form.event.title == "Mega 15" and created_reg:
|
||||
# field = EventCommentField.objects.get(
|
||||
# event=form.event, name="Commentaires")
|
||||
# try:
|
||||
# comments = EventCommentValue.objects.get(
|
||||
# commentfield=field,
|
||||
# registration=current_registration).content
|
||||
# except EventCommentValue.DoesNotExist:
|
||||
# comments = field.default
|
||||
# FIXME : il faut faire quelque chose de propre ici,
|
||||
# par exemple écrire un mail générique pour
|
||||
# l'inscription aux événements et/ou donner la
|
||||
# possibilité d'associer un mail aux événements
|
||||
# send_custom_mail(...)
|
||||
# Enregistrement des inscriptions aux clubs
|
||||
member.clubs.clear()
|
||||
for club in clubs_form.cleaned_data["clubs"]:
|
||||
club.membres.add(member)
|
||||
club.save()
|
||||
if profile.is_kfet:
|
||||
profile.make_adh_kfet(request, was_kfet)
|
||||
|
||||
if is_buro:
|
||||
# Enregistrement des inscriptions aux événements
|
||||
for form in event_formset:
|
||||
if "status" not in form.cleaned_data:
|
||||
form.cleaned_data["status"] = "no"
|
||||
if form.cleaned_data["status"] == "no":
|
||||
try:
|
||||
current_registration = EventRegistration.objects.get(
|
||||
user=member, event=form.event
|
||||
)
|
||||
current_registration.delete()
|
||||
except EventRegistration.DoesNotExist:
|
||||
pass
|
||||
continue
|
||||
all_choices = get_event_form_choices(form.event, form)
|
||||
(
|
||||
current_registration,
|
||||
created_reg,
|
||||
) = EventRegistration.objects.get_or_create(
|
||||
user=member, event=form.event
|
||||
)
|
||||
update_event_form_comments(
|
||||
form.event, form, current_registration
|
||||
)
|
||||
current_registration.options.set(all_choices)
|
||||
current_registration.paid = (
|
||||
form.cleaned_data["status"] == "paid"
|
||||
)
|
||||
current_registration.save()
|
||||
# if form.event.title == "Mega 15" and created_reg:
|
||||
# field = EventCommentField.objects.get(
|
||||
# event=form.event, name="Commentaires")
|
||||
# try:
|
||||
# comments = EventCommentValue.objects.get(
|
||||
# commentfield=field,
|
||||
# registration=current_registration).content
|
||||
# except EventCommentValue.DoesNotExist:
|
||||
# comments = field.default
|
||||
# FIXME : il faut faire quelque chose de propre ici,
|
||||
# par exemple écrire un mail générique pour
|
||||
# l'inscription aux événements et/ou donner la
|
||||
# possibilité d'associer un mail aux événements
|
||||
# send_custom_mail(...)
|
||||
# Enregistrement des inscriptions aux clubs
|
||||
member.clubs.clear()
|
||||
for club in clubs_form.cleaned_data["clubs"]:
|
||||
club.membres.add(member)
|
||||
club.save()
|
||||
|
||||
# ---
|
||||
# Success
|
||||
|
@ -633,27 +662,91 @@ def registration(request):
|
|||
member.get_full_name(), member.email
|
||||
)
|
||||
)
|
||||
if profile.is_cof:
|
||||
if is_buro and profile.is_cof:
|
||||
msg += "\nIl est désormais membre du COF n°{:d} !".format(
|
||||
member.profile.id
|
||||
)
|
||||
messages.success(request, msg, extra_tags="safe")
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_post.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"event_formset": event_formset,
|
||||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
if is_buro:
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_post.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"event_formset": event_formset,
|
||||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_kf_post.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(request, "registration.html")
|
||||
|
||||
|
||||
# TODO: without login
|
||||
@login_required
|
||||
def self_kf_registration(request):
|
||||
member = request.user
|
||||
(profile, _) = CofProfile.objects.get_or_create(user=member)
|
||||
|
||||
if profile.is_kfet or profile.is_cof:
|
||||
msg = "Vous êtes déjà adhérent du COF !"
|
||||
messages.success(request, msg)
|
||||
response = HttpResponse(content="", status=303)
|
||||
response["Location"] = reverse("profile")
|
||||
return response
|
||||
|
||||
was_kfet = profile.is_kfet
|
||||
if request.POST:
|
||||
user_form = RegistrationUserForm(request.POST, instance=member)
|
||||
profile_form = PhoneForm(request.POST, instance=profile)
|
||||
agreement_form = SubscribForm(request.POST)
|
||||
if (
|
||||
user_form.is_valid()
|
||||
and profile_form.is_valid()
|
||||
and agreement_form.is_valid()
|
||||
):
|
||||
member = user_form.save()
|
||||
profile = profile_form.save()
|
||||
profile.is_kfet = True
|
||||
profile.save()
|
||||
profile.make_adh_kfet(request, was_kfet)
|
||||
|
||||
msg = "Votre adhésion a été enregistrée avec succès."
|
||||
messages.success(request, msg, extra_tags="safe")
|
||||
response = HttpResponse(content="", status=303)
|
||||
response["Location"] = reverse("profile")
|
||||
return response
|
||||
else:
|
||||
user_form = RegistrationUserForm(instance=member)
|
||||
profile_form = PhoneForm(instance=profile)
|
||||
agreement_form = SubscribForm()
|
||||
|
||||
user_form.fields["username"].widget.attrs["readonly"] = True
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/self_registration.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"agreement_form": agreement_form,
|
||||
"member": member,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# -----
|
||||
# Clubs
|
||||
# -----
|
||||
|
@ -707,7 +800,7 @@ def export_members(request):
|
|||
response["Content-Disposition"] = "attachment; filename=membres_cof.csv"
|
||||
|
||||
writer = csv.writer(response)
|
||||
for profile in CofProfile.objects.filter(is_cof=True).all():
|
||||
for profile in CofProfile.objects.filter(Q(is_cof=True) | Q(is_kfet=True)).all():
|
||||
user = profile.user
|
||||
bits = [
|
||||
user.id,
|
||||
|
@ -718,8 +811,10 @@ def export_members(request):
|
|||
profile.phone,
|
||||
profile.occupation,
|
||||
profile.departement,
|
||||
"COF" if profile.is_cof else "K-Fêt",
|
||||
profile.type_cotiz,
|
||||
profile.date_adhesion,
|
||||
profile.date_adhesion_cof,
|
||||
profile.date_adhesion_kfet,
|
||||
]
|
||||
writer.writerow([str(bit) for bit in bits])
|
||||
|
||||
|
@ -975,6 +1070,6 @@ class UserAutocompleteView(BuroRequiredMixin, Select2QuerySetView):
|
|||
search_fields = ("username", "first_name", "last_name")
|
||||
|
||||
|
||||
class RegistrationAutocompleteView(BuroRequiredMixin, AutocompleteView):
|
||||
class RegistrationAutocompleteView(ChefRequiredMixin, AutocompleteView):
|
||||
template_name = "gestioncof/search_results.html"
|
||||
search_composer = cof_autocomplete
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
default_app_config = "kfet.apps.KFetConfig"
|
||||
KFET_DELETED_TRIGRAMME = "☠☠☠"
|
||||
KFET_DELETED_USERNAME = "kfet_deleted_user"
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
default_app_config = "kfet.auth.apps.KFetAuthConfig"
|
||||
|
||||
KFET_GENERIC_USERNAME = "kfet_genericteam"
|
||||
KFET_GENERIC_TRIGRAMME = "GNR"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class KFetAuthConfig(AppConfig):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from shared.forms import ProtectedModelForm
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.contrib.auth.models import Group, Permission
|
||||
from django.db import models
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
KFET_APP_LABELS = ["kfet", "kfetauth"]
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib.auth.signals import user_logged_in
|
|||
from django.dispatch import receiver
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .utils import get_kfet_generic_user
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ class UserGroupFormTests(TestCase):
|
|||
self.assertQuerysetEqual(
|
||||
groups_field.queryset,
|
||||
[repr(g.group_ptr) for g in self.kfet_groups],
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.http import QueryDict
|
|||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.views.generic import View
|
||||
from django.views.generic.edit import CreateView, UpdateView
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default_app_config = "kfet.cms.apps.KFetCMSAppConfig"
|
|
@ -1,6 +1,6 @@
|
|||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
from django.templatetags.static import static
|
||||
from django.utils.html import format_html
|
||||
from wagtail.core import hooks
|
||||
from wagtail import hooks
|
||||
|
||||
|
||||
@hooks.register("insert_editor_css")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.contrib.auth.models import Group
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import BaseCommand
|
||||
from wagtail.core.models import Page, Site
|
||||
from wagtail.models import Page, Site
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
import wagtail.snippets.blocks
|
||||
from django.db import migrations, models
|
||||
|
||||
|
@ -41,20 +41,20 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.core.fields.StreamField(
|
||||
wagtail.fields.StreamField(
|
||||
(
|
||||
(
|
||||
"rich",
|
||||
wagtail.core.blocks.RichTextBlock(label="Éditeur"),
|
||||
wagtail.blocks.RichTextBlock(label="Éditeur"),
|
||||
),
|
||||
("carte", kfet.cms.models.MenuBlock()),
|
||||
(
|
||||
"group_team",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
(
|
||||
(
|
||||
"show_only",
|
||||
wagtail.core.blocks.IntegerBlock(
|
||||
wagtail.blocks.IntegerBlock(
|
||||
help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.", # noqa
|
||||
required=False,
|
||||
label="Montrer seulement",
|
||||
|
@ -62,7 +62,7 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"members",
|
||||
wagtail.core.blocks.ListBlock(
|
||||
wagtail.blocks.ListBlock(
|
||||
wagtail.snippets.blocks.SnippetChooserBlock( # noqa
|
||||
kfet.cms.models.MemberTeam
|
||||
),
|
||||
|
@ -75,22 +75,22 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"group",
|
||||
wagtail.core.blocks.StreamBlock(
|
||||
wagtail.blocks.StreamBlock(
|
||||
(
|
||||
(
|
||||
"rich",
|
||||
wagtail.core.blocks.RichTextBlock(
|
||||
wagtail.blocks.RichTextBlock(
|
||||
label="Éditeur"
|
||||
),
|
||||
),
|
||||
("carte", kfet.cms.models.MenuBlock()),
|
||||
(
|
||||
"group_team",
|
||||
wagtail.core.blocks.StructBlock(
|
||||
wagtail.blocks.StructBlock(
|
||||
(
|
||||
(
|
||||
"show_only",
|
||||
wagtail.core.blocks.IntegerBlock( # noqa
|
||||
wagtail.blocks.IntegerBlock( # noqa
|
||||
help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.", # noqa
|
||||
required=False,
|
||||
label="Montrer seulement",
|
||||
|
@ -98,7 +98,7 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
(
|
||||
"members",
|
||||
wagtail.core.blocks.ListBlock(
|
||||
wagtail.blocks.ListBlock(
|
||||
wagtail.snippets.blocks.SnippetChooserBlock( # noqa
|
||||
kfet.cms.models.MemberTeam # noqa
|
||||
),
|
||||
|
|
90
kfet/cms/migrations/0003_alter_kfetpage_content.py
Normal file
90
kfet/cms/migrations/0003_alter_kfetpage_content.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-19 12:27
|
||||
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
import wagtail.snippets.blocks
|
||||
from django.db import migrations
|
||||
|
||||
import kfet.cms.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfetcms", "0002_alter_kfetpage_colcount"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="kfetpage",
|
||||
name="content",
|
||||
field=wagtail.fields.StreamField(
|
||||
[
|
||||
("rich", wagtail.blocks.RichTextBlock(label="Éditeur")),
|
||||
("carte", kfet.cms.models.MenuBlock()),
|
||||
(
|
||||
"group_team",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"show_only",
|
||||
wagtail.blocks.IntegerBlock(
|
||||
help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.",
|
||||
label="Montrer seulement",
|
||||
required=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"members",
|
||||
wagtail.blocks.ListBlock(
|
||||
wagtail.snippets.blocks.SnippetChooserBlock(
|
||||
kfet.cms.models.MemberTeam
|
||||
),
|
||||
form_classname="team-group",
|
||||
label="K-Fêt-eux-ses",
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"group",
|
||||
wagtail.blocks.StreamBlock(
|
||||
[
|
||||
("rich", wagtail.blocks.RichTextBlock(label="Éditeur")),
|
||||
("carte", kfet.cms.models.MenuBlock()),
|
||||
(
|
||||
"group_team",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"show_only",
|
||||
wagtail.blocks.IntegerBlock(
|
||||
help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.",
|
||||
label="Montrer seulement",
|
||||
required=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"members",
|
||||
wagtail.blocks.ListBlock(
|
||||
wagtail.snippets.blocks.SnippetChooserBlock(
|
||||
kfet.cms.models.MemberTeam
|
||||
),
|
||||
form_classname="team-group",
|
||||
label="K-Fêt-eux-ses",
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
label="Contenu groupé",
|
||||
),
|
||||
),
|
||||
],
|
||||
use_json_field=True,
|
||||
verbose_name="Contenu",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,15 +1,9 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from wagtail.admin.edit_handlers import (
|
||||
FieldPanel,
|
||||
FieldRowPanel,
|
||||
MultiFieldPanel,
|
||||
StreamFieldPanel,
|
||||
)
|
||||
from wagtail.core import blocks
|
||||
from wagtail.core.fields import StreamField
|
||||
from wagtail.core.models import Page
|
||||
from wagtail.images.edit_handlers import ImageChooserPanel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from wagtail import blocks
|
||||
from wagtail.admin.panels import FieldPanel, FieldRowPanel, MultiFieldPanel
|
||||
from wagtail.fields import StreamField
|
||||
from wagtail.models import Page
|
||||
from wagtail.snippets.blocks import SnippetChooserBlock
|
||||
from wagtail.snippets.models import register_snippet
|
||||
|
||||
|
@ -43,7 +37,7 @@ class MemberTeam(models.Model):
|
|||
FieldPanel("first_name"),
|
||||
FieldPanel("last_name"),
|
||||
FieldPanel("nick_name"),
|
||||
ImageChooserPanel("photo"),
|
||||
FieldPanel("photo"),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
|
@ -97,7 +91,9 @@ class KFetStreamBlock(ChoicesStreamBlock):
|
|||
|
||||
|
||||
class KFetPage(Page):
|
||||
content = StreamField(KFetStreamBlock, verbose_name=_("Contenu"))
|
||||
content = StreamField(
|
||||
KFetStreamBlock, verbose_name=_("Contenu"), use_json_field=True
|
||||
)
|
||||
|
||||
# Layout fields
|
||||
|
||||
|
@ -135,7 +131,7 @@ class KFetPage(Page):
|
|||
|
||||
# Panels
|
||||
|
||||
content_panels = Page.content_panels + [StreamFieldPanel("content")]
|
||||
content_panels = Page.content_panels + [FieldPanel("content")]
|
||||
|
||||
layout_panel = [
|
||||
FieldPanel("no_header"),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import djconfig
|
||||
from asgiref.sync import sync_to_async
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
|
||||
|
@ -23,7 +24,7 @@ class KFetConfig(object):
|
|||
# Note it should be called only once across requests, if you use
|
||||
# kfet_config instance below.
|
||||
if not self._conf_init:
|
||||
djconfig.reload_maybe()
|
||||
sync_to_async(djconfig.reload_maybe)()
|
||||
self._conf_init = True
|
||||
|
||||
def __getattr__(self, key):
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
|
||||
from .utils import DjangoJsonWebsocketConsumer, PermConsumerMixin
|
||||
|
||||
|
||||
class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer):
|
||||
groups = ["kfet.kpsul"]
|
||||
perms_connect = ["kfet.is_team"]
|
||||
|
||||
async def kpsul(self, event):
|
||||
await self.send_json(event)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def group_send(cls, group, data):
|
||||
channel_layer = get_channel_layer()
|
||||
await channel_layer.group_send(group, data)
|
||||
|
|
|
@ -8,7 +8,7 @@ from django.core import validators
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.forms import modelformset_factory
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from djconfig.forms import ConfigForm
|
||||
|
||||
from gestioncof.models import CofProfile
|
||||
|
@ -93,7 +93,7 @@ class DemandeSoireeForm(forms.Form):
|
|||
|
||||
def default_promo():
|
||||
now = date.today()
|
||||
return now.month <= 8 and now.year - 1 or now.year
|
||||
return now.month <= 7 and now.year - 1 or now.year
|
||||
|
||||
|
||||
def get_promo_choices():
|
||||
|
@ -118,7 +118,11 @@ class AccountForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Account
|
||||
fields = ["trigramme", "promo", "nickname"]
|
||||
widgets = {"trigramme": forms.TextInput(attrs={"autocomplete": "off"})}
|
||||
widgets = {
|
||||
"trigramme": forms.TextInput(
|
||||
attrs={"autocomplete": "off", "class": "trigramme_field"}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class AccountBalanceForm(forms.ModelForm):
|
||||
|
@ -191,7 +195,13 @@ class CofForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = ["login_clipper", "is_cof", "departement"]
|
||||
fields = ["login_clipper", "is_cof", "is_kfet", "departement"]
|
||||
|
||||
|
||||
class CofKFForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = ["is_kfet"]
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
|
@ -346,6 +356,7 @@ class ArticleForm(forms.ModelForm):
|
|||
fields = [
|
||||
"name",
|
||||
"is_sold",
|
||||
"no_exte",
|
||||
"hidden",
|
||||
"price",
|
||||
"stock",
|
||||
|
@ -360,6 +371,7 @@ class ArticleRestrictForm(ArticleForm):
|
|||
fields = [
|
||||
"name",
|
||||
"is_sold",
|
||||
"no_exte",
|
||||
"hidden",
|
||||
"price",
|
||||
"category",
|
||||
|
@ -404,7 +416,11 @@ class KPsulAccountForm(forms.ModelForm):
|
|||
fields = ["trigramme"]
|
||||
widgets = {
|
||||
"trigramme": forms.TextInput(
|
||||
attrs={"autocomplete": "off", "spellcheck": "false"}
|
||||
attrs={
|
||||
"autocomplete": "off",
|
||||
"spellcheck": "false",
|
||||
"class": "trigramme_field",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -650,7 +666,7 @@ class OrderArticleForm(forms.Form):
|
|||
self.v_moy = kwargs["initial"]["v_moy"]
|
||||
self.v_et = kwargs["initial"]["v_et"]
|
||||
self.v_prev = kwargs["initial"]["v_prev"]
|
||||
self.c_rec = kwargs["initial"]["c_rec"]
|
||||
self.c_rec_1w = kwargs["initial"]["c_rec_1w"]
|
||||
self.is_sold = kwargs["initial"]["is_sold"]
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ from __future__ import unicode_literals
|
|||
import datetime
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.utils.timezone import utc
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -17,7 +16,9 @@ class Migration(migrations.Migration):
|
|||
name="at",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc),
|
||||
default=datetime.datetime(
|
||||
2016, 8, 29, 18, 35, 3, 419033, tzinfo=datetime.timezone.utc
|
||||
),
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
|
|
20
kfet/migrations/0081_article_no_exte.py
Normal file
20
kfet/migrations/0081_article_no_exte.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-06 16:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfet", "0080_accountnegative_last_rappel"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="article",
|
||||
name="no_exte",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="Réservé au adhérents"
|
||||
),
|
||||
),
|
||||
]
|
34
kfet/migrations/0082_alter_operation_options.py
Normal file
34
kfet/migrations/0082_alter_operation_options.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-18 10:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfet", "0081_article_no_exte"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="operation",
|
||||
options={
|
||||
"permissions": (
|
||||
("perform_deposit", "Effectuer une charge"),
|
||||
(
|
||||
"perform_negative_operations",
|
||||
"Enregistrer des commandes en négatif",
|
||||
),
|
||||
(
|
||||
"perform_liq_reserved",
|
||||
"Effectuer une opération réservé aux adhérents sur LIQ",
|
||||
),
|
||||
("cancel_old_operations", "Annuler des commandes non récentes"),
|
||||
(
|
||||
"perform_commented_operations",
|
||||
"Enregistrer des commandes avec commentaires",
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue