From 87aaf12d2732ba4769963f6cfe0169fbebc07896 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 23 Apr 2024 18:28:00 +0200 Subject: [PATCH 1/8] Protect against Reflected XSS on home page (and other exception-handling pages) --- app/templates/error_access_denied.j2 | 2 +- app/templates/sco_value_error.j2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/error_access_denied.j2 b/app/templates/error_access_denied.j2 index c14a2c8a..46793cea 100644 --- a/app/templates/error_access_denied.j2 +++ b/app/templates/error_access_denied.j2 @@ -6,7 +6,7 @@

Accès non autorisé

-{{ exc | safe }} +{{ exc }}

Erreur !

-{{ exc | safe }} +{{ exc }}
{% if g.scodoc_dept %} From 0bc57807de7bb7408f9428eda41a94f6d676aded Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 24 Apr 2024 20:37:03 +0200 Subject: [PATCH 2/8] release build script using gitea again --- tools/build_release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build_release.sh b/tools/build_release.sh index 0244b963..ad916867 100755 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -96,7 +96,7 @@ mkdir -p "$optdir" || die "mkdir failure for $optdir" archive="$FACTORY_DIR"/"$PACKAGE_NAME-$RELEASE_TAG".tar.gz echo "Downloading $GIT_RELEASE_URL ..." # curl -o "$archive" "$GIT_RELEASE_URL" || die "curl failure for $GIT_RELEASE_URL" -#wget --progress=dot -O "$archive" "$GIT_RELEASE_URL" || die "wget failure for $GIT_RELEASE_URL" +wget --progress=dot -O "$archive" "$GIT_RELEASE_URL" || die "wget failure for $GIT_RELEASE_URL" # -nv # On décomprime From 09f4525e66bfb8ca98f6f859582904a289c2f5bc Mon Sep 17 00:00:00 2001 From: Iziram Date: Mon, 22 Apr 2024 08:39:00 +0200 Subject: [PATCH 3/8] =?UTF-8?q?Assiduit=C3=A9=20:=20maj=20couleurs=20minit?= =?UTF-8?q?imeline=20+=20legende?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/css/assiduites.css | 28 +++-------- .../assiduites/pages/calendrier_assi_etud.j2 | 48 ++----------------- .../pages/signal_assiduites_group.j2 | 5 ++ .../assiduites/widgets/legende_couleur.j2 | 38 ++++++++++----- .../assiduites/widgets/minitimeline.j2 | 8 +++- 5 files changed, 48 insertions(+), 79 deletions(-) diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index 088df3f6..7623736c 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -730,31 +730,11 @@ tr.row-justificatif.non_valide td.assi-type { background-color: var(--color-defaut) !important; } -.color.est_just.sans_etat::before { - content: ""; - position: absolute; - width: 25%; - height: 100%; - background-color: var(--color-justi) !important; - right: 0; -} - -.color.invalide::before { - content: ""; - position: absolute; - width: 25%; - height: 100%; - right: 0; +.color.invalide { background-color: var(--color-justi-invalide) !important; } -.color.attente::before, -.color.modifie::before { - content: ""; - position: absolute; - width: 25%; - height: 100%; - right: 0; +.color.attente { background: repeating-linear-gradient(to bottom, var(--color-justi-attente-stripe) 0px, var(--color-justi-attente-stripe) 4px, @@ -762,6 +742,10 @@ tr.row-justificatif.non_valide td.assi-type { var(--color-justi-attente) 7px) !important; } +.color.est_just { + background-color: var(--color-justi) !important; +} + #gtrcontent .pdp { display: none; } diff --git a/app/templates/assiduites/pages/calendrier_assi_etud.j2 b/app/templates/assiduites/pages/calendrier_assi_etud.j2 index 3c3de3a4..a3f2c93e 100644 --- a/app/templates/assiduites/pages/calendrier_assi_etud.j2 +++ b/app/templates/assiduites/pages/calendrier_assi_etud.j2 @@ -75,36 +75,7 @@ Calendrier de l'assiduité

