Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
19 changed files with 125 additions and 136 deletions
Showing only changes of commit ab4731bd43 - Show all commits

View File

@ -232,7 +232,9 @@ class ScolarNews(db.Model):
)
# Transforme les URL en URL absolues
base = scu.ScoURL()
base = url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)[
: -len("/index_html")
]
txt = re.sub('href=/.*?"', 'href="' + base + "/", txt)
# Transforme les liens HTML en texte brut: '<a href="url">texte</a>' devient 'texte: url'

View File

@ -30,7 +30,7 @@
import html
from flask import g, render_template
from flask import g, render_template, url_for
from flask import request
from flask_login import current_user
@ -163,7 +163,7 @@ def sco_header(
params = {
"page_title": page_title or sco_version.SCONAME,
"no_side_bar": no_side_bar,
"ScoURL": scu.ScoURL(),
"ScoURL": url_for("scolar.index_html", scodoc_dept=g.scodoc_dept),
"encoding": scu.SCO_ENCODING,
"titrebandeau_mkup": "<td>" + titrebandeau + "</td>",
"authuser": current_user.user_name,
@ -220,7 +220,7 @@ def sco_header(
<script>
window.onload=function(){{enableTooltips("gtrcontent")}};
const SCO_URL="{scu.ScoURL()}";
const SCO_URL="{url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)}";
const SCO_TIMEZONE="{scu.TIME_ZONE}";
</script>"""
)

View File

@ -108,19 +108,27 @@ def sidebar_common():
</div>
{sidebar_dept()}
<h2 class="insidebar">Scolarité</h2>
<a href="{scu.ScoURL()}" class="sidebar">Semestres</a> <br>
<a href="{scu.NotesURL()}" class="sidebar">Formations</a> <br>
<a href="{
url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
}" class="sidebar">Semestres</a> <br>
<a href="{
url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
}" class="sidebar">Formations</a> <br>
"""
]
if current_user.has_permission(Permission.AbsChange):
H.append(
f""" <a href="{scu.AssiduitesURL()}" class="sidebar">Assiduité</a> <br> """
f""" <a href="{
url_for("assiduites.bilan_dept", scodoc_dept=g.scodoc_dept)
}" class="sidebar">Assiduité</a> <br> """
)
if current_user.has_permission(
Permission.UsersAdmin
) or current_user.has_permission(Permission.UsersView):
H.append(
f"""<a href="{scu.UsersURL()}" class="sidebar">Utilisateurs</a> <br>"""
f"""<a href="{
url_for("users.index_html", scodoc_dept=g.scodoc_dept)
}" class="sidebar">Utilisateurs</a> <br>"""
)
if current_user.has_permission(Permission.EditPreferences):

View File

@ -58,21 +58,20 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
html_sco_header.sco_header(page_title="Suppression d'une formation"),
f"""<h2>Suppression de la formation {formation.titre} ({formation.acronyme})</h2>""",
]
sems = sco_formsemestre.do_formsemestre_list({"formation_id": formation_id})
if sems:
formsemestres = formation.formsemestres.all()
if formsemestres:
H.append(
"""<p class="warning">Impossible de supprimer cette formation,
car les sessions suivantes l'utilisent:</p>
<ul>"""
)
for sem in sems:
for formsemestre in formsemestres:
H.append(f"""<li>{formsemestre.html_link_status()}</li>""")
H.append(
'<li><a class="stdlink" href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titremois)s</a></li>'
% sem
)
H.append(
'</ul><p><a class="stdlink" href="%s">Revenir</a></p>' % scu.NotesURL()
f"""</ul>
<p><a class="stdlink" href="{
url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
}">Revenir</a></p>"""
)
else:
if not dialog_confirmed:
@ -85,14 +84,16 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
</p>
""",
OK="Supprimer cette formation",
cancel_url=scu.NotesURL(),
cancel_url=url_for("notes.index_html", scodoc_dept=g.scodoc_dept),
parameters={"formation_id": formation_id},
)
else:
do_formation_delete(formation_id)
H.append(
f"""<p>OK, formation supprimée.</p>
<p><a class="stdlink" href="{scu.NotesURL()}">continuer</a></p>"""
<p><a class="stdlink" href="{
url_for("notes.index_html", scodoc_dept=g.scodoc_dept)
}">continuer</a></p>"""
)
H.append(html_sco_header.sco_footer())
@ -252,7 +253,7 @@ def formation_edit(formation_id=None, create=False):
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return flask.redirect(scu.NotesURL())
return flask.redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
else:
# check unicity : constraint UNIQUE(acronyme,titre,version)
if create:

