forked from ScoDoc/DocScoDoc
conserve le visuel sur les pages preferences
This commit is contained in:
parent
cc977da44e
commit
585cf84de7
451
app/forms/config_logos.py
Normal file
451
app/forms/config_logos.py
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ScoDoc
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Formulaires configuration logos
|
||||||
|
|
||||||
|
Contrib @jmp, dec 21
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import flash, url_for, redirect, render_template
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask_wtf.file import FileField, FileAllowed
|
||||||
|
from wtforms import SubmitField, FormField, validators, FieldList
|
||||||
|
from wtforms import ValidationError
|
||||||
|
from wtforms.fields.simple import StringField, HiddenField
|
||||||
|
|
||||||
|
from app.models import Departement
|
||||||
|
from app.scodoc import sco_logos, html_sco_header
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
from app.scodoc.sco_config_actions import LogoInsert
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
from app.scodoc.sco_logos import find_logo
|
||||||
|
|
||||||
|
|
||||||
|
JAVASCRIPTS = html_sco_header.BOOTSTRAP_MULTISELECT_JS + []
|
||||||
|
|
||||||
|
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
|
||||||
|
|
||||||
|
# class ItemForm(FlaskForm):
|
||||||
|
# """Unused Generic class to document common behavior for classes
|
||||||
|
# * ScoConfigurationForm
|
||||||
|
# * DeptForm
|
||||||
|
# * LogoForm
|
||||||
|
# Some or all of these implements:
|
||||||
|
# * Composite design pattern (ScoConfigurationForm and DeptForm)
|
||||||
|
# - a FieldList(FormField(ItemForm))
|
||||||
|
# - FieldListItem are created by browsing the model
|
||||||
|
# - index dictionnary to provide direct access to a SubItemForm
|
||||||
|
# - the direct access method (get_form)
|
||||||
|
# * have some information added to be displayed
|
||||||
|
# - information are collected from a model object
|
||||||
|
# Common methods:
|
||||||
|
# * build(model) (not for LogoForm who has no child)
|
||||||
|
# for each child:
|
||||||
|
# * create en entry in the FieldList for each subitem found
|
||||||
|
# * update self.index
|
||||||
|
# * fill_in additional information into the form
|
||||||
|
# * recursively calls build for each chid
|
||||||
|
# some spécific information may be added after standard processing
|
||||||
|
# (typically header/footer description)
|
||||||
|
# * preview(data)
|
||||||
|
# check the data from a post and build a list of operations that has to be done.
|
||||||
|
# for a two phase process:
|
||||||
|
# * phase 1 (list all opérations)
|
||||||
|
# * phase 2 (may be confirmation and execure)
|
||||||
|
# - if no op found: return to the form with a message 'Aucune modification trouvée'
|
||||||
|
# - only one operation found: execute and go to main page
|
||||||
|
# - more than 1 operation found. asked form confirmation (and execution if confirmed)
|
||||||
|
#
|
||||||
|
# Someday we'll have time to refactor as abstract classes but Abstract FieldList makes this
|
||||||
|
# a bit complicated
|
||||||
|
# """
|
||||||
|
|
||||||
|
# Terminology:
|
||||||
|
# dept_id : identifies a dept in modele (= list_logos()). None designates globals logos
|
||||||
|
# dept_key : identifies a dept in this form only (..index[dept_key], and fields 'dept_key').
|
||||||
|
# 'GLOBAL' designates globals logos (we need a string value to set up HiddenField
|
||||||
|
GLOBAL = "_"
|
||||||
|
|
||||||
|
|
||||||
|
def dept_id_to_key(dept_id):
|
||||||
|
if dept_id is None:
|
||||||
|
return GLOBAL
|
||||||
|
return dept_id
|
||||||
|
|
||||||
|
|
||||||
|
def dept_key_to_id(dept_key):
|
||||||
|
if dept_key == GLOBAL:
|
||||||
|
return None
|
||||||
|
return dept_key
|
||||||
|
|
||||||
|
|
||||||
|
def logo_name_validator(message=None):
|
||||||
|
def validate_logo_name(form, field):
|
||||||
|
name = field.data if field.data else ""
|
||||||
|
if "." in name:
|
||||||
|
raise ValidationError(message)
|
||||||
|
if not scu.is_valid_filename(name):
|
||||||
|
raise ValidationError(message)
|
||||||
|
|
||||||
|
return validate_logo_name
|
||||||
|
|
||||||
|
|
||||||
|
class AddLogoForm(FlaskForm):
|
||||||
|
"""Formulaire permettant l'ajout d'un logo (dans un département)"""
|
||||||
|
|
||||||
|
from app.scodoc.sco_config_actions import LogoInsert
|
||||||
|
|
||||||
|
dept_key = HiddenField()
|
||||||
|
name = StringField(
|
||||||
|
label="Nom",
|
||||||
|
validators=[
|
||||||
|
logo_name_validator("Nom de logo invalide (alphanumérique, _)"),
|
||||||
|
validators.Length(
|
||||||
|
max=20, message="Un nom ne doit pas dépasser 20 caractères"
|
||||||
|
),
|
||||||
|
validators.DataRequired("Nom de logo requis (alphanumériques ou '-')"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
upload = FileField(
|
||||||
|
label="Sélectionner l'image",
|
||||||
|
validators=[
|
||||||
|
FileAllowed(
|
||||||
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
f"n'accepte que les fichiers image {', '.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}",
|
||||||
|
),
|
||||||
|
validators.DataRequired("Fichier image manquant"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
do_insert = SubmitField("ajouter une image")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def id(self):
|
||||||
|
return f"id=add_{self.dept_key.data}"
|
||||||
|
|
||||||
|
def validate_name(self, name):
|
||||||
|
dept_id = dept_key_to_id(self.dept_key.data)
|
||||||
|
if dept_id == GLOBAL:
|
||||||
|
dept_id = None
|
||||||
|
if find_logo(logoname=name.data, dept_id=dept_id, strict=True) is not None:
|
||||||
|
raise validators.ValidationError("Un logo de même nom existe déjà")
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
if self.data["do_insert"]:
|
||||||
|
if self.validate():
|
||||||
|
return LogoInsert.build_action(self.data)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def opened(self):
|
||||||
|
if self.do_insert.data:
|
||||||
|
if self.name.errors:
|
||||||
|
return "open"
|
||||||
|
if self.upload.errors:
|
||||||
|
return "open"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class LogoForm(FlaskForm):
|
||||||
|
"""Embed both presentation of a logo (cf. template file configuration.j2)
|
||||||
|
and all its data and UI action (change, delete)"""
|
||||||
|
|
||||||
|
dept_key = HiddenField()
|
||||||
|
logo_id = HiddenField()
|
||||||
|
upload = FileField(
|
||||||
|
label="Remplacer l'image",
|
||||||
|
validators=[
|
||||||
|
FileAllowed(
|
||||||
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
f"n'accepte que les fichiers image {', '.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
do_delete = SubmitField("Supprimer")
|
||||||
|
do_rename = SubmitField("Renommer")
|
||||||
|
new_name = StringField(
|
||||||
|
label="Nom",
|
||||||
|
validators=[
|
||||||
|
logo_name_validator("Nom de logo invalide (alphanumérique, _)"),
|
||||||
|
validators.Length(
|
||||||
|
max=20, message="Un nom ne doit pas dépasser 20 caractères"
|
||||||
|
),
|
||||||
|
validators.DataRequired("Nom de logo requis (alphanumériques ou '-')"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
logo = find_logo(
|
||||||
|
logoname=self.logo_id.data, dept_id=dept_key_to_id(self.dept_key.data)
|
||||||
|
)
|
||||||
|
if logo is None:
|
||||||
|
raise ScoValueError("logo introuvable")
|
||||||
|
self.logo = logo.select()
|
||||||
|
self.description = None
|
||||||
|
self.titre = None
|
||||||
|
self.can_delete = True
|
||||||
|
if self.dept_key.data == GLOBAL:
|
||||||
|
if self.logo_id.data == "header":
|
||||||
|
self.can_delete = False
|
||||||
|
self.description = ""
|
||||||
|
self.titre = "Logo en-tête"
|
||||||
|
if self.logo_id.data == "footer":
|
||||||
|
self.can_delete = False
|
||||||
|
self.titre = "Logo pied de page"
|
||||||
|
self.description = ""
|
||||||
|
else:
|
||||||
|
if self.logo_id.data == "header":
|
||||||
|
self.description = "Se substitue au header défini au niveau global"
|
||||||
|
self.titre = "Logo en-tête"
|
||||||
|
if self.logo_id.data == "footer":
|
||||||
|
self.description = "Se substitue au footer défini au niveau global"
|
||||||
|
self.titre = "Logo pied de page"
|
||||||
|
|
||||||
|
def id(self):
|
||||||
|
idstring = f"{self.dept_key.data}_{self.logo_id.data}"
|
||||||
|
return f"id={idstring}"
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
from app.scodoc.sco_config_actions import LogoRename
|
||||||
|
from app.scodoc.sco_config_actions import LogoUpdate
|
||||||
|
from app.scodoc.sco_config_actions import LogoDelete
|
||||||
|
|
||||||
|
if self.do_delete.data and self.can_delete:
|
||||||
|
return LogoDelete.build_action(self.data)
|
||||||
|
if self.upload.data and self.validate():
|
||||||
|
return LogoUpdate.build_action(self.data)
|
||||||
|
if self.do_rename.data and self.validate():
|
||||||
|
return LogoRename.build_action(self.data)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def opened(self):
|
||||||
|
if self.upload.data and self.upload.errors:
|
||||||
|
return "open"
|
||||||
|
if self.new_name.data and self.new_name.errors:
|
||||||
|
return "open"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class DeptForm(FlaskForm):
|
||||||
|
dept_key = HiddenField()
|
||||||
|
dept_name = HiddenField()
|
||||||
|
add_logo = FormField(AddLogoForm)
|
||||||
|
logos = FieldList(FormField(LogoForm))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def id(self):
|
||||||
|
return f"id=DEPT_{self.dept_key.data}"
|
||||||
|
|
||||||
|
def is_local(self):
|
||||||
|
if self.dept_key.data == GLOBAL:
|
||||||
|
return None
|
||||||
|
return True
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
action = self.add_logo.form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
for logo_entry in self.logos.entries:
|
||||||
|
logo_form = logo_entry.form
|
||||||
|
action = logo_form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_form(self, logoname=None):
|
||||||
|
"""Retourne le formulaire associé à un logo. None si pas trouvé"""
|
||||||
|
if logoname is None: # recherche de département
|
||||||
|
return self
|
||||||
|
return self.index.get(logoname, None)
|
||||||
|
|
||||||
|
def opened(self):
|
||||||
|
if self.add_logo.opened():
|
||||||
|
return "open"
|
||||||
|
for logo_form in self.logos:
|
||||||
|
if logo_form.opened():
|
||||||
|
return "open"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
compte = len(self.logos.entries)
|
||||||
|
if compte == 0:
|
||||||
|
return "vide"
|
||||||
|
elif compte == 1:
|
||||||
|
return "1 élément"
|
||||||
|
else:
|
||||||
|
return f"{compte} éléments"
|
||||||
|
|
||||||
|
|
||||||
|
def _make_dept_id_name():
|
||||||
|
"""Cette section assure que tous les départements sont traités (y compris ceux qu'ont pas de logo au départ)
|
||||||
|
et détermine l'ordre d'affichage des DeptForm (GLOBAL d'abord, puis par ordre alpha de nom de département)
|
||||||
|
-> [ (None, None), (dept_id, dept_name)... ]"""
|
||||||
|
depts = [(None, GLOBAL)]
|
||||||
|
for dept in (
|
||||||
|
Departement.query.filter_by(visible=True).order_by(Departement.acronym).all()
|
||||||
|
):
|
||||||
|
depts.append((dept.id, dept.acronym))
|
||||||
|
return depts
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_logos(modele):
|
||||||
|
"""sort logoname alphabetically but header and footer moved at start. (since there is no space in logoname)"""
|
||||||
|
|
||||||
|
def sort(name):
|
||||||
|
if name == "header":
|
||||||
|
return " 0"
|
||||||
|
if name == "footer":
|
||||||
|
return " 1"
|
||||||
|
return name
|
||||||
|
|
||||||
|
order = sorted(modele.keys(), key=sort)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
def _make_dept_data(dept_id, dept_name, modele):
|
||||||
|
dept_key = dept_id_to_key(dept_id)
|
||||||
|
data = {
|
||||||
|
"dept_key": dept_key,
|
||||||
|
"dept_name": dept_name,
|
||||||
|
"add_logo": {"dept_key": dept_key},
|
||||||
|
}
|
||||||
|
logos = []
|
||||||
|
if modele is not None:
|
||||||
|
for name in _ordered_logos(modele):
|
||||||
|
logos.append({"dept_key": dept_key, "logo_id": name})
|
||||||
|
data["logos"] = logos
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _make_depts_data(modele):
|
||||||
|
data = []
|
||||||
|
for dept_id, dept_name in _make_dept_id_name():
|
||||||
|
data.append(
|
||||||
|
_make_dept_data(
|
||||||
|
dept_id=dept_id, dept_name=dept_name, modele=modele.get(dept_id, None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _make_data(modele):
|
||||||
|
data = {
|
||||||
|
"depts": _make_depts_data(modele=modele),
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class LogosConfigurationForm(FlaskForm):
|
||||||
|
"Panneau de configuration des logos"
|
||||||
|
depts = FieldList(FormField(DeptForm))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# def _set_global_logos_infos(self):
|
||||||
|
# "specific processing for globals items"
|
||||||
|
# global_header = self.get_form(logoname="header")
|
||||||
|
# global_header.description = (
|
||||||
|
# "image placée en haut de certains documents documents PDF."
|
||||||
|
# )
|
||||||
|
# global_header.titre = "Logo en-tête"
|
||||||
|
# global_header.can_delete = False
|
||||||
|
# global_footer = self.get_form(logoname="footer")
|
||||||
|
# global_footer.description = (
|
||||||
|
# "image placée en pied de page de certains documents documents PDF."
|
||||||
|
# )
|
||||||
|
# global_footer.titre = "Logo pied de page"
|
||||||
|
# global_footer.can_delete = False
|
||||||
|
|
||||||
|
# def _build_dept(self, dept_id, dept_name, modele):
|
||||||
|
# dept_key = dept_id or GLOBAL
|
||||||
|
# data = {"dept_key": dept_key}
|
||||||
|
# entry = self.depts.append_entry(data)
|
||||||
|
# entry.form.build(dept_name, modele.get(dept_id, {}))
|
||||||
|
# self.index[str(dept_key)] = entry.form
|
||||||
|
|
||||||
|
# def build(self, modele):
|
||||||
|
# "Build the Form hierachy (DeptForm, LogoForm) and add extra data (from modele)"
|
||||||
|
# # if entries already initialized (POST). keep subforms
|
||||||
|
# self.index = {}
|
||||||
|
# # create entries in FieldList (one entry per dept
|
||||||
|
# for dept_id, dept_name in self.dept_id_name:
|
||||||
|
# self._build_dept(dept_id=dept_id, dept_name=dept_name, modele=modele)
|
||||||
|
# self._set_global_logos_infos()
|
||||||
|
|
||||||
|
def get_form(self, dept_key=GLOBAL, logoname=None):
|
||||||
|
"""Retourne un formulaire:
|
||||||
|
* pour un département (get_form(dept_id)) ou à un logo (get_form(dept_id, logname))
|
||||||
|
* propre à un département (get_form(dept_id, logoname) ou global (get_form(logoname))
|
||||||
|
retourne None si le formulaire cherché ne peut être trouvé
|
||||||
|
"""
|
||||||
|
dept_form = self.index.get(dept_key, None)
|
||||||
|
if dept_form is None: # département non trouvé
|
||||||
|
return None
|
||||||
|
return dept_form.get_form(logoname)
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
for dept_entry in self.depts:
|
||||||
|
dept_form = dept_entry.form
|
||||||
|
action = dept_form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def config_logos():
|
||||||
|
"Page de configuration des logos"
|
||||||
|
# nb: le contrôle d'accès (SuperAdmin) doit être fait dans la vue
|
||||||
|
form = LogosConfigurationForm(
|
||||||
|
data=_make_data(
|
||||||
|
modele=sco_logos.list_logos(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if form.is_submitted():
|
||||||
|
action = form.select_action()
|
||||||
|
if action:
|
||||||
|
action.execute()
|
||||||
|
flash(action.message)
|
||||||
|
return redirect(url_for("scodoc.configure_logos"))
|
||||||
|
else:
|
||||||
|
if not form.validate():
|
||||||
|
scu.flash_errors(form)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"config_logos.j2",
|
||||||
|
scodoc_dept=None,
|
||||||
|
title="Configuration ScoDoc",
|
||||||
|
form=form,
|
||||||
|
)
|
@ -1243,7 +1243,7 @@ class BasePreferences(object):
|
|||||||
{
|
{
|
||||||
"initvalue": 0,
|
"initvalue": 0,
|
||||||
"title": "Afficher toutes les évaluations sur les bulletins",
|
"title": "Afficher toutes les évaluations sur les bulletins",
|
||||||
"explanation": "y compris incomplètes ou futures (déconseillé, risque de publier des notes non définitives; n'affecte pas le calcul des moyennes)",
|
"explanation": "y compris incomplètes ou futures (déconseillé, risque de publier des notes non définitives)",
|
||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"category": "bul",
|
"category": "bul",
|
||||||
"labels": ["non", "oui"],
|
"labels": ["non", "oui"],
|
||||||
@ -2069,7 +2069,10 @@ class BasePreferences(object):
|
|||||||
|
|
||||||
self.load()
|
self.load()
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Préférences"),
|
html_sco_header.sco_header(
|
||||||
|
page_title="Préférences",
|
||||||
|
javascripts=["js/detail_summary_persistence.js"],
|
||||||
|
),
|
||||||
f"<h2>Préférences globales pour {scu.ScoURL()}</h2>",
|
f"<h2>Préférences globales pour {scu.ScoURL()}</h2>",
|
||||||
# f"""<p><a href="{url_for("scodoc.configure_logos", scodoc_dept=g.scodoc_dept)
|
# f"""<p><a href="{url_for("scodoc.configure_logos", scodoc_dept=g.scodoc_dept)
|
||||||
# }">modification des logos du département (pour documents pdf)</a></p>"""
|
# }">modification des logos du département (pour documents pdf)</a></p>"""
|
||||||
@ -2210,10 +2213,14 @@ class SemPreferences:
|
|||||||
) # a bug !
|
) # a bug !
|
||||||
sem = sco_formsemestre.get_formsemestre(self.formsemestre_id)
|
sem = sco_formsemestre.get_formsemestre(self.formsemestre_id)
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.html_sem_header("Préférences du semestre"),
|
html_sco_header.html_sem_header(
|
||||||
|
"Préférences du semestre",
|
||||||
|
javascripts=["js/detail_summary_persistence.js"],
|
||||||
|
),
|
||||||
"""
|
"""
|
||||||
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
|
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
|
||||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function sel_global(el, pref_name) {
|
function sel_global(el, pref_name) {
|
||||||
var tf = document.getElementById("tf");
|
var tf = document.getElementById("tf");
|
||||||
|
82
app/static/js/detail_summary_persistence.js
Normal file
82
app/static/js/detail_summary_persistence.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
Ce module a pour objet de conserver la configuration (état ouvert/fermé) des balises details/summary
|
||||||
|
d'un page donnée.
|
||||||
|
On stocke l'état de chaque balise dans le localstorage javascript (associé à la page)
|
||||||
|
pour cela il est nécessaire d'identifier chaque balise. Ceci se fait:
|
||||||
|
* soit au moyen de l'id si un id est déclaré pour la balise ('#' + id)
|
||||||
|
* soit en numérotant les balises dans l'ordre de parcours de la page. ('ds_' + numéro)
|
||||||
|
l'identifiant généré est stocké comme attribut ds_id de la balise.
|
||||||
|
|
||||||
|
la variable keepDetails peut être mise à false (par inclusion du code
|
||||||
|
<script type="text/javascript>keepDetails = false;</script> pour reinitialisés l'état de toutes
|
||||||
|
les balises (fermées par défaut sauf si attribut open déjà activé dans le code source de la page)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ID_ATTRIBUTE = "ds_id"
|
||||||
|
|
||||||
|
function genere_id(detail, idnum) {
|
||||||
|
let id = "ds_" + idnum;
|
||||||
|
if (detail.getAttribute("id")) {
|
||||||
|
id = "#" + detail.getAttribute("id");
|
||||||
|
}
|
||||||
|
detail.setAttribute(ID_ATTRIBUTE, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remise à l'état initial. doit être exécuté dès le chargement de la page pour que l'état 'open'
|
||||||
|
// des balises soit celui indiqué par le serveur (et donc indépendant du localstorage)
|
||||||
|
function reset_detail(detail, id) {
|
||||||
|
let opened = detail.getAttribute("open");
|
||||||
|
if (opened) {
|
||||||
|
detail.setAttribute("open", true);
|
||||||
|
localStorage.setItem(id, true);
|
||||||
|
} else {
|
||||||
|
detail.removeAttribute("open");
|
||||||
|
localStorage.setItem(id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restore_detail(detail, id) {
|
||||||
|
let status = localStorage.getItem(id);
|
||||||
|
if (status == "true") {
|
||||||
|
detail.setAttribute("open", true);
|
||||||
|
} else {
|
||||||
|
detail.removeAttribute("open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_listener(detail) {
|
||||||
|
detail.addEventListener('toggle', (e) => {
|
||||||
|
let id = e.target.getAttribute(ID_ATTRIBUTE);
|
||||||
|
let ante = e.target.getAttribute("open");
|
||||||
|
if (ante == null) {
|
||||||
|
localStorage.setItem(id, false);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(id, true);
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset_ds() {
|
||||||
|
let idnum = 0;
|
||||||
|
keepDetails = true;
|
||||||
|
details = document.querySelectorAll("details")
|
||||||
|
details.forEach(function (detail) {
|
||||||
|
let id = genere_id(detail, idnum);
|
||||||
|
console.log("Processing " + id)
|
||||||
|
if (keepDetails) {
|
||||||
|
restore_detail(detail, id);
|
||||||
|
} else {
|
||||||
|
reset_detail(detail, id);
|
||||||
|
}
|
||||||
|
add_listener(detail);
|
||||||
|
idnum++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
console.log("details/summary persistence ON");
|
||||||
|
reset_ds();
|
||||||
|
})
|
@ -20,7 +20,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_add_logo(add_logo_form) %}
|
{% macro render_add_logo(add_logo_form) %}
|
||||||
<details {{ add_logo_form.opened() }}>
|
<details {{ add_logo_form.id() }} {{ add_logo_form.opened() }}>
|
||||||
<summary>
|
<summary>
|
||||||
<h3>Ajouter un logo</h3>
|
<h3>Ajouter un logo</h3>
|
||||||
</summary>
|
</summary>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_logo(dept_form, logo_form) %}
|
{% macro render_logo(dept_form, logo_form) %}
|
||||||
<details {{ logo_form.opened() }}>
|
<details {{ logo_form.id() }} {{ logo_form.opened() }}>
|
||||||
{{ logo_form.hidden_tag() }}
|
{{ logo_form.hidden_tag() }}
|
||||||
<summary>
|
<summary>
|
||||||
{% if logo_form.titre %}
|
{% if logo_form.titre %}
|
||||||
@ -95,6 +95,7 @@
|
|||||||
|
|
||||||
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||||
<script src="/ScoDoc/static/js/config_logos.js"></script>
|
<script src="/ScoDoc/static/js/config_logos.js"></script>
|
||||||
|
<script src="/ScoDoc/static/js/detail_summary_persistence.js"></script>
|
||||||
|
|
||||||
<form id="config_logos_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
|
<form id="config_logos_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
@ -104,7 +105,7 @@
|
|||||||
{% for dept_entry in form.depts.entries %}
|
{% for dept_entry in form.depts.entries %}
|
||||||
{% set dept_form = dept_entry.form %}
|
{% set dept_form = dept_entry.form %}
|
||||||
{{ dept_entry.form.hidden_tag() }}
|
{{ dept_entry.form.hidden_tag() }}
|
||||||
<details {{ dept_form.opened() }}>
|
<details {{ dept_form.id() }} {{ dept_form.opened() }}>
|
||||||
<summary>
|
<summary>
|
||||||
<span class="entete_dept">
|
<span class="entete_dept">
|
||||||
{% if dept_entry.form.is_local() %}
|
{% if dept_entry.form.is_local() %}
|
||||||
|
Loading…
Reference in New Issue
Block a user