Compare commits

..

5 commits

Author SHA1 Message Date
Tom Hubrecht
7460ca591f Merge branch 'master' into 'Production'
Master

See merge request klub-dev-ens/gestioCOF!527
2023-06-15 13:59:53 +02:00
Tom Hubrecht
a4494c3648 Merge branch 'master' into 'Production'
Version 0.15

See merge request klub-dev-ens/gestioCOF!524
2023-05-22 20:49:38 +02:00
Tom Hubrecht
1fbf27a9f7 Merge branch 'master' into 'Production'
Mise en production de la v0.14

See merge request klub-dev-ens/gestioCOF!519
2023-05-19 20:24:04 +02:00
Tom Hubrecht
3178657e33 Merge branch 'master' into 'Production'
Update prod

See merge request klub-dev-ens/gestioCOF!517
2023-02-21 21:50:26 +01:00
Tom Hubrecht
a4ebeb266a Merge branch 'master' into 'Production'
Mise en production de 0.12.1

See merge request klub-dev-ens/gestioCOF!514
2022-10-03 18:46:49 +02:00
161 changed files with 827 additions and 3330 deletions

View file

@ -1 +0,0 @@
0x0000000000000000000000000000000000000000

View file

@ -1 +0,0 @@
10000000-ffff-ffff-ffff-000000000001

View file

@ -1 +0,0 @@
k-feste_token

View file

@ -1 +0,0 @@
insecure-key

View file

@ -1 +0,0 @@
toto

View file

@ -1 +0,0 @@
sympa

1
.gitignore vendored
View file

@ -21,4 +21,3 @@ media/
# VSCode
.vscode/
.direnv
.static

View file

@ -43,21 +43,13 @@ 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 petitscours shared --parallel
- coverage run manage.py test gestioncof bda kfet petitscours shared --parallel
bdstest:
stage: test

View file

@ -23,9 +23,6 @@ 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 ??? - ??/??/????
@ -68,8 +65,6 @@ 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

View file

@ -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 libsasl2-dev python-dev-is-python3 libldap2-dev libssl-dev
sudo apt-get install python3-pip python3-dev python3-venv sqlite3
Si vous décidez d'utiliser un environnement virtuel Python (virtualenv;
fortement conseillé), déplacez-vous dans le dossier où est installé GestioCOF
@ -30,15 +30,7 @@ Pour l'activer, il faut taper
. venv/bin/activate
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)
depuis le même dossier.
Vous pouvez maintenant installer les dépendances Python depuis le fichier
`requirements-devel.txt` :

View file

@ -1,23 +0,0 @@
# 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"
),
),
]

View file

@ -1,18 +0,0 @@
# 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'),
),
]

View file

@ -1,13 +0,0 @@
# 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 = []

View file

@ -151,7 +151,6 @@ PAYMENT_TYPES = (
("cash", "Cash"),
("cb", "CB"),
("cheque", "Chèque"),
("virement", "Virement"),
("autre", "Autre"),
)
@ -164,7 +163,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=8, choices=PAYMENT_TYPES, blank=True
"Moyen de paiement", max_length=6, choices=PAYMENT_TYPES, blank=True
)
def __str__(self):
@ -254,11 +253,7 @@ class ChoixSpectacle(models.Model):
class Meta:
ordering = ("priority",)
constraints = [
models.UniqueConstraint(
fields=["participant", "spectacle"], name="unique_participation"
)
]
unique_together = (("participant", "spectacle"),)
verbose_name = "voeu"
verbose_name_plural = "voeux"

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block extra_head %}
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}
<h2>État des inscriptions BdA</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block extra_head %}
<script type="text/javascript" src="{% static 'vendor/jquery/jquery-ui.min.js' %}" ></script>

View file

@ -6,23 +6,25 @@ pour les spectacles suivants :
- 1 place pour {{ place }}{% endfor %}
*Paiement*
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 lensemble 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 lensemble 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.
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.
*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 :
https://bda.ens.fr/lequipe/charte-bda/
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.
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.
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.
En vous souhaitant de très beaux spectacles tout au long de l'année,
--

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}
<h2>{{ spectacle }}</h2>

View file

@ -11,19 +11,9 @@
<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

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{%block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}
<h2>Inscription à une revente</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles%}
{% block realcontent %}
<h2>Inscriptions pour BdA-Revente</h2>

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block realcontent %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block extra_head %}
<link type="text/css" rel="stylesheet" href="{% static "bda/css/bda.css" %}" />

View file