View File

@ -541,7 +541,9 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, fmt="html"):
scodoc_dept=g.scodoc_dept,
moduleimpl_id=e.moduleimpl.id,
),
"module_titre": e.moduleimpl.module.abbrev or e.moduleimpl.module.titre,
"module_titre": e.moduleimpl.module.abbrev
or e.moduleimpl.module.titre
or "",
"responsable_id": e.moduleimpl.responsable_id,
"responsable_nomplogin": sco_users.user_info(
e.moduleimpl.responsable_id

View File

@ -1431,18 +1431,25 @@ Ceci n'est possible que si :
def formsemestre_delete2(formsemestre_id, dialog_confirmed=False):
"""Delete a formsemestre (confirmation)"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
# Confirmation dialog
if not dialog_confirmed:
return scu.confirm_dialog(
"""<h2>Vous voulez vraiment supprimer ce semestre ???</h2><p>(opération irréversible)</p>""",
"""<h2>Vous voulez vraiment supprimer ce semestre ???</h2>
<p>(opération irréversible)</p>
""",
dest_url="",
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
parameters={"formsemestre_id": formsemestre_id},
cancel_url=url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id,
),
parameters={"formsemestre_id": formsemestre.id},
)
# Bon, s'il le faut...
do_formsemestre_delete(formsemestre_id)
do_formsemestre_delete(formsemestre.id)
flash("Semestre supprimé !")
return flask.redirect(scu.ScoURL())
return flask.redirect(url_for("scolar.index_html", scodoc_dept=g.scodoc_dept))
def formsemestre_has_decisions_or_compensations(

View File

@ -634,7 +634,7 @@ def formsemestre_description_table(
"UE": modimpl.module.ue.acronyme,
"_UE_td_attrs": ue_info.get("_UE_td_attrs", ""),
"Code": modimpl.module.code or "",
"Module": modimpl.module.abbrev or modimpl.module.titre,
"Module": modimpl.module.abbrev or modimpl.module.titre or "",
"_Module_class": "scotext",
"Inscrits": mod_nb_inscrits,
"Responsable": sco_users.user_info(modimpl.responsable_id)["nomprenom"],

View File

@ -180,7 +180,7 @@ def fiche_etud(etudid=None):
)
else:
info["etat_civil"] = ""
info["ScoURL"] = scu.ScoURL()
info["ScoURL"] = url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
info["authuser"] = current_user
if restrict_etud_data:
info["info_naissance"] = ""

View File

@ -2260,16 +2260,17 @@ class BasePreferences:
before_table="<details><summary>{title}</summary>",
after_table="</details>",
)
dest_url = url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return flask.redirect(scu.ScoURL()) # cancel
else:
if tf[0] == -1:
return flask.redirect(dest_url) # cancel
#
for pref in self.prefs_definition:
self.prefs[None][pref[0]] = tf[2][pref[0]]
self.save()
flash("Préférences modifiées")
return flask.redirect(scu.ScoURL())
return flask.redirect(dest_url)
def build_tf_form(self, categories: list[str] = None, formsemestre_id: int = None):
"""Build list of elements for TrivialFormulator.
@ -2433,10 +2434,12 @@ function set_global_pref(el, pref_name) {
before_table="<details><summary>{title}</summary>",
after_table="</details>",
)
dest_url = (
scu.NotesURL()
+ "/formsemestre_status?formsemestre_id=%s" % self.formsemestre_id
dest_url = url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=self.formsemestre_id,
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
@ -2482,7 +2485,9 @@ function set_global_pref(el, pref_name) {
request.base_url + "?formsemestre_id=" + str(self.formsemestre_id)
)
elif destination == "global":
return flask.redirect(scu.ScoURL() + "/edit_preferences")
return flask.redirect(
url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept)
)
#

View File

@ -785,51 +785,6 @@ BULLETINS_VERSIONS_BUT = BULLETINS_VERSIONS | {
"butcourt": "Version courte spéciale BUT"
}
# ----- Support for ScoDoc7 compatibility
def ScoURL():
"""base URL for this sco instance.
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite
= page accueil département
"""
return url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)[
: -len("/index_html")
]
def NotesURL():
"""URL of Notes
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite/Notes
= url de base des méthodes de notes
(page accueil programmes).
"""
return url_for("notes.index_html", scodoc_dept=g.scodoc_dept)[: -len("/index_html")]
def AbsencesURL():
"""URL of Absences"""
return url_for("absences.index_html", scodoc_dept=g.scodoc_dept)[
: -len("/index_html")
]
def AssiduitesURL():
"""URL of Assiduités"""
return url_for("assiduites.bilan_dept", scodoc_dept=g.scodoc_dept)[
: -len("/BilanDept")
]
def UsersURL():
"""URL of Users
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite/Users
= url de base des requêtes ZScoUsers
et page accueil users
"""
return url_for("users.index_html", scodoc_dept=g.scodoc_dept)[: -len("/index_html")]
# ---- Simple python utilities

