diff --git a/app/scodoc/TrivialFormulator.py b/app/scodoc/TrivialFormulator.py index acbe8d8ca..722b7ec9a 100644 --- a/app/scodoc/TrivialFormulator.py +++ b/app/scodoc/TrivialFormulator.py @@ -9,6 +9,12 @@ v 1.3 (python3) """ import html +import re + +# re validant dd/mm/yyyy +DMY_REGEXP = re.compile( + r"^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$" +) def TrivialFormulator( @@ -66,8 +72,8 @@ def TrivialFormulator( HTML elements: input_type : 'text', 'textarea', 'password', 'radio', 'menu', 'checkbox', - 'hidden', 'separator', 'file', 'date', 'boolcheckbox', - 'text_suggest' + 'hidden', 'separator', 'file', 'date', 'datedmy' (avec validation), + 'boolcheckbox', 'text_suggest' (default text) size : text field width rows, cols: textarea geometry @@ -243,6 +249,8 @@ class TF(object): "Le champ '%s' doit être renseigné" % descr.get("title", field) ) ok = 0 + elif val == "" or val == None: + continue # allowed empty field, skip # type typ = descr.get("type", "string") if val != "" and val != None: @@ -300,6 +308,10 @@ class TF(object): if not descr["validator"](val, field): msg.append("valeur invalide (%s) pour le champ '%s'" % (val, field)) ok = 0 + elif descr.get("input_type") == "datedmy": + if not DMY_REGEXP.match(val): + msg.append("valeur invalide (%s) pour la date '%s'" % (val, field)) + ok = 0 # boolean checkbox if descr.get("input_type", None) == "boolcheckbox": if int(val): @@ -564,7 +576,9 @@ class TF(object): '' % (field, size, values[field], attribs) ) - elif input_type == "date": # JavaScript widget for date input + elif ( + input_type == "date" or input_type == "datedmy" + ): # JavaScript widget for date input lem.append( '' % (field, values[field]) diff --git a/app/scodoc/sco_evaluation_edit.py b/app/scodoc/sco_evaluation_edit.py index 583ac5c44..c324a89be 100644 --- a/app/scodoc/sco_evaluation_edit.py +++ b/app/scodoc/sco_evaluation_edit.py @@ -170,7 +170,7 @@ def evaluation_create_form( ( "jour", { - "input_type": "date", + "input_type": "datedmy", "title": "Date", "size": 12, "explanation": "date de l'examen, devoir ou contrôle", diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py index 1099986bf..c563c93e6 100644 --- a/app/scodoc/sco_exceptions.py +++ b/app/scodoc/sco_exceptions.py @@ -96,6 +96,28 @@ class ScoNonEmptyFormationObject(ScoValueError): super().__init__(msg=msg, dest_url=dest_url) +class ScoInvalidIdType(ScoValueError): + """Pour les clients qui s'obstinnent à utiliser des bookmarks ou + historiques anciens avec des ID ScoDoc7""" + + def __init__(self, msg=""): + import app.scodoc.sco_utils as scu + + msg = f"""

Adresse de page invalide

+

+ Vous utilisez un lien invalide, qui correspond probablement + à une ancienne version du logiciel.
+ Au besoin, mettre à jour vos marque-pages. +

+

Si le problème persiste, merci de contacter l'assistance + via la liste de diffusion Notes + ou le salon Discord. +

+

Message serveur: {msg}