@ -1,80 +1,74 @@
from django.urls import re_path
from django.conf.urls import url
from bda import views
from bda.views import SpectacleListView
from gestioncof.decorators import buro_required
urlpatterns = [
re_path(
url(
r"^inscription/(?P<tirage_id>\d+)$",
views.inscription,
name="bda-tirage-inscription",
),
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(
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(
r"^spectacles/(?P<tirage_id>\d+)$",
buro_required(SpectacleListView.as_view()),
name="bda-liste-spectacles",
),
re_path(
url(
r"^spectacles/(?P<tirage_id>\d+)/(?P<spectacle_id>\d+)$",
views.spectacle,
name="bda-spectacle",
),
re_path(
url(
r"^spectacles/unpaid/(?P<tirage_id>\d+)$",
views.UnpaidParticipants.as_view(),
name="bda-unpaid",
),
re_path(
url(
r"^spectacles/autocomplete$",
views.spectacle_autocomplete,
name="bda-spectacle-autocomplete",
),
re_path(
url(
r"^participants/autocomplete$",
views.participant_autocomplete,
name="bda-participant-autocomplete",
),
# Urls BdA-Revente
re_path(
url(
r"^revente/(?P<tirage_id>\d+)/manage$",
views.revente_manage,
name="bda-revente-manage",
),
re_path(
url(
r"^revente/(?P<tirage_id>\d+)/subscribe$",
views.revente_subscribe,
name="bda-revente-subscribe",
),
re_path(
url(
r"^revente/(?P<tirage_id>\d+)/tirages$",
views.revente_tirages,
name="bda-revente-tirages",
),
re_path(
url(
r"^revente/(?P<spectacle_id>\d+)/buy$",
views.revente_buy,
name="bda-revente-buy",
),
re_path(
url(
r"^revente/(?P<revente_id>\d+)/confirm$",
views.revente_confirm,
name="bda-revente-confirm",
),
re_path(
url(
r"^revente/(?P<tirage_id>\d+)/shotgun$",
views.revente_shotgun,
name="bda-revente-shotgun",
),
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"
),
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"),
]

View file

@ -114,7 +114,6 @@ 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 = []
@ -125,8 +124,6 @@ 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)
@ -135,12 +132,6 @@ 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(
@ -156,7 +147,6 @@ def places(request, tirage_id):
"places": filtered_places,
"tirage": tirage,
"total": total,
"unpaid": unpaid,
},
)

View file

@ -0,0 +1 @@
default_app_config = "bds.apps.BdsConfig"

View file

@ -1,4 +1,5 @@
from django.apps import AppConfig, apps as global_apps
from django import apps as global_apps
from django.apps import AppConfig
from django.db.models import Q
from django.db.models.signals import post_migrate

View file

@ -1,4 +1,4 @@
{% load static %}
{% load staticfiles %}
{% load bulma_utils %}
<!DOCTYPE html>

View file

@ -31,7 +31,7 @@ class TestHomeView(TestCase):
user, backend="django.contrib.auth.backends.ModelBackend"
)
resp = self.client.get(reverse("bds:home"))
self.assertEqual(resp.status_code, 200)
self.assertEquals(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.assertEqual(resp.status_code, 403)
self.assertEquals(resp.status_code, 403)
# Burô user GET
give_bds_buro_permissions(user)
resp = client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertEquals(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.assertEqual(resp.status_code, 403)
self.assertEquals(resp.status_code, 403)
# Burô user GET
give_bds_buro_permissions(user)
resp = client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertEquals(resp.status_code, 200)

View file

@ -1,7 +1,6 @@
from django.urls import path
from bds import views
from shared.views import SympaListView
app_name = "bds"
urlpatterns = [
@ -22,10 +21,4 @@ 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",
),
]

View file

@ -1,63 +0,0 @@
# 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"
),
),
]

View file

@ -72,13 +72,9 @@ 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
@ -91,13 +87,9 @@ 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
@ -126,9 +118,7 @@ class ExtraField(models.Model):
field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE)
class Meta:
constraints = [
models.UniqueConstraint(fields=["event", "name"], name="unique_extra_field")
]
unique_together = [["event", "name"]]
class ExtraFieldContent(models.Model):
@ -147,13 +137,9 @@ 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
@ -177,13 +163,9 @@ 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)

View file

@ -1,15 +1,8 @@
"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""
import os
import django
from channels.routing import get_default_application
from channels.asgi import get_channel_layer
if "DJANGO_SETTINGS_MODULE" not in os.environ:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings")
django.setup()
application = get_default_application()
channel_layer = get_channel_layer()

View file

@ -1,20 +1,3 @@
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from channels.routing import include
from kfet.routing import KFRouter
application = ProtocolTypeRouter(
{
# WebSocket chat handler
"websocket": AuthMiddlewareStack(
URLRouter(
[
path("ws/k-fet", KFRouter),
]
)
),
"http": get_asgi_application(),
}
)
routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")]

View file

@ -67,8 +67,8 @@ INSTALLED_APPS = (
"wagtail.images",
"wagtail.search",
"wagtail.admin",
"wagtail",
# "wagtail.contrib.modeladmin",
"wagtail.core",
"wagtail.contrib.modeladmin",
"wagtail.contrib.routable_page",
"wagtailmenus",
"modelcluster",
@ -85,6 +85,7 @@ MIDDLEWARE = (
+ MIDDLEWARE
+ [
"djconfig.middleware.DjConfigMiddleware",
"wagtail.core.middleware.SiteMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
]
)
@ -108,8 +109,6 @@ 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
# ---
@ -148,7 +147,7 @@ CACHES = {
CHANNEL_LAYERS = {
"default": {
"BACKEND": "shared.channels.ChannelLayer",
"BACKEND": "asgi_redis.RedisChannelLayer",
"CONFIG": {
"hosts": [
(
@ -161,9 +160,11 @@ CHANNEL_LAYERS = {
)
]
},
"ROUTING": "gestioasso.routing.routing",
}
}
# ---
# reCAPTCHA settings
# https://github.com/praekelt/django-recaptcha
@ -205,7 +206,7 @@ MAIL_DATA = {
"REPLYTO": "cof@ens.fr",
},
"rappels": {"FROM": "Le BdA <bda@ens.fr>", "REPLYTO": "Le BdA <bda@ens.fr>"},
"kfet": {
"rappel_negatif": {
"FROM": "La K-Fêt <chefs-k-fet@ens.fr>",
"REPLYTO": "La K-Fêt <chefs-k-fet@ens.fr>",
},

View file

@ -101,7 +101,7 @@ TEMPLATES = [
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": DBNAME,
"USER": DBUSER,
"PASSWORD": DBPASSWD,
@ -111,7 +111,6 @@ DATABASES = {
SITE_ID = 1
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# ---
# Internationalization

View file

@ -27,9 +27,6 @@ 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"]
@ -50,7 +47,8 @@ CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"
# Use the default in memory asgi backend for local development
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
"BACKEND": "asgiref.inmemory.ChannelLayer",
"ROUTING": "gestioasso.routing.routing",
}
}

View file

@ -1,197 +0,0 @@
"""
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

View file

@ -1,324 +0,0 @@
"""
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

View file

@ -1,7 +1,6 @@
"""
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
@ -57,13 +56,12 @@ 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 urls must be last, as catch-all)
if "wagtail" in settings.INSTALLED_APPS:
from wagtail import urls as wagtail_urls
# Wagtail URLs (wagtail.core urls must be last, as catch-all)
if "wagtail.core" in settings.INSTALLED_APPS:
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 += [

View file

@ -0,0 +1 @@
default_app_config = "gestioncof.apps.GestioncofConfig"

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from gestioncof.models import (
Club,
@ -130,33 +130,15 @@ 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 COF"
is_cof.short_description = "Membre du 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",
@ -164,9 +146,7 @@ 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 + (

View file

@ -0,0 +1 @@
default_app_config = "gestioncof.cms.apps.COFCMSAppConfig"

View file

@ -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.fields
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.images.blocks
from django.db import migrations, models
@ -72,14 +72,18 @@ class Migration(migrations.Migration):
blank=True, null=True, verbose_name="Description rapide"
),
),
("body", wagtail.fields.RichTextField(verbose_name="Contenu")),
("body", wagtail.core.fields.RichTextField(verbose_name="Contenu")),
(
"body_fr",
wagtail.fields.RichTextField(null=True, verbose_name="Contenu"),
wagtail.core.fields.RichTextField(
null=True, verbose_name="Contenu"
),
),
(
"body_en",
wagtail.fields.RichTextField(null=True, verbose_name="Contenu"),
wagtail.core.fields.RichTextField(
null=True, verbose_name="Contenu"
),
),
(
"is_event",
@ -134,40 +138,46 @@ class Migration(migrations.Migration):
to="wagtailcore.Page",
),
),
("body", wagtail.fields.RichTextField(verbose_name="Description")),
("body", wagtail.core.fields.RichTextField(verbose_name="Description")),
(
"body_fr",
wagtail.fields.RichTextField(null=True, verbose_name="Description"),
wagtail.core.fields.RichTextField(
null=True, verbose_name="Description"
),
),
(
"body_en",
wagtail.fields.RichTextField(null=True, verbose_name="Description"),
wagtail.core.fields.RichTextField(
null=True, verbose_name="Description"
),
),
(
"links",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(required=True),
wagtail.core.blocks.URLBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(
required=True
),
("texte", wagtail.blocks.CharBlock()),
),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
@ -176,29 +186,31 @@ class Migration(migrations.Migration):
),
(
"links_fr",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(required=True),
wagtail.core.blocks.URLBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(
required=True
),
("texte", wagtail.blocks.CharBlock()),
),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
@ -208,29 +220,31 @@ class Migration(migrations.Migration):
),
(
"links_en",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(required=True),
wagtail.core.blocks.URLBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(
required=True
),
("texte", wagtail.blocks.CharBlock()),
),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
@ -272,17 +286,17 @@ class Migration(migrations.Migration):
),
(
"introduction",
wagtail.fields.RichTextField(verbose_name="Introduction"),
wagtail.core.fields.RichTextField(verbose_name="Introduction"),
),
(
"introduction_fr",
wagtail.fields.RichTextField(
wagtail.core.fields.RichTextField(
null=True, verbose_name="Introduction"
),
),
(
"introduction_en",
wagtail.fields.RichTextField(
wagtail.core.fields.RichTextField(
null=True, verbose_name="Introduction"
),
),
@ -315,27 +329,27 @@ class Migration(migrations.Migration):
),
(
"body",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"heading",
wagtail.blocks.CharBlock(classname="full title"),
wagtail.core.blocks.CharBlock(classname="full title"),
),
("paragraph", wagtail.blocks.RichTextBlock()),
("paragraph", wagtail.core.blocks.RichTextBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"iframe",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(
wagtail.core.blocks.URLBlock(
"Adresse de la page"
),
),
(
"height",
wagtail.blocks.CharBlock(
wagtail.core.blocks.CharBlock(
"Hauteur (en pixels)"
),
),
@ -347,27 +361,27 @@ class Migration(migrations.Migration):
),
(
"body_fr",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"heading",
wagtail.blocks.CharBlock(classname="full title"),
wagtail.core.blocks.CharBlock(classname="full title"),
),
("paragraph", wagtail.blocks.RichTextBlock()),
("paragraph", wagtail.core.blocks.RichTextBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"iframe",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(
wagtail.core.blocks.URLBlock(
"Adresse de la page"
),
),
(
"height",
wagtail.blocks.CharBlock(
wagtail.core.blocks.CharBlock(
"Hauteur (en pixels)"
),
),
@ -380,27 +394,27 @@ class Migration(migrations.Migration):
),
(
"body_en",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
[
(
"heading",
wagtail.blocks.CharBlock(classname="full title"),
wagtail.core.blocks.CharBlock(classname="full title"),
),
("paragraph", wagtail.blocks.RichTextBlock()),
("paragraph", wagtail.core.blocks.RichTextBlock()),
("image", wagtail.images.blocks.ImageChooserBlock()),
(
"iframe",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"url",
wagtail.blocks.URLBlock(
wagtail.core.blocks.URLBlock(
"Adresse de la page"
),
),
(
"height",
wagtail.blocks.CharBlock(
wagtail.core.blocks.CharBlock(
"Hauteur (en pixels)"
),
),
@ -434,17 +448,17 @@ class Migration(migrations.Migration):
),
(
"introduction",
wagtail.fields.RichTextField(verbose_name="Introduction"),
wagtail.core.fields.RichTextField(verbose_name="Introduction"),
),
(
"introduction_fr",
wagtail.fields.RichTextField(
wagtail.core.fields.RichTextField(
null=True, verbose_name="Introduction"
),
),
(
"introduction_en",
wagtail.fields.RichTextField(
wagtail.core.fields.RichTextField(
null=True, verbose_name="Introduction"
),
),

View file

@ -1,7 +1,7 @@
# Generated by Django 2.2.8 on 2019-12-20 16:22
import wagtail.blocks
import wagtail.fields
import wagtail.core.blocks
import wagtail.core.fields
from django.db import migrations
@ -14,26 +14,26 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
@ -44,26 +44,26 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links_en",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
@ -75,26 +75,26 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links_fr",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),

View file

@ -1,7 +1,7 @@
# Generated by Django 2.2.15 on 2020-08-29 21:14
import wagtail.blocks
import wagtail.fields
import wagtail.core.blocks
import wagtail.core.fields
from django.db import migrations
@ -14,35 +14,35 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"info",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("nom", wagtail.blocks.CharBlock(required=False)),
("texte", wagtail.blocks.CharBlock(required=True)),
("nom", wagtail.core.blocks.CharBlock(required=False)),
("texte", wagtail.core.blocks.CharBlock(required=True)),
]
),
),
@ -53,35 +53,35 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links_en",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"info",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("nom", wagtail.blocks.CharBlock(required=False)),
("texte", wagtail.blocks.CharBlock(required=True)),
("nom", wagtail.core.blocks.CharBlock(required=False)),
("texte", wagtail.core.blocks.CharBlock(required=True)),
]
),
),
@ -93,35 +93,35 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="cofdirectoryentrypage",
name="links_fr",
field=wagtail.fields.StreamField(
field=wagtail.core.fields.StreamField(
[
(
"lien",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("url", wagtail.blocks.URLBlock(required=True)),
("texte", wagtail.blocks.CharBlock()),
("url", wagtail.core.blocks.URLBlock(required=True)),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"contact",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
(
"email",
wagtail.blocks.EmailBlock(required=True),
wagtail.core.blocks.EmailBlock(required=True),
),
("texte", wagtail.blocks.CharBlock()),
("texte", wagtail.core.blocks.CharBlock()),
]
),
),
(
"info",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
[
("nom", wagtail.blocks.CharBlock(required=False)),
("texte", wagtail.blocks.CharBlock(required=True)),
("nom", wagtail.core.blocks.CharBlock(required=False)),
("texte", wagtail.core.blocks.CharBlock(required=True)),
]
),
),

View file

@ -1,203 +0,0 @@
# 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,
),
),
]

View file

@ -1,11 +1,12 @@
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db import models
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.fields import RichTextField, StreamField
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page
from wagtail.images.blocks import ImageChooserBlock
from wagtail.models import Page
from wagtail.images.edit_handlers import ImageChooserPanel
# Page pouvant afficher des actualités
@ -68,11 +69,10 @@ class COFPage(Page):
("paragraph", blocks.RichTextBlock()),
("image", ImageChooserBlock()),
("iframe", IFrameBlock()),
],
use_json_field=True,
]
)
content_panels = Page.content_panels + [FieldPanel("body")]
content_panels = Page.content_panels + [StreamFieldPanel("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 + [
FieldPanel("image"),
ImageChooserPanel("image"),
FieldPanel("chapo"),
FieldPanel("body", classname="full"),
FieldPanel("is_event"),
@ -204,7 +204,6 @@ class COFDirectoryEntryPage(Page):
),
],
blank=True,
use_json_field=True,
)
image = models.ForeignKey(
@ -217,9 +216,9 @@ class COFDirectoryEntryPage(Page):
)
content_panels = Page.content_panels + [
FieldPanel("image"),
ImageChooserPanel("image"),
FieldPanel("body", classname="full"),
FieldPanel("links"),
StreamFieldPanel("links"),
]
subpage_types = []

View file

@ -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 %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% 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 %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% 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 %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% 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 %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% 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 %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% 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 %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% 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 %}{% if key != 'page' %}&amp;{{ key }}={{ value }}{% endif %}{% 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 %}{% ifnotequal key 'page' %}&amp;{{ key }}={{ value }}{% endifnotequal %}{% endfor %}">{% trans "Actualités plus anciennes" %}</a>
{% endif %}
</section>
{% endblock %}

View file

@ -2,7 +2,7 @@ from datetime import date, timedelta
from django import template
from django.utils import formats, timezone
from django.utils.translation import gettext as _
from django.utils.translation import ugettext as _
from ..models import COFActuPage, COFRootPage

View file

@ -1,15 +1,12 @@
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)

View file

@ -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 COF.
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du COF.
- Si l'utilisteur n'est pas connecté, il est redirigé vers la page de
connexion
@ -33,31 +33,6 @@ 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ô.
@ -83,33 +58,6 @@ 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:
@ -131,18 +79,3 @@ 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

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from djconfig.forms import ConfigForm
from bda.models import Spectacle
@ -276,15 +276,12 @@ class RegistrationProfileForm(forms.ModelForm):
self.fields["mailing_bda_revente"].initial = True
self.fields["mailing_unernestaparis"].initial = True
class Meta:
model = CofProfile
fields = [
self.fields.keyOrder = [
"login_clipper",
"phone",
"occupation",
"departement",
"is_cof",
"is_kfet",
"type_cotiz",
"mailing_cof",
"mailing_bda",
@ -293,18 +290,21 @@ class RegistrationProfileForm(forms.ModelForm):
"comments",
]
class RegistrationKFProfileForm(forms.ModelForm):
class Meta:
model = CofProfile
fields = [
fields = (
"login_clipper",
"phone",
"occupation",
"departement",
"is_kfet",
"is_cof",
"type_cotiz",
"mailing_cof",
"mailing_bda",
"mailing_bda_revente",
"mailing_unernestaparis",
"comments",
]
)
STATUS_CHOICES = (
@ -458,20 +458,3 @@ 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
)

View file

@ -1,43 +0,0 @@
# 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"
),
),
]

View file

@ -1,13 +0,0 @@
# 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 = []

View file

@ -1,23 +0,0 @@
# 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"),
),
]

View file

@ -1,32 +0,0 @@
# 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"
),
),
]

View file

@ -1,18 +0,0 @@
# 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"),
),
]

View file

@ -1,14 +1,8 @@
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.template import loader
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from bda.models import Spectacle
from shared.utils import choices_length
@ -55,12 +49,8 @@ class CofProfile(models.Model):
login_clipper = models.CharField(
"Login clipper", max_length=32, blank=True, unique=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
)
is_cof = models.BooleanField("Membre du COF", default=False)
date_adhesion = models.DateField("Date d'adhésion", blank=True, null=True)
phone = models.CharField("Téléphone", max_length=20, blank=True)
occupation = models.CharField(
_("Occupation"),
@ -85,7 +75,6 @@ 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
)
@ -100,46 +89,6 @@ 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):
@ -245,12 +194,8 @@ 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)
@ -302,12 +247,8 @@ 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" % (

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from django_cas_ng.signals import cas_user_authenticated

View file

@ -701,9 +701,6 @@ header a:active {
.user-is-cof {
color : #ADE297;
}
.user-is-kfet {
color : #FF8C00;
}
.user-is-not-cof {
color: #EE8585;
}

View file

@ -1,4 +1,4 @@
{% load static %}
{% load staticfiles %}
<!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">

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% block realcontent %}
<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>
<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>
{% endblock %}

View file

@ -10,12 +10,10 @@
{% endblock %}
</a>
<div class="secondary">
{% if user.is_authenticated %}
<span class="hidden-xxs">&nbsp;&nbsp;|&nbsp; </span>
<span><a href="{% url "cof-logout" %}">Se déconnecter&nbsp;<span class="glyphicon glyphicon-log-out"></span></a></span>
{% endif %}
</div>
<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>
<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>
</div><!-- /.container -->
</header>

View file

@ -1,19 +0,0 @@
{% 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 %}

View file

@ -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 or user.profile.is_chef %}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 %}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,10 +50,7 @@
<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>
{% 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>
<li><a href="https://cof.ens.fr/gestion/k-fet/le-calendrier/">Calendrier</a></li>
{% if perms.kfet.is_team %}
<li><a href="{% url 'kfet.kpsul' %}">K-Psul</a></li>
{% endif %}
@ -71,23 +68,9 @@
{% 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>
@ -132,7 +115,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érent⋅e⋅s COF</a></li>
<li><a href="{% url "reset_comptes" %}">Remise à zéro adhérents COF</a></li>
</ul>
</div>
</div>

View file

@ -1,10 +1,10 @@
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 compte instagram : https://www.instagram.com/cof_ulm
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
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ô les lundi, mardi, jeudi et vendredi de 12h à 14h et du lundi au jeudi de 18h30 à 19h30.
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.
Retrouvez tout les évènements organisés par le COF et ses clubs ici : https://calendrier.dgnum.eu/.
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.
Amicalement,

View file

@ -1,21 +0,0 @@
{% 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>

View file

@ -1,8 +0,0 @@
{% 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 %}

View file

@ -1,28 +0,0 @@
{% 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 %}

View file

@ -1,5 +0,0 @@
{% 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 %}

View file

@ -1,5 +1,5 @@
{% extends "base_title.html" %}
{% load static %}
{% load staticfiles %}
{% block page_size %}col-sm-8{% endblock %}

View file

@ -1,4 +1,4 @@
{% load static %}
{% load staticfiles %}
<script type="text/javascript">
var supernifty_tristate = function() {
var

View file

@ -194,9 +194,7 @@ class RegistrationViewTests(ViewTestCaseMixin, TestCase):
)
er = e.eventregistration_set.get(user=self.users["user"])
self.assertQuerysetEqual(
er.options.all(), map(repr, [oc1, oc3]), transform=repr, ordered=False
)
self.assertQuerysetEqual(er.options.all(), map(repr, [oc1, oc3]), ordered=False)
self.assertCountEqual(
er.comments.values_list("content", flat=True), ["comment 1"]
)
@ -301,10 +299,10 @@ class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCa
raise ValueError("Unexpected section name: {}".format(section.name))
self.assertQuerysetEqual(
others, map(str, expected_others), transform=str, ordered=False
others, map(str, expected_others), ordered=False, transform=str
)
self.assertQuerysetEqual(
members, map(str, expected_members), transform=str, ordered=False
members, map(str, expected_members), ordered=False, transform=str
)
self.assertSetEqual(
set(clippers), set(map(LDAPSearch().result_verbose_name, expected_clippers))
@ -486,7 +484,7 @@ class ExportMembersViewTests(CSVResponseMixin, ViewTestCaseMixin, TestCase):
u1.last_name = "last"
u1.email = "user@mail.net"
u1.save()
u1.profile.date_adhesion_cof = date(2023, 5, 22)
u1.profile.date_adhesion = date(2023, 5, 22)
u1.profile.phone = "0123456789"
u1.profile.departement = "Dept"
u1.profile.save()
@ -650,10 +648,7 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["owned_clubs"],
map(repr, [self.c1, self.c2]),
transform=repr,
ordered=False,
r.context["owned_clubs"], map(repr, [self.c1, self.c2]), ordered=False
)
@ -955,10 +950,7 @@ 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]),
transform=repr,
ordered=False,
er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]), ordered=False
)
# TODO: Make the view care about comments.
# self.assertQuerysetEqual(
@ -983,9 +975,7 @@ 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]), transform=repr, ordered=False
)
self.assertQuerysetEqual(er.options.all(), map(repr, [self.oc3]), ordered=False)
# TODO: Make the view care about comments.
# self.assertQuerysetEqual(
# er.comments.all(), map(repr, []),
@ -1039,10 +1029,7 @@ class EventStatusViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["user_choices"],
map(repr, expected),
transform=repr,
ordered=False,
r.context["user_choices"], map(repr, expected), ordered=False
)
def test_filter_none(self):
@ -1109,10 +1096,7 @@ 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]),
transform=repr,
ordered=False,
a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]), ordered=False
)
def test_post_edit(self):
@ -1131,9 +1115,7 @@ 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]), transform=repr, ordered=False
)
self.assertQuerysetEqual(a.answers.all(), map(repr, [self.qa3]), ordered=False)
def test_post_delete(self):
a = self.s.surveyanswer_set.create(user=self.users["user"])
@ -1214,10 +1196,7 @@ class SurveyStatusViewTests(ViewTestCaseMixin, TestCase):
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["user_answers"],
map(repr, expected),
transform=repr,
ordered=False,
r.context["user_answers"], map(repr, expected), ordered=False
)
def test_filter_none(self):

View file

@ -4,16 +4,6 @@ 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"),
@ -80,7 +70,6 @@ registration_patterns = [
views.RegistrationAutocompleteView.as_view(),
name="cof.registration.autocomplete",
),
path("self_kf", views.self_kf_registration, name="self.kf_registration"),
]
urlpatterns = [
@ -100,7 +89,6 @@ urlpatterns = [
name="cof-user-autocomplete",
),
path("config", views.ConfigUpdate.as_view(), name="config.edit"),
path("carte", views.carte_kf, name="profile.carte"),
# -----
# Authentification
# -----
@ -174,8 +162,4 @@ urlpatterns = [
# Clubs
# -----
path("clubs/", include(clubs_patterns)),
# -----
# Sympa export
# -----
path("sympa/", include(sympa_patterns)),
]

View file

@ -1,6 +1,7 @@
import csv
import uuid
from datetime import timedelta
from datetime import date, timedelta
from smtplib import SMTPRecipientsRefused
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
from django.contrib import messages
@ -13,26 +14,20 @@ from django.contrib.auth.views import (
redirect_to_login,
)
from django.contrib.sites.models import Site
from django.db.models import Q
from django.core.mail import send_mail
from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.template import loader
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_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,
ChefRequiredMixin,
buro_required,
chef_required,
cof_required,
kfet_required,
)
from gestioncof.decorators import BuroRequiredMixin, buro_required, cof_required
from gestioncof.forms import (
CalendarForm,
ClubsForm,
@ -43,11 +38,9 @@ from gestioncof.forms import (
GestioncofConfigForm,
PhoneForm,
ProfileForm,
RegistrationKFProfileForm,
RegistrationPassUserForm,
RegistrationProfileForm,
RegistrationUserForm,
SubscribForm,
SurveyForm,
SurveyStatusFilterForm,
UserForm,
@ -93,9 +86,7 @@ class ResetComptes(BuroRequiredMixin, TemplateView):
nb_adherents = CofProfile.objects.filter(is_cof=True).count()
CofProfile.objects.update(
is_cof=False,
is_kfet=False,
date_adhesion_cof=None,
date_adhesion_kfet=None,
date_adhesion=None,
mailing_cof=False,
mailing_bda=False,
mailing_bda_revente=False,
@ -430,20 +421,13 @@ 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
@chef_required
@buro_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:
@ -465,27 +449,21 @@ 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:])
if is_buro:
# profile
profile_form = RegistrationProfileForm(
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()
else:
profile_form = RegistrationKFProfileForm(
initial={"login_clipper": login_clipper}
)
registration_set_ro_fields(user_form, profile_form)
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)
if is_buro:
profile_form = RegistrationProfileForm(instance=profile)
registration_set_ro_fields(user_form, profile_form)
# events
current_registrations = []
for event in events:
@ -496,25 +474,16 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
except EventRegistration.DoesNotExist:
current_registrations.append(None)
event_formset = EventFormset(
events=events,
prefix="events",
current_registrations=current_registrations,
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)
elif not login_clipper:
# new user
user_form = RegistrationPassUserForm()
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,
"gestioncof/registration_form.html",
@ -527,22 +496,38 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
"clubs_form": clubs_form,
},
)
else:
return render(
def notify_new_member(request, member: User):
if not member.email:
messages.warning(
request,
"gestioncof/registration_kf_form.html",
{
"member": member,
"login_clipper": login_clipper,
"user_form": user_form,
"profile_form": profile_form,
},
"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
),
)
@chef_required
@buro_required
def registration(request):
is_buro = request.user.profile.is_buro
if request.POST:
request_dict = request.POST.copy()
member = None
@ -556,15 +541,10 @@ def registration(request):
user_form = RegistrationPassUserForm(request_dict)
else:
user_form = RegistrationUserForm(request_dict)
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)
event_formset = EventFormset(events=events, data=request_dict, prefix="events")
if "user_exists" in request_dict and request_dict["user_exists"]:
username = request_dict["username"]
try:
@ -585,25 +565,20 @@ 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
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())
if (
profile_form.is_valid()
and event_formset.is_valid()
and clubs_form.is_valid()
):
# Enregistrement du profil
profile = profile_form.save()
if is_buro:
if profile.is_cof:
profile.make_adh_cof(request, was_cof)
if profile.is_cof and not was_cof:
notify_new_member(request, member)
profile.date_adhesion = date.today()
profile.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:
@ -624,13 +599,9 @@ def registration(request):
) = EventRegistration.objects.get_or_create(
user=member, event=form.event
)
update_event_form_comments(
form.event, form, current_registration
)
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.paid = form.cleaned_data["status"] == "paid"
current_registration.save()
# if form.event.title == "Mega 15" and created_reg:
# field = EventCommentField.objects.get(
@ -662,12 +633,11 @@ def registration(request):
member.get_full_name(), member.email
)
)
if is_buro and profile.is_cof:
if profile.is_cof:
msg += "\nIl est désormais membre du COF n°{:d} !".format(
member.profile.id
)
messages.success(request, msg, extra_tags="safe")
if is_buro:
return render(
request,
"gestioncof/registration_post.html",
@ -680,73 +650,10 @@ def registration(request):
"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
# -----
@ -800,7 +707,7 @@ def export_members(request):
response["Content-Disposition"] = "attachment; filename=membres_cof.csv"
writer = csv.writer(response)
for profile in CofProfile.objects.filter(Q(is_cof=True) | Q(is_kfet=True)).all():
for profile in CofProfile.objects.filter(is_cof=True).all():
user = profile.user
bits = [
user.id,
@ -811,10 +718,8 @@ 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_cof,
profile.date_adhesion_kfet,
profile.date_adhesion,
]
writer.writerow([str(bit) for bit in bits])
@ -1070,6 +975,6 @@ class UserAutocompleteView(BuroRequiredMixin, Select2QuerySetView):
search_fields = ("username", "first_name", "last_name")
class RegistrationAutocompleteView(ChefRequiredMixin, AutocompleteView):
class RegistrationAutocompleteView(BuroRequiredMixin, AutocompleteView):
template_name = "gestioncof/search_results.html"
search_composer = cof_autocomplete

View file

@ -1,2 +1,3 @@
default_app_config = "kfet.apps.KFetConfig"
KFET_DELETED_TRIGRAMME = "☠☠☠"
KFET_DELETED_USERNAME = "kfet_deleted_user"

View file

@ -1,2 +1,4 @@
default_app_config = "kfet.auth.apps.KFetAuthConfig"
KFET_GENERIC_USERNAME = "kfet_genericteam"
KFET_GENERIC_TRIGRAMME = "GNR"

View file

@ -1,6 +1,6 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
class KFetAuthConfig(AppConfig):

View file

@ -1,5 +1,5 @@
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from shared.forms import ProtectedModelForm

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
KFET_APP_LABELS = ["kfet", "kfetauth"]

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext as _
from .utils import get_kfet_generic_user

View file

@ -40,7 +40,6 @@ class UserGroupFormTests(TestCase):
self.assertQuerysetEqual(
groups_field.queryset,
[repr(g.group_ptr) for g in self.kfet_groups],
transform=repr,
ordered=False,
)

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_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

View file

@ -0,0 +1 @@
default_app_config = "kfet.cms.apps.KFetCMSAppConfig"

View file

@ -1,6 +1,6 @@
from django.templatetags.static import static
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.utils.html import format_html
from wagtail import hooks
from wagtail.core import hooks
@hooks.register("insert_editor_css")

View file

@ -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.models import Page, Site
from wagtail.core.models import Page, Site
class Command(BaseCommand):

View file

@ -2,8 +2,8 @@
from __future__ import unicode_literals
import django.db.models.deletion
import wagtail.blocks
import wagtail.fields
import wagtail.core.blocks
import wagtail.core.fields
import wagtail.snippets.blocks
from django.db import migrations, models
@ -41,20 +41,20 @@ class Migration(migrations.Migration):
),
(
"content",
wagtail.fields.StreamField(
wagtail.core.fields.StreamField(
(
(
"rich",
wagtail.blocks.RichTextBlock(label="Éditeur"),
wagtail.core.blocks.RichTextBlock(label="Éditeur"),
),
("carte", kfet.cms.models.MenuBlock()),
(
"group_team",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
(
(
"show_only",
wagtail.blocks.IntegerBlock(
wagtail.core.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.blocks.ListBlock(
wagtail.core.blocks.ListBlock(
wagtail.snippets.blocks.SnippetChooserBlock( # noqa
kfet.cms.models.MemberTeam
),
@ -75,22 +75,22 @@ class Migration(migrations.Migration):
),
(
"group",
wagtail.blocks.StreamBlock(
wagtail.core.blocks.StreamBlock(
(
(
"rich",
wagtail.blocks.RichTextBlock(
wagtail.core.blocks.RichTextBlock(
label="Éditeur"
),
),
("carte", kfet.cms.models.MenuBlock()),
(
"group_team",
wagtail.blocks.StructBlock(
wagtail.core.blocks.StructBlock(
(
(
"show_only",
wagtail.blocks.IntegerBlock( # noqa
wagtail.core.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.blocks.ListBlock(
wagtail.core.blocks.ListBlock(
wagtail.snippets.blocks.SnippetChooserBlock( # noqa
kfet.cms.models.MemberTeam # noqa
),

View file

@ -1,90 +0,0 @@
# 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",
),
),
]

View file

@ -1,9 +1,15 @@
from django.db import models
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 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 wagtail.snippets.blocks import SnippetChooserBlock
from wagtail.snippets.models import register_snippet
@ -37,7 +43,7 @@ class MemberTeam(models.Model):
FieldPanel("first_name"),
FieldPanel("last_name"),
FieldPanel("nick_name"),
FieldPanel("photo"),
ImageChooserPanel("photo"),
]
def __str__(self):
@ -91,9 +97,7 @@ class KFetStreamBlock(ChoicesStreamBlock):
class KFetPage(Page):
content = StreamField(
KFetStreamBlock, verbose_name=_("Contenu"), use_json_field=True
)
content = StreamField(KFetStreamBlock, verbose_name=_("Contenu"))
# Layout fields
@ -131,7 +135,7 @@ class KFetPage(Page):
# Panels
content_panels = Page.content_panels + [FieldPanel("content")]
content_panels = Page.content_panels + [StreamFieldPanel("content")]
layout_panel = [
FieldPanel("no_header"),

View file

@ -1,5 +1,4 @@
import djconfig
from asgiref.sync import sync_to_async
from django.core.exceptions import ValidationError
from django.db import models
@ -24,7 +23,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:
sync_to_async(djconfig.reload_maybe)()
djconfig.reload_maybe()
self._conf_init = True
def __getattr__(self, key):

View file

@ -1,18 +1,6 @@
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)

View file

@ -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 gettext_lazy as _
from django.utils.translation import ugettext_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 <= 7 and now.year - 1 or now.year
return now.month <= 8 and now.year - 1 or now.year
def get_promo_choices():
@ -118,11 +118,7 @@ class AccountForm(forms.ModelForm):
class Meta:
model = Account
fields = ["trigramme", "promo", "nickname"]
widgets = {
"trigramme": forms.TextInput(
attrs={"autocomplete": "off", "class": "trigramme_field"}
)
}
widgets = {"trigramme": forms.TextInput(attrs={"autocomplete": "off"})}
class AccountBalanceForm(forms.ModelForm):
@ -195,13 +191,7 @@ class CofForm(forms.ModelForm):
class Meta:
model = CofProfile
fields = ["login_clipper", "is_cof", "is_kfet", "departement"]
class CofKFForm(forms.ModelForm):
class Meta:
model = CofProfile
fields = ["is_kfet"]
fields = ["login_clipper", "is_cof", "departement"]
class UserForm(forms.ModelForm):
@ -356,7 +346,6 @@ class ArticleForm(forms.ModelForm):
fields = [
"name",
"is_sold",
"no_exte",
"hidden",
"price",
"stock",
@ -371,7 +360,6 @@ class ArticleRestrictForm(ArticleForm):
fields = [
"name",
"is_sold",
"no_exte",
"hidden",
"price",
"category",
@ -416,11 +404,7 @@ class KPsulAccountForm(forms.ModelForm):
fields = ["trigramme"]
widgets = {
"trigramme": forms.TextInput(
attrs={
"autocomplete": "off",
"spellcheck": "false",
"class": "trigramme_field",
}
attrs={"autocomplete": "off", "spellcheck": "false"}
)
}
@ -666,7 +650,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_1w = kwargs["initial"]["c_rec_1w"]
self.c_rec = kwargs["initial"]["c_rec"]
self.is_sold = kwargs["initial"]["is_sold"]

View file

@ -4,6 +4,7 @@ from __future__ import unicode_literals
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
@ -16,9 +17,7 @@ class Migration(migrations.Migration):
name="at",
field=models.DateTimeField(
auto_now_add=True,
default=datetime.datetime(
2016, 8, 29, 18, 35, 3, 419033, tzinfo=datetime.timezone.utc
),
default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc),
),
preserve_default=False,
),

View file

@ -1,20 +0,0 @@
# 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"
),
),
]

View file

@ -1,34 +0,0 @@
# 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