Calendrier

-

Code couleur

-
    -
  • → présence de l'étudiant lors de la - période -
  • -
  • → la période n'est pas travaillée -
  • -
  • → absence de l'étudiant lors de la - période -
  • -
  • → absence justifiée -
  • -
  • → retard de l'étudiant lors de la - période -
  • -
  • → retard justifié -
  • - -
  • → la période est couverte par un - justificatif valide
  • -
  • → la période est - couverte par un justificatif non valide -
  • -
  • → la période - a un justificatif en attente de validation -
  • -
- - -

Vous pouvez passer le curseur sur les jours colorés afin de voir les informations supplémentaires

+ {% include "assiduites/widgets/legende_couleur.j2" %}
  • présence @@ -180,21 +151,8 @@ Calendrier de l'assiduité justify-content: start; } - .demo.invalide { - background-color: var(--color-justi-invalide) !important; - } - - .demo.attente { - background: repeating-linear-gradient(to bottom, - var(--color-justi-attente-stripe) 0px, - var(--color-justi-attente-stripe) 4px, - var(--color-justi-attente) 4px, - var(--color-justi-attente) 7px) !important; - } - - .demo.est_just { - background-color: var(--color-justi) !important; - } + + .demi .day.nonwork>span { diff --git a/app/templates/assiduites/pages/signal_assiduites_group.j2 b/app/templates/assiduites/pages/signal_assiduites_group.j2 index 68e8e6fa..2f46b07a 100644 --- a/app/templates/assiduites/pages/signal_assiduites_group.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_group.j2 @@ -178,6 +178,11 @@

+
+

Calendrier