+ """ + super().__init__(msg) + + class ScoGenError(ScoException): "exception avec affichage d'une page explicative ad-hoc" diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 272c5c383..35336ccf2 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -27,21 +27,22 @@ """Operations de base sur les formsemestres """ -from app.scodoc.sco_exceptions import ScoValueError -import time from operator import itemgetter +import time from flask import g, request import app +from app import log from app.models import Departement + from app.scodoc import sco_codes_parcours from app.scodoc import sco_cache from app.scodoc import sco_formations from app.scodoc import sco_preferences from app.scodoc.gen_tables import GenTable -from app import log from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID +from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidIdType from app.scodoc.sco_vdi import ApoEtapeVDI import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu @@ -97,7 +98,7 @@ def get_formsemestre(formsemestre_id, raise_soft_exc=False): if formsemestre_id in g.stored_get_formsemestre: return g.stored_get_formsemestre[formsemestre_id] if not isinstance(formsemestre_id, int): - raise ValueError("formsemestre_id must be an integer !") + raise ScoInvalidIdType("formsemestre_id must be an integer !") sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id}) if not sems: log("get_formsemestre: invalid formsemestre_id (%s)" % formsemestre_id) diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 060289e95..e32119130 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -254,7 +254,7 @@ def do_formsemestre_createwithmodules(edit=False): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -264,7 +264,7 @@ def do_formsemestre_createwithmodules(edit=False): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -914,7 +914,7 @@ def formsemestre_clone(formsemestre_id): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, @@ -924,7 +924,7 @@ def formsemestre_clone(formsemestre_id): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a", "size": 9, "allow_null": False, diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py index b3d601de7..7e4e3da89 100644 --- a/app/scodoc/sco_formsemestre_exterieurs.py +++ b/app/scodoc/sco_formsemestre_exterieurs.py @@ -154,7 +154,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id): "date_debut", { "title": "Date de début", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a (peut être approximatif)", "size": 9, "allow_null": False, @@ -164,7 +164,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id): "date_fin", { "title": "Date de fin", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a (peut être approximatif)", "size": 9, "allow_null": False, diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js index 68918448b..ea9f8bc6c 100644 --- a/app/static/js/releve-but.js +++ b/app/static/js/releve-but.js @@ -1,42 +1,42 @@ /* Module par Seb. L. */ class releveBUT extends HTMLElement { - constructor(){ + constructor() { super(); - this.shadow = this.attachShadow({mode: 'open'}); + this.shadow = this.attachShadow({ mode: 'open' }); /* Config par defaut */ this.config = { showURL: true }; - + /* Template du module */ this.shadow.innerHTML = this.template(); - + /* Style du module */ const styles = document.createElement('link'); styles.setAttribute('rel', 'stylesheet'); styles.setAttribute('href', '/ScoDoc/static/css/releve-but.css'); - this.shadow.appendChild(styles); + this.shadow.appendChild(styles); } listeOnOff() { this.parentElement.parentElement.classList.toggle("listeOff"); - this.parentElement.parentElement.querySelectorAll(".moduleOnOff").forEach(e=>{ + this.parentElement.parentElement.querySelectorAll(".moduleOnOff").forEach(e => { e.classList.remove("moduleOnOff") }) } - moduleOnOff(){ + moduleOnOff() { this.parentElement.classList.toggle("moduleOnOff"); } - goTo(){ + goTo() { let module = this.dataset.module; this.parentElement.parentElement.parentElement.parentElement.querySelector("#Module_" + module).scrollIntoView(); } - set setConfig(config){ + set setConfig(config) { this.config.showURL = config.showURL ?? this.config.showURL; } - set showData(data) { + set showData(data) { this.showInformations(data); this.showSemestre(data); this.showSynthese(data); @@ -46,7 +46,7 @@ class releveBUT extends HTMLElement { this.shadow.querySelectorAll(".CTA_Liste").forEach(e => { e.addEventListener("click", this.listeOnOff) - }) + }) this.shadow.querySelectorAll(".ue, .module").forEach(e => { e.addEventListener("click", this.moduleOnOff) }) @@ -57,7 +57,7 @@ class releveBUT extends HTMLElement { this.shadow.children[0].classList.add("ready"); } - template(){ + template() { return `
@@ -142,8 +142,8 @@ class releveBUT extends HTMLElement { this.shadow.querySelector(".studentPic").src = data.etudiant.photo_url || "default_Student.svg"; let output = ''; - - if(this.config.showURL){ + + if (this.config.showURL) { output += ``; } else { output += `
`; @@ -167,7 +167,7 @@ class releveBUT extends HTMLElement {
${data.formation.titre}
`; - if(this.config.showURL){ + if (this.config.showURL) { output += `
`; } else { output += `
`; @@ -190,18 +190,18 @@ class releveBUT extends HTMLElement {
Moy. promo. :
${data.semestre.notes.moy}
Min. promo. :
${data.semestre.notes.min}
`; - /*${data.semestre.groupes.map(groupe => { - return ` -
-
Groupe
${groupe.nom}
-
Rang :
${groupe.rang.value} / ${groupe.rang.total}
-
Max. groupe :
${groupe.notes.max}
-
Moy. groupe :
${groupe.notes.min}
-
Min. groupe :
${groupe.notes.min}
-
- `; - }).join("") - }*/ + /*${data.semestre.groupes.map(groupe => { + return ` +
+
Groupe
${groupe.nom}
+
Rang :
${groupe.rang.value} / ${groupe.rang.total}
+
Max. groupe :
${groupe.notes.max}
+
Moy. groupe :
${groupe.notes.min}
+
Min. groupe :
${groupe.notes.min}
+
+ `; + }).join("") + }*/ this.shadow.querySelector(".infoSemestre").innerHTML = output; /*this.shadow.querySelector(".decision").innerHTML = data.semestre.decision.code;*/ } @@ -213,6 +213,7 @@ class releveBUT extends HTMLElement { let output = ``; Object.entries(data.ues).forEach(([ue, dataUE]) => { output += ` +

@@ -257,7 +258,7 @@ class releveBUT extends HTMLElement { }) return output; } - + /*******************************/ /* Evaluations */ /*******************************/ @@ -335,8 +336,8 @@ class releveBUT extends HTMLElement { /********************/ /* Fonctions d'aide */ /********************/ - URL(href, content){ - if(this.config.showURL){ + URL(href, content) { + if (this.config.showURL) { return `${content}`; } else { return content; diff --git a/app/views/absences.py b/app/views/absences.py index dcbac37fc..a7513f847 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -1151,8 +1151,8 @@ def AddBilletAbsenceForm(etudid): scu.get_request_args(), ( ("etudid", {"input_type": "hidden"}), - ("begin", {"input_type": "date"}), - ("end", {"input_type": "date"}), + ("begin", {"input_type": "datedmy"}), + ("end", {"input_type": "datedmy"}), ( "justified", {"input_type": "boolcheckbox", "default": 0, "title": "Justifiée"}, diff --git a/app/views/notes.py b/app/views/notes.py index 4ca10dff6..c9fec1a1c 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -72,12 +72,7 @@ from app import log, send_scodoc_alarm from app.scodoc import scolog from app.scodoc.scolog import logdb -from app.scodoc.sco_exceptions import ( - ScoValueError, - ScoLockedFormError, - ScoGenError, - AccessDenied, -) +from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoInvalidIdType from app.scodoc import html_sco_header from app.pe import pe_view from app.scodoc import sco_abs @@ -287,6 +282,8 @@ def formsemestre_bulletinetud( ): if not formsemestre_id: flask.abort(404, "argument manquant: formsemestre_id") + if not isinstance(formsemestre_id, int): + raise ScoInvalidIdType("formsemestre_id must be an integer !") formsemestre = FormSemestre.query.get_or_404(formsemestre_id) if formsemestre.formation.is_apc() and format != "oldjson": if etudid: @@ -744,6 +741,10 @@ def XMLgetFormsemestres(etape_apo=None, formsemestre_id=None): DEPRECATED: use formsemestre_list() """ current_app.logger.debug("Warning: calling deprecated XMLgetFormsemestres") + if not formsemestre_id: + return flask.abort(404, "argument manquant: formsemestre_id") + if not isinstance(formsemestre_id, int): + return flask.abort(404, "formsemestre_id must be an integer !") args = {} if etape_apo: args["etape_apo"] = etape_apo diff --git a/app/views/users.py b/app/views/users.py index 608eb9ab7..af4a721ed 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -424,7 +424,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): "date_expiration", { "title": "Date d'expiration", # j/m/a - "input_type": "date", + "input_type": "datedmy", "explanation": "j/m/a, laisser vide si pas de limite", "size": 9, "allow_null": True, diff --git a/sco_version.py b/sco_version.py index 72a898cd8..1893a44fd 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.22" +SCOVERSION = "9.1.23" SCONAME = "ScoDoc"