Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • mediatek/site-interludes
  • aeltheos/site-kwei
  • mediatek/site-kwei
3 results
Show changes
Commits on Source (67)
Showing
with 176 additions and 100 deletions
# Change Log
## Version 2.0.0-beta - Coming soon
- Added a form that allows admins to send emails to all users
- Added a form for users to submit activities
- Added a changeable caption for the planning
- Added fixes/improvement from 48h des jeux:
- bug fixed in activity submission form
- new validator that checks the number of slots for each activity in the planning
- fixed room display on activity page
- fixed planning info displayed on activity even when planning hidden
- added boolean field to show host email on activity
- added boolean field to separate showing slot on planning and next to activity
## Version 1.2.8 - 2021-05-06
- Added links to FAQ
- Added django-admin link to admin profile page
## Version 1.2.7 - 2021-05-03
- Fix broken discord link
......
......@@ -6,7 +6,8 @@ SECRET := interludes/secret.py
.PHONY: help
help: ## Show this help
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
@echo "make: list of useful targets :"
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
.PHONY: install
install: ## Install requirements
......@@ -37,8 +38,8 @@ start: install $(SECRET) migrate serve ## Install requirements, apply migrations
.PHONY: clean
clean: ## Remove migrations and delete database
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc" -delete
find . -path "*/migrations/*.py" -not -name "__init__.py" -not -path "*/venv/*" -delete
find . -path "*/migrations/*.pyc" -not -path "*/venv/*" -delete
rm $(DB)
.PHONY: test
......
......@@ -2,6 +2,8 @@
Ce répo contient le sites des interludes. Ce site est en ligne à [https://interludes.ens.fr](https://interludes.ens.fr).
Ce répo est une copie du répo initial sur le [git interne de l'ENS Ulm](https://git.eleves.ens.fr/dlesbre/site-interludes).
Ce répo est diffusé sous une [license MIT](https://choosealicense.com/licenses/mit/).
**Contenu:**
......@@ -17,7 +19,7 @@ Ce répo est diffusé sous une [license MIT](https://choosealicense.com/licenses
Pour installer toutes les dépendances et lancer le serveur :
git clone https://git.eleves.ens.fr/dlesbre/site-interludes.git &&
git clone https://github.com/dlesbre/site-interludes.git &&
cd site-interlude &&
python3 -m venv venv &&
source venv/bin/activate &&
......@@ -88,27 +90,29 @@ Le site se gère depuis deux pages d'administration:
- Utilisateurs - contient tous les utilisateurs et leur permissions. Pour donner les droits d'administrateur à quelqu'un il faut lui donner le statut superutilisateur (accès à l'admin du site) ET le statut équipe (accès à l'admin django)
- Paramêtres - les réglages du site, ils permettent:
- ouvrir/fermer la création de compte, les inscriptions
- ouvrir fermer le formulaire de proposition d'activités
- afficher/cacher le planning
- renseigner l'email de contact, les dates de l'événement, les dates d'inscription
- ajouter un message global au dessus de toutes les pages
- bloquer/autoriser l'envoi d'email globaux
- Activités - liste des activités prévues. C'est ici que vous pouvez rajouter/modifier les activités qui s'affichent sur la page activité.
Pour le moment il n'y a pas de formulaire qui permette aux orga de proposer une activité sur le site (on était passé par un appel à projet externe et on avait rempli les activités nous-même)
Un formulaire permet aux utilisateurs de proposer des activités directement. Ils vous faudra les relire et les valider ensuite manuellement pour qu'elles soient affichées sur le site.
- Crénaux - place une activité sur le planning. Une activité peut avoir plusieurs crénaux si elle a lieu plusieurs fois. Noter que les inscriptions se font à des crénaux et non a des activités.
- Participant - liste des gens inscrits et des informations sur leur inscription (ENS, repas choisi...)
- Choix d'activité - Liste de (participant, priorité, activité) indiquant les voeux des participant. Une fois que vous avez fait l'attribution, cocher les case "Obtenues" pour indiquer qui a eu quelle activité.
- celle du site [http://localhost:8000/admin_site/](http://localhost:8000/admin_site/)
- celle du site [http://localhost:8000/admin_pages/](http://localhost:8000/admin_pages/)
- permet d'exporter les différentes tables au format CSV
- affiche l'état du site (version, réglages actuels, différentes métriques)
- une prévisualisation du planning
- permet d'envoyer deux séries d'emails :
- une aux inscrits pour leur communiquer les activités qu'ils ont obtenus
- une aux orgas qui ont besoin de connaître la liste des participants à l'avance pour préparer leurs activités.
- permet l'écriture d'un mail à tous.
## En production
Le serveur a besoin d'être configuré pour HTTPS et d'être configuré pour livrer directement les fichiers situés des `/static/`.
Le serveur a besoin d'être configuré pour HTTPS et d'être configuré pour livrer directement les fichiers situés dans `/static/` et `/media/`.
1. Installer les dépendances `make install`
......@@ -126,13 +130,17 @@ Le serveur a besoin d'être configuré pour HTTPS et d'être configuré pour liv
A.K.A. la liste des trucs utiles que j'ai pas eu le temps d'ajouter
- Un formulaire pour proposer une activité directement sur le site
- Intégrer l'[algorithme de répartition](https://github.com/Imakoala/InterludesMatchings) dans le site au lieu de le faire tourner en externe à partir des export CSV et de remplir les résultats à la main
- Envoyer une concaténation de tous les emails aux admin (pour vérification, et pas juste en copie pour éviter le spam...)
- Générer la version PDF du planning automatiquement au lieu de la faire à base de captures d'écran
- Remplacer les templates HTML statiques par du rendu de fichier markdown éditable depuis la page d'admin (afin d'éviter de devoir refaire un pull à chaque petit changement)
## Liens divers
- [Le site des interludes 2021](https://interludes.ens.fr)
- [Le répo initial](https://git.eleves.ens.fr/dlesbre/site-interludes) sur le gitlab de l'ENS Ulm
- [Le github de l'algorithme de répartition](https://github.com/Imakoala/InterludesMatchings)
- [Le wiki de Paris-Saclay](https://wiki.crans.org/VieBdl/InterLudes) qui recensent les visuels, sites webs et photos des interludes passées.
- [Le wiki de Paris-Saclay](https://wiki.crans.org/VieBdl/InterLudes) qui recense les visuels, sites webs et photos des interludes passées.
- [Le gitlab du site des 48h des jeux](https://git.eleves.ens.fr/dlesbre/48h-des-jeux) un événement très similaire intra-ENS Ulm, c'est fork de ce répo.
- [Le site des 48h des jeux](https://48hdesjeux.cof.ens.fr/)
- [Le site du club jeu d'Ulm](https://jeux.cof.ens.fr/)
# Generated by Django 3.0.8 on 2021-03-21 17:30
# Generated by Django 3.2.7 on 2021-10-05 18:45
from django.db import migrations, models
import django.utils.timezone
......@@ -9,7 +9,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
......
{% extends 'base.html' %}
{% block "content" %}
<h2>Mot de passe oublié ?</h2>
<p>Saissisez votre adresse email ci-dessous pour recevoir un lien de réinitialisation du mot de passe.</p>
<h2>Mot de passe oublié ?</h2>
<p>Saissisez votre adresse email ci-dessous pour recevoir un lien de réinitialisation du mot de passe.</p>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<div class="flex">
<input type="submit" value="Valider">
<input type="submit" value="Valider">
<a class="button" href="{% url 'accounts:login' %}">Annuler</a>
</div>
</form>
</form>
{% endblock %}
......@@ -6,12 +6,12 @@
<h2>Saissisez un nouveau mot de passe</h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<div class="flex">
<input type="submit" value="Changer mon mot de passe">
<a class="button" href="{% url 'accounts:login' %}">Annuler</a>
</div>
{% csrf_token %}
{{ form.as_p }}
<div class="flex">
<input type="submit" value="Changer mon mot de passe">
<a class="button" href="{% url 'accounts:login' %}">Annuler</a>
</div>
</form>
{% else %}
......@@ -19,8 +19,8 @@
<h2>Lien invalide</h2>
<p>Le lien de réinitialisation est invalide, peut-être a-t-il déjà été utilisé.
Veuillez <a href="{% url 'accounts:password_reset' %}">demander un nouveau lien</a>.
Veuillez <a href="{% url 'accounts:password_reset' %}">demander un nouveau lien</a>.
</p>
{% endif %}
{% endblock %}
\ No newline at end of file
{% endblock %}
......@@ -9,19 +9,9 @@
<br>
<div class="flex">
<input type="submit" value="Valider">
<a class="button" href="{% url 'accounts:profile' %}">Annuler</a>
</div>
</form>
<h2>Changer mon mot de passe</h2>
<form method="post" action="{% url 'accounts:change_password' %}">
{% csrf_token %}
{{ password_form.as_html }}
<br>
<div class="flex">
<input type="submit" value="Valider">
<a class="button" href="{% url 'accounts:profile' %}">Annuler</a>
<a class="button" href="{% url 'profile' %}">Annuler</a>
</div>
</form>
<h2> Mot de Passe </h2>
<a href ="{% url 'account_reset_password'% }" rel="text/html"> Réinitialisez votre mot de passe. </a>
{% endblock %}
......@@ -5,17 +5,5 @@ from accounts import views
app_name = "accounts"
urlpatterns = [
path("login/", views.LoginView.as_view(), name="login"),
path("logout/", views.LogoutView.as_view(), name="logout"),
path("profile/", views.ProfileView.as_view(), name="profile"),
path("create/", views.CreateAccountView.as_view(), name="create"),
path("update/", views.UpdateAccountView.as_view(), name="update"),
path("change_password/", views.UpdatePasswordView.as_view(), name="change_password"),
path('activate/<uidb64>/<token>/', views.ActivateAccountView.as_view(), name='activate'),
path("password_reset/", views.ResetPasswordView.as_view(), name="password_reset"),
path(
"password_reset/<uidb64>/<token>/",
views.ResetPasswordConfirmView.as_view(),
name="password_reset_confirm"
),
]
......@@ -8,13 +8,12 @@ from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.urls import reverse, reverse_lazy
from django.template.loader import render_to_string
from django.views.generic import FormView, RedirectView, TemplateView, UpdateView, View
from django.views.generic import FormView, RedirectView, UpdateView, View
from django.shortcuts import render, redirect
from accounts import forms
from accounts.models import EmailUser
from accounts.tokens import email_token_generator
from home.models import InterludesActivityChoices
from site_settings.models import SiteSettings
def send_validation_email(request, user, subject, template):
......@@ -32,7 +31,7 @@ def send_validation_email(request, user, subject, template):
class LoginView(auth_views.LoginView):
"""Vue pour se connecter"""
template_name = "login.html"
redirect_authenticated_user = "accounts:profile"
redirect_authenticated_user = "profile"
form_class = forms.LoginForm
......@@ -49,26 +48,6 @@ class LogoutView(RedirectView):
return super().get_redirect_url(*args, **kwargs)
class ProfileView(LoginRequiredMixin, TemplateView):
"""Vue des actions de gestion de son profil"""
template_name = "profile.html"
redirect_field_name = "next"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
settings = SiteSettings.load()
if settings.activities_allocated:
my_choices = InterludesActivityChoices.objects.filter(
participant=self.request.user.profile,
accepted=True
)
else:
my_choices = InterludesActivityChoices.objects.filter(participant=self.request.user.profile)
context["my_choices"] = my_choices
return context
# ==============================
# Create Account
# ==============================
......@@ -117,7 +96,7 @@ class CreateAccountView(View):
class ActivateAccountView(RedirectView):
"""Vue d'activation de compte (lien envoyé par mail)"""
permanent = False
success_pattern_name = "accounts:profile"
success_pattern_name = "profile"
failure_pattern_name = "home"
def get_redirect_url(self, uidb64, token, *args, **kwargs):
......@@ -180,7 +159,7 @@ class UpdateAccountView(LoginRequiredMixin, UpdateView):
messages.info(self.request, 'Un lien vous a été envoyé par mail. Utilisez le pour valider la mise à jour.')
# return reverse("registration:email_confirmation_needed")
return reverse("accounts:profile")
return reverse("profile")
def form_valid(self, form):
messages.success(self.request, "Informations personnelles mises à jour")
......@@ -208,7 +187,7 @@ class UpdatePasswordView(LoginRequiredMixin, FormView):
form.apply()
messages.success(self.request, "Mot de passe mis à jour")
login(self.request, self.request.user)
return redirect("accounts:profile")
return redirect("profile")
# ==============================
......
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class AdminPagesConfig(AppConfig):
name = 'admin_pages'
from django import forms
from django.conf import settings
from django.db.models import TextChoices
from shared.forms import FormRenderMixin
class Recipients(TextChoices):
ALL = ("a", "tous les utilisateurs")
REGISTERED = ("b", "tous les inscrits")
class SendEmailForm(forms.Form):
"""Formulaire pour un envoie d'email
à tous les utilisateurs/inscrits"""
dest = forms.ChoiceField(
choices=Recipients.choices, required=True,
label="Envoyer à", initial=Recipients.REGISTERED,
)
subject = forms.CharField(
max_length=100, required=True,
label="Sujet", initial=settings.USER_EMAIL_SUBJECT_PREFIX,
strip=True
)
text = forms.CharField(
label="Contenu", strip=True, widget=forms.Textarea
)
from django.db import models
# Create your models here.
......@@ -10,15 +10,15 @@
<h2>Page d'administration</h2>
<p><strong>Version {{ constants.WEBSITE_FULL_VERSION }}</strong></p>
<div class="flex wrap">
<a class="button" href="{% url 'participants.csv' %}"><i class="fa fa-download"></i> Participants</a>
<a class="button" href="{% url 'activities.csv' %}"><i class="fa fa-download"></i> Activités</a>
<a class="button" href="{% url 'slots.csv' %}"><i class="fa fa-download"></i> Crénaux</a>
<a class="button" href="{% url 'activity_choices.csv' %}"><i class="fa fa-download"></i> Choix d'activités</a>
<a class="button" href="{% url 'admin_pages:participants.csv' %}"><i class="fa fa-download"></i> Participants</a>
<a class="button" href="{% url 'admin_pages:activities.csv' %}"><i class="fa fa-download"></i> Activités</a>
<a class="button" href="{% url 'admin_pages:slots.csv' %}"><i class="fa fa-download"></i> Crénaux</a>
<a class="button" href="{% url 'admin_pages:activity_choices.csv' %}"><i class="fa fa-download"></i> Choix d'activités</a>
</div>
<ul class="messagelist">
<li class="info">
Pour plus d'options d'accès et de modifications, aller à la <a href="{% url 'admin:index' %}">page d'administration de django</a>.
<br>N'y modifiez rien si vous n'êtes pas sur de ce que cela fait.
<br>N'y modifiez rien si vous n'êtes pas sûrs de ce que cela fait.
</li></ul>
<h2>Paramètres du site</h2>
......@@ -31,10 +31,12 @@
<li>Fermeture : {{ settings.inscriptions_end|default:"non fixée" }}</li>
</ul>
</li>
<li>Les emails des orgas sont {% if settings.show_host_emails %}affichés sur la page activité{% else %}masqués{% endif %}.</li>
<li>Le planning {% if settings.display_planning %}est affiché{% else %}n'est pas affiché{% endif %}.</li>
<li>La répartition des activités {% if settings.activities_allocated %}est effectuée et affichée{% else %}n'est pas faite/affichée{% endif %}.</li>
<li>{% if settings.global_message %}Un message global est affiché{% else %}Aucun message global{% endif %}.</li>
<li>Le lien du serveur discord {% if settings.discord_link %}est affiché{% else %}n'est pas affiché{% endif %}.</li>
<li>L'envoi d'email en masse est {% if settings.allow_mass_email %}activé{% else %}désactivé{% endif %}</li>
</ul>
<h2>Métriques</h2>
......@@ -44,22 +46,14 @@
<div class="qty">Participants</div>
<div class="nb_big">{{ metrics.participants }}</div>
</div>
<div class="stat">
<div class="qty">Ulm</div>
<div class="nb_small">{{ metrics.ulm }}</div>
</div>
<div class="stat">
<div class="qty">Lyon</div>
<div class="nb_small">{{ metrics.lyon }}</div>
</div>
<div class="stat">
<div class="qty">Rennes</div>
<div class="nb_small">{{ metrics.rennes }}</div>
</div>
<div class="stat">
<div class="qty">Paris-Saclay</div>
<div class="nb_small">{{ metrics.saclay }}</div>
</div>
<div class="stat">
<div class="qty">Extérieur</div>
<div class="nb_small">{{ metrics.exterieur }}</div>
</div>
<div class="stat">
<div class="qty">Non inscrits</div>
<div class="nb_small">{{ metrics.non_registered }}</div>
......@@ -69,7 +63,7 @@
<div class="nb_small">{{ metrics.sleeps }}</div>
</div>
</div>
<!--
<div class="flex wrap lines">
<div class="stat">
<div class="qty">Repas</div>
......@@ -100,7 +94,7 @@
<div class="nb_small">{{ metrics.meal6 }}</div>
</div>
</div>
-->
<div class="flex wrap lines">
<div class="stat">
<div class="qty">Activités</div>
......@@ -167,6 +161,10 @@
<h2>Prévisualisation du planning</h2>
<ul class="messagelist">
{{ planning_validation|safe }}
</ul>
<p>Vous pouver uploader une version PDF dans le réglages (depuis django-admin)</p>
{% include "_planning.html" %}
......@@ -191,7 +189,7 @@
if (confirm(
`${errors}Cette action va envoyer {{ user_email_nb }} emails.\nÊtes-vous sur de vouloir continuer ?`
))
window.location = "{% url 'email_users' %}";
window.location = "{% url 'admin_pages:email_users' %}";
}
{% endif %}
{% if not settings.orga_notified and settings.allow_mass_mail %}
......@@ -199,7 +197,7 @@
if (confirm(
`${errors}Cette action va envoyer {{ orga_email_nb }} emails.\nÊtes-vous sur de vouloir continuer ?`
))
window.location = "{% url 'email_orgas' %}";
window.location = "{% url 'admin_pages:email_orgas' %}";
}
{% endif %}
</script>
......@@ -237,4 +235,12 @@
<button class="button" onclick="mail_orgas();">Email aux orgas</button>
{% endif %}
</div>
{% endblock %}
\ No newline at end of file
<h2>Mail aux utilisateurs</h2>
<p>Écrire un mail aux utilisateurs (tous ou seulement les inscrits)</p>
<p>Évitez de spammer. N'envoyez que si vraiment nécessaire.</p>
<p><a class="button{% if not settings.allow_mass_mail %} disabled{% endif %}" href="{% url 'admin_pages:email_new' %}">Écrire un nouveau mail</a></p>
{% endblock %}
......@@ -9,7 +9,7 @@ Vous avez obtenu {{ my_choices|length }} activité(s) (sur {{ requested_activiti
{% for choice in my_choices %}
- {{ choice.slot }}{% if choice.slot.on_planning %} (le {{ choice.slot.start|date:"l à H:i" }}){% endif %}{% endfor %}{% if activities %}
Cette liste est également disponible sur la page "Mon compte" du site: {% url "accounts:profile" %}.
Cette liste est également disponible sur la page "Mon compte" du site: {% url "profile" %}.
Votre adresse email sera communiquée aux orgas pour les activités nécessitant préparation avant l'événement.{% endif %}
{% else %}
Vous n'aviez demandé aucune activité.
......
{% extends "base.html" %}
{% load static %}
{% block "content" %}
<h2>Envoyer un email</h2>
<p>
Ce formulaire permet d'envoyer un mail à tous les comptes du site
(ou seulement aux comptes actuellements inscrits).
À utiliser avec modération.
</p>
<ul>
<li>Nombre de comptes : {{ accounts_nb }}</li>
<li>Nombre d'inscrits : {{ registered_nb }}</li>
</ul>
<form method="post" action="{% url 'admin_pages:email_new' %}">
{% csrf_token %}
<table>
<tr><td><strong>De :</strong></td><td>{{ from_email }}</td></tr>
<tr><td><strong>Envoyer à : <strong></td><td>{{ form.dest }}</td></tr>
<tr><td><strong>Sujet : <strong></td><td>{{ form.subject }}</td></tr>
</table>
{{ form.text }}
<br>
<div class="flex">
<input type="submit" value="Envoyer">
<a class="button" href="{% url 'admin_pages:index' %}">Annuler</a>
</div>
</form>
{% endblock %}
from django.test import TestCase
# Create your tests here.
from django.urls import path, include
from admin_pages import views
urlpatterns = [
path('', views.AdminView.as_view(), name="index"),
path('export/activities/', views.ExportActivities.as_view(), name="activities.csv"),
path('export/slots/', views.ExportSlots.as_view(), name="slots.csv"),
path('export/participants/', views.ExportParticipants.as_view(), name="participants.csv"),
path('export/activity_choices/', views.ExportActivityChoices.as_view(), name="activity_choices.csv"),
path('email/send_user_emails_0564946523/', views.SendUserEmail.as_view(), name="email_users"),
path('email/send_orga_emails_5682480453/', views.SendOrgaEmail.as_view(), name="email_orgas"),
path('email/new_email/', views.NewEmail.as_view(), name="email_new"),
]