+ {% include "assiduites/widgets/legende_couleur.j2" %} +
+ {% include "assiduites/widgets/toast.j2" %} {% include "assiduites/widgets/alert.j2" %} {% include "assiduites/widgets/prompt.j2" %} diff --git a/app/templates/assiduites/widgets/legende_couleur.j2 b/app/templates/assiduites/widgets/legende_couleur.j2 index 7ceaba7d..5a39d41a 100644 --- a/app/templates/assiduites/widgets/legende_couleur.j2 +++ b/app/templates/assiduites/widgets/legende_couleur.j2 @@ -1,12 +1,28 @@ -
  • → présence de l'étudiant lors de la période -
  • -
  • → retard de l'étudiant lors de la période -
  • -
  • → absence de l'étudiant lors de la période -
  • +

    Code couleur

    + +

    Vous pouvez passer le curseur sur les jours colorés afin de voir les informations supplémentaires

    diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2 index 8a6b4d11..1e2efdee 100644 --- a/app/templates/assiduites/widgets/minitimeline.j2 +++ b/app/templates/assiduites/widgets/minitimeline.j2 @@ -74,7 +74,13 @@ setupAssiduiteBubble(block, assiduité); } - // TODO: ajout couleur justificatif + // ajout couleur justificatif + const justificatifs = assiduité.justificatifs || []; + const justified = justificatifs.some( + (justificatif) => justificatif.etat === "VALIDE" + ) + + if(justified) block.classList.add("est_just"); block.classList.add(assiduité.etat.toLowerCase()); if(assiduité.etat != "CRENEAU") block.classList.add("color"); From 0a5919b7884b975af7f0c14fcfb64028dfe5ace3 Mon Sep 17 00:00:00 2001 From: Iziram Date: Mon, 22 Apr 2024 09:20:05 +0200 Subject: [PATCH 4/8] =?UTF-8?q?Assiduit=C3=A9=20:=20pseudo-fix=20(catch=20?= =?UTF-8?q?+=20http=20error)=20#872?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/assiduites.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index d44e0c6b..3f3aee5d 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -3,14 +3,15 @@ # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## -"""ScoDoc 9 API : Assiduités -""" +"""ScoDoc 9 API : Assiduités""" + from datetime import datetime from flask import g, request from flask_json import as_json from flask_login import current_user, login_required from flask_sqlalchemy.query import Query +from sqlalchemy.orm.exc import ObjectDeletedError from app import db, log, set_sco_dept import app.scodoc.sco_assiduites as scass @@ -858,7 +859,10 @@ def assiduite_edit(assiduite_id: int): msg=f"assiduite: modif {assiduite_unique}", ) db.session.commit() - scass.simple_invalidate_cache(assiduite_unique.to_dict()) + try: + scass.simple_invalidate_cache(assiduite_unique.to_dict()) + except ObjectDeletedError: + return json_error(404, "Assiduité supprimée / inexistante") return {"OK": True} From 5d45fcf6568cd18560f495115f0c52f326e7a616 Mon Sep 17 00:00:00 2001 From: Iziram Date: Mon, 22 Apr 2024 11:24:16 +0200 Subject: [PATCH 5/8] =?UTF-8?q?Assiduit=C3=A9=20:=20signal=5Fassiduites=5F?= =?UTF-8?q?group=20:=20bug=20fix=20suppr=20assi=20autre?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/assiduites.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index a269db1a..30cddade 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -425,7 +425,7 @@ async function getModuleImpl(assiduite) { return res.json(); }) .then((data) => { - moduleimpls[id] = `${data.module.code} ${data.module.abbrev || ''}`; + moduleimpls[id] = `${data.module.code} ${data.module.abbrev || ""}`; return moduleimpls[id]; }) .catch((_) => { @@ -531,12 +531,7 @@ async function MiseAJourLigneEtud(etud) { async function actionAssiduite(etud, etat, type, assiduite = null) { const modimpl_id = $("#moduleimpl_select").val(); - if ( - assiduite && - assiduite.etat.toLowerCase() === etat && - assiduite.moduleimpl_id == modimpl_id - ) - type = "suppression"; + if (assiduite && assiduite.etat.toLowerCase() === etat) type = "suppression"; const { deb, fin } = getPeriodAsDate(); From 4d234ba353038f9c97c60e7cc665b61159efc1a5 Mon Sep 17 00:00:00 2001 From: Iziram Date: Mon, 22 Apr 2024 15:05:31 +0200 Subject: [PATCH 6/8] =?UTF-8?q?Assiduit=C3=A9=20:=20d=C3=A9sactiver=20sais?= =?UTF-8?q?ie=20pr=C3=A9sence=20closes=20#793?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_preferences.py | 45 ++++++++++--------- app/static/js/assiduites.js | 8 +++- app/tables/visu_assiduites.py | 11 ++++- .../pages/signal_assiduites_diff.j2 | 11 ++++- .../pages/signal_assiduites_group.j2 | 3 ++ app/views/assiduites.py | 10 +++++ 6 files changed, 62 insertions(+), 26 deletions(-) diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index fa8197f3..effc1e68 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -611,16 +611,17 @@ class BasePreferences: "explanation": "toute saisie d'absence doit indiquer le module concerné", }, ), - # ( - # "forcer_present", - # { - # "initvalue": 0, - # "title": "Forcer l'appel des présents", - # "input_type": "boolcheckbox", - # "labels": ["non", "oui"], - # "category": "assi", - # }, - # ), + ( + "non_present", + { + "initvalue": 0, + "title": "Désactiver la saisie des présences", + "input_type": "boolcheckbox", + "labels": ["non", "oui"], + "category": "assi", + "explanation": "Désactive la saisie et l'affichage des présences", + }, + ), ( "periode_defaut", { @@ -644,18 +645,18 @@ class BasePreferences: "category": "assi", }, ), - ( - "assi_etat_defaut", - { - "explanation": "⚠ non fonctionnel, travaux en cours !", - "initvalue": "aucun", - "input_type": "menu", - "labels": ["aucun", "present", "retard", "absent"], - "allowed_values": ["aucun", "present", "retard", "absent"], - "title": "Définir l'état par défaut", - "category": "assi", - }, - ), + # ( + # "assi_etat_defaut", + # { + # "explanation": "⚠ non fonctionnel, travaux en cours !", + # "initvalue": "aucun", + # "input_type": "menu", + # "labels": ["aucun", "present", "retard", "absent"], + # "allowed_values": ["aucun", "present", "retard", "absent"], + # "title": "Définir l'état par défaut", + # "category": "assi", + # }, + # ), ( "non_travail", { diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index 30cddade..00757640 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -296,7 +296,13 @@ function creerLigneEtudiant(etud, index) { // Création des boutons d'assiduités if (readOnly) { } else if (currentAssiduite.type != "conflit") { - ["present", "retard", "absent"].forEach((abs) => { + const etats = ["retard", "absent"]; + + if (!window.nonPresent) { + etats.splice(0, 0, "present"); + } + + etats.forEach((abs) => { const btn = document.createElement("input"); btn.type = "checkbox"; btn.value = abs; diff --git a/app/tables/visu_assiduites.py b/app/tables/visu_assiduites.py index af3895e7..11fa4896 100644 --- a/app/tables/visu_assiduites.py +++ b/app/tables/visu_assiduites.py @@ -4,8 +4,8 @@ # See LICENSE ############################################################################## -"""Liste simple d'étudiants -""" +"""Liste simple d'étudiants""" + import datetime from flask import g, url_for from app import log @@ -140,6 +140,13 @@ class RowAssi(tb.Row): ) stats = self._get_etud_stats(etud) for key, value in stats.items(): + if key == "present" and sco_preferences.get_preference( + "non_present", + dept_id=g.scodoc_dept_id, + formsemestre_id=self.table.formsemestre.id, + ): + continue + self.add_cell(key, value[0], fmt_num(value[1] - value[2]), "assi_stats") if key != "present": self.add_cell( diff --git a/app/templates/assiduites/pages/signal_assiduites_diff.j2 b/app/templates/assiduites/pages/signal_assiduites_diff.j2 index 474c6db5..76d40d43 100644 --- a/app/templates/assiduites/pages/signal_assiduites_diff.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_diff.j2 @@ -310,8 +310,13 @@ async function nouvellePeriode(period = null) { const assi_btns = document.createElement('div'); assi_btns.classList.add('assi-btns'); + const etats = ["retard", "absent"]; - ["present", "retard", "absent"].forEach((value) => { + if(!window.nonPresent){ + etats.splice(0,0,"present"); + } + + etats.forEach((value) => { const cbox = document.createElement("input"); cbox.type = "checkbox"; cbox.value = value; @@ -499,6 +504,8 @@ const moduleimpls = new Map(); const inscriptionsModules = new Map(); const nonWorkDays = [{{ nonworkdays| safe }}]; +window.nonPresent = {{ 'true' if non_present else 'false' }}; + // Vérification du forçage de module window.forceModule = "{{ forcer_module }}" == "True"; if (window.forceModule) { @@ -600,7 +607,9 @@ main(); Intialiser les étudiants comme : diff --git a/app/templates/assiduites/pages/signal_assiduites_group.j2 b/app/templates/assiduites/pages/signal_assiduites_group.j2 index 2f46b07a..73744040 100644 --- a/app/templates/assiduites/pages/signal_assiduites_group.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_group.j2 @@ -31,6 +31,7 @@ const readOnly = {{ readonly }}; window.forceModule = "{{ forcer_module }}" == "True" + window.nonPresent = {{ 'true' if non_present else 'false' }}; const etudsDefDem = {{ defdem | safe }} @@ -159,8 +160,10 @@
    Mettre tout le monde :
    + {% if not non_present %} + {% endif %} Date: Mon, 22 Apr 2024 15:46:17 +0200 Subject: [PATCH 7/8] =?UTF-8?q?Assiduit=C3=A9=20:=20signal=5Fassiduites=5F?= =?UTF-8?q?diff=20:=20plage=20depuis=20args=20url=20closes=20#741?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/signal_assiduites_diff.j2 | 19 +++++++++- app/views/assiduites.py | 36 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/app/templates/assiduites/pages/signal_assiduites_diff.j2 b/app/templates/assiduites/pages/signal_assiduites_diff.j2 index 76d40d43..0c5c800e 100644 --- a/app/templates/assiduites/pages/signal_assiduites_diff.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_diff.j2 @@ -525,12 +525,29 @@ if (window.forceModule) { } }); } + +const defaultPlage = {{ nouv_plage | safe}} || []; + /** * Fonction exécutée au lancement de la page * - On affiche ou non les photos des étudiants * - On vérifie si la date est un jour travaillé */ async function main() { + + // On initialise les sélecteurs avec les valeurs par défaut (si elles existent) + if (defaultPlage.every((e) => e)) { + $("#date").datepicker("setDate", defaultPlage[0]); + $("#debut").val(defaultPlage[1]); + $("#fin").val(defaultPlage[2]); + + // On ajoute la période si la date est un jour travaillé + if(dateCouranteEstTravaillee()){ + await nouvellePeriode(); + } + } + + const checked = localStorage.getItem("scodoc-etud-pdp") == "true"; afficherPDP(checked); $("#date").on("change", async function (d) { @@ -539,7 +556,7 @@ async function main() { }); } -main(); +window.addEventListener("load", main); diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 1981473f..82990fcb 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -1919,8 +1919,29 @@ def _preparer_objet( @scodoc @permission_required(Permission.AbsChange) def signal_assiduites_diff(): - """TODO documenter + """ Utilisé notamment par "Saisie différée" sur tableau de bord semetstre" + + Arguments de la requête: + + - group_ids : liste des groupes + example : group_ids=1,2,3 + - formsemestre_id : id du formsemestre + example : formsemestre_id=1 + - moduleimpl_id : id du moduleimpl + example : moduleimpl_id=1 + + (Permet de pré-générer une plage. Si non renseigné, la plage sera vide) + (Les trois valeurs suivantes doivent être renseignées ensemble) + - date + example : date=01/01/2021 + - heure_debut + example : heure_debut=08:00 + - heure_fin + example : heure_fin=10:00 + + Exemple de requête : + signal_assiduites_diff?formsemestre_id=67&group_ids=400&moduleimpl_id=1229&date=15/04/2024&heure_debut=12:34&heure_fin=12:55 """ # Récupération des paramètres de la requête group_ids: list[int] = request.args.get("group_ids", None) @@ -1962,11 +1983,23 @@ def signal_assiduites_diff(): grp + ' ' + groups_infos.groups_titles + "" ) + # Pré-remplissage des sélecteurs moduleimpl_id = request.args.get("moduleimpl_id", -1) try: moduleimpl_id = int(moduleimpl_id) except ValueError: moduleimpl_id = -1 + # date fra (dd/mm/yyyy) + date = request.args.get("date", "") + # heures (hh:mm) + heure_deb = request.args.get("heure_debut", "") + heure_fin = request.args.get("heure_fin", "") + + # vérifications des sélecteurs + date = date if re.match(r"^\d{2}\/\d{2}\/\d{4}$", date) else "" + heure_deb = heure_deb if re.match(r"^[0-2]\d:[0-5]\d$", heure_deb) else "" + heure_fin = heure_fin if re.match(r"^[0-2]\d:[0-5]\d$", heure_fin) else "" + nouv_plage: list[str] = [date, heure_deb, heure_fin] return render_template( "assiduites/pages/signal_assiduites_diff.j2", @@ -1987,6 +2020,7 @@ def signal_assiduites_diff(): formsemestre_id=formsemestre_id, dept_id=g.scodoc_dept_id, ), + nouv_plage=nouv_plage, ) From 18b1f00586143db25d3395169442382df162d767 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 24 Apr 2024 20:58:02 +0200 Subject: [PATCH 8/8] version 9.6.964 --- sco_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sco_version.py b/sco_version.py index 57654dde..0a661492 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.963" +SCOVERSION = "9.6.964" SCONAME = "ScoDoc"