View File

@ -485,6 +485,10 @@
cursor: pointer;
}
.mass-selection em {
margin-left: 16px;
}
.fieldsplit {
display: flex;
justify-content: flex-start;

View File

@ -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((_) => {

View File

@ -402,12 +402,12 @@ function sauvegarderAssiduites() {
await nouvellePeriode(periode);
}
// Si il y n'a pas d'erreur, on affiche un message de succès
// Si il n'y a pas d'erreur, on affiche un message de succès
if (data.errors.length == 0) {
const span = document.createElement("span");
span.textContent = "Les assiduités ont bien été sauvegardées.";
span.textContent = "Le relevé d'assiduité a été enregistré.";
openAlertModal(
"Sauvegarde des assiduités",
"Enregistrement de l'assiduité",
span,
null,
"var(--color-present)"

View File

@ -168,6 +168,7 @@
<input type="checkbox" value="remove" name="mass_btn_assiduites" id="mass_rbtn_aucun"
class="rbtn aucun" onclick="mettreToutLeMonde('vide', this)" title="Supprimer">
</fieldset>
<em>Les saisies ci-dessous sont enregistrées au fur et à mesure.</em>
</div>
{% endif %}

View File

@ -237,7 +237,7 @@ span.calendarEdit {
<input class=groupe type=checkbox ${partition.show_in_lists ? "checked" : ""} data-attr=show_in_lists> Afficher sur bulletins et tableaux
</label>
<label>
<a class="stdlink" href="{{scu.ScoURL()
<a class="stdlink" href="{{url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
}}/groups_auto_repartition/${partition.id}">Répartir les étudiants</a>
</label>
</div>

View File

@ -181,7 +181,7 @@ def add_billets_absence_form(etudid):
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return flask.redirect(scu.ScoURL())
return flask.redirect(url_for("scolar.index_html", scodoc_dept=g.scodoc_dept))
else:
e = tf[2]["begin"].split("/")
begin = e[2] + "-" + e[1] + "-" + e[0] + " 00:00:00"
@ -407,7 +407,7 @@ def process_billet_absence_form(billet_id: int):
return "\n".join(H) + "<br>" + tf[1] + F + html_sco_header.sco_footer()
elif tf[0] == -1:
return flask.redirect(scu.ScoURL())
return flask.redirect(url_for("scolar.index_html", scodoc_dept=g.scodoc_dept))
else:
n = _ProcessBilletAbsence(billet, tf[2]["estjust"], tf[2]["description"])
if tf[2]["estjust"]:

View File

@ -685,7 +685,7 @@ def module_clone():
#
@bp.route("/")
@bp.route("/index_html")
@bp.route("/index_html", alias=True)
@scodoc
@permission_required(Permission.ScoView)
def index_html():
@ -807,7 +807,7 @@ def formation_import_xml_form():
{ html_sco_header.sco_footer() }
"""
elif tf[0] == -1:
return flask.redirect(scu.NotesURL())
return flask.redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
else:
formation_id, _, _ = sco_formations.formation_import_xml(
tf[2]["xmlfile"].read()

View File

@ -340,8 +340,8 @@ def showEtudLog(etudid, fmt="html"):
# ---------- PAGE ACCUEIL (listes) --------------
@bp.route("/", alias=True)
@bp.route("/index_html")
@bp.route("/")
@bp.route("/index_html", alias=True)
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func
@ -1954,7 +1954,7 @@ def etudident_delete(etudid: int = -1, dialog_confirmed=False):
for formsemestre_id in formsemestre_ids_to_inval:
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
flash("Étudiant supprimé !")
return flask.redirect(scu.ScoURL())
return flask.redirect(url_for("scolar.index_html", scodoc_dept=g.scodoc_dept))
@bp.route("/check_group_apogee")
@ -2148,7 +2148,7 @@ def form_students_import_excel(formsemestre_id=None):
)
else:
sem = None
dest_url = scu.ScoURL()
dest_url = url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
if sem and not sem["etat"]:
raise ScoValueError("Modification impossible: semestre verrouille")
H = [
@ -2183,13 +2183,15 @@ def form_students_import_excel(formsemestre_id=None):
)
else:
H.append(
"""
f"""
<p>Pour inscrire directement les étudiants dans un semestre de
formation, il suffit d'indiquer le code de ce semestre
(qui doit avoir été créé au préalable). <a class="stdlink" href="%s?showcodes=1">Cliquez ici pour afficher les codes</a>
(qui doit avoir été créé au préalable).
<a class="stdlink" href="{
url_for("scolar.index_html", showcodes=1, scodoc_dept=g.scodoc_dept)
}">Cliquez ici pour afficher les codes</a>
</p>
"""
% (scu.ScoURL())
)
H.append("""<ol><li>""")
@ -2414,9 +2416,11 @@ def form_students_import_infos_admissions(formsemestre_id=None):
return "\n".join(H) + tf[1] + help_text + F
elif tf[0] == -1:
return flask.redirect(
scu.ScoURL()
+ "/formsemestre_status?formsemestre_id="
+ str(formsemestre_id)
url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
)
else:
return sco_import_etuds.students_import_admission(

View File

@ -132,7 +132,7 @@ class Mode(IntEnum):
@bp.route("/")
@bp.route("/index_html")
@bp.route("/index_html", alias=True)
@scodoc
@permission_required(Permission.UsersView)
@scodoc7func
@ -605,7 +605,7 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] + F
elif tf[0] == -1:
return flask.redirect(scu.UsersURL())
return flask.redirect(url_for("users.index_html", scodoc_dept=g.scodoc_dept))
else:
vals = tf[2]
roles = set(vals["roles"]).intersection(editable_roles_strings)
@ -1080,28 +1080,28 @@ def change_password(user_name, password, password2):
#
# ici page simplifiee car on peut ne plus avoir
# le droit d'acceder aux feuilles de style
H.append(
"""<h2>Changement effectué !</h2>
return f"""<?xml version="1.0" encoding="{scu.SCO_ENCODING}"?>
<!DOCTYPE html>
<html>
<head>
<title>Mot de passe changé</title>
<meta http-equiv="Content-Type" content="text/html; charset={scu.SCO_ENCODING}" />
<body>
<h1>Mot de passe changé !</h1>
<h2>Changement effectué</h2>
<p>Ne notez pas ce mot de passe, mais mémorisez le !</p>
<p>Rappel: il est <b>interdit</b> de communiquer son mot de passe à
un tiers, même si c'est un collègue de confiance !</p>
<p><b>Si vous n'êtes pas administrateur, le système va vous redemander
votre login et nouveau mot de passe au prochain accès.</b>
</p>"""
)
return (
f"""<?xml version="1.0" encoding="{scu.SCO_ENCODING}"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Mot de passe changé</title>
<meta http-equiv="Content-Type" content="text/html; charset={scu.SCO_ENCODING}" />
<body><h1>Mot de passe changé !</h1>
</p>
<a href="{
url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)
}" class="stdlink">Continuer</a>
</body>
</html>
"""
+ "\n".join(H)
+ f'<a href="{scu.ScoURL()}" class="stdlink">Continuer</a></body></html>'
)
return html_sco_header.sco_header() + "\n".join(H) + F