1782 lines
91 KiB
PHP
1782 lines
91 KiB
PHP
<?php
|
|
include "../config/config.php";
|
|
Init_session(basename($_SERVER['SCRIPT_NAME'])); // Fonction ouvrant la session et vérifiant que les accès sont autorisés.
|
|
|
|
|
|
|
|
|
|
//************************************************************************************************************************************
|
|
// Initialisation des variables globales
|
|
//************************************************************************************************************************************
|
|
$message_erreur = "";
|
|
$message_information = "";
|
|
|
|
// Messages transmis par une redirection (motif POST -> redirection -> GET, après une action sur les accès : un
|
|
// rafraîchissement de la page ne rejoue jamais l'action) :
|
|
if (isset($_SESSION['Bateaux']['flash_information'])) { $message_information .= $_SESSION['Bateaux']['flash_information']; unset($_SESSION['Bateaux']['flash_information']); }
|
|
if (isset($_SESSION['Bateaux']['flash_erreur'])) { $message_erreur .= $_SESSION['Bateaux']['flash_erreur']; unset($_SESSION['Bateaux']['flash_erreur']); }
|
|
|
|
Ouverture_bdd(); // Connexion à MySql
|
|
|
|
|
|
//************************************************************************************************************************************
|
|
// Traitement des variables d'entrées
|
|
//************************************************************************************************************************************
|
|
|
|
// Variables de SESSION :
|
|
if (!isset($_SESSION['Bateaux']['page'])) $_SESSION['Bateaux']['page'] = 'Accueil';
|
|
if (!isset($_SESSION['Bateaux']['ancre'])) $_SESSION['Bateaux']['ancre'] = "";
|
|
if (!isset($_SESSION['Bateaux']['origine'])) $_SESSION['Bateaux']['origine'] = "";
|
|
if (!isset($_SESSION['Bateaux']['tri_liste'])) $_SESSION['Bateaux']['tri_liste'] = "Flotte";
|
|
if (!isset($_SESSION['Bateaux']['sens_tri_liste'])) $_SESSION['Bateaux']['sens_tri_liste'] = "ASC";
|
|
if (!isset($_SESSION['Bateaux']['type_details_bateau'])) $_SESSION['Bateaux']['type_details_bateau'] = "resume"; // 'resume', 'gerer_les_acces' ou 'alertes'
|
|
if (!isset($_SESSION['Bateaux']['bateau']['Cle_primaire'])) $_SESSION['Bateaux']['bateau']['Cle_primaire'] = 0;
|
|
if (!isset($_SESSION['Bateaux']['bateau']['Nom'])) $_SESSION['Bateaux']['bateau']['Nom'] = "";
|
|
if (!isset($_SESSION['Bateaux']['bateau']['ID_Flotte'])) $_SESSION['Bateaux']['bateau']['ID_Flotte'] = 0;
|
|
if (!isset($_SESSION['Bateaux']['bateau']['Date_mes'])) $_SESSION['Bateaux']['bateau']['Date_mes'] = date("Y-m-d");
|
|
if (!isset($_SESSION['Bateaux']['bateau']['Commentaires'])) $_SESSION['Bateaux']['bateau']['Commentaires'] = "";
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Contrôles PRÉALABLES à toute action (synthèse §48) - auparavant, les actions GET et POST s'exécutaient avant tout
|
|
// contrôle : n'importe quel utilisateur connecté pouvait créer, modifier ou dévalider un bateau.
|
|
//---------------------------------------------------------------------------
|
|
if (!$_SESSION['acces_valide'])
|
|
{
|
|
Fermeture_bdd();
|
|
header('Location:shipmembers.php?action=navigation&destination=Login');
|
|
exit();
|
|
}
|
|
$est_administrateur = ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR);
|
|
|
|
// Toute requête POST doit porter le jeton de la session (protection contre les formulaires envoyés depuis un autre site,
|
|
// en complément de SameSite=Lax) :
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !Jeton_csrf_valide())
|
|
{
|
|
$_SESSION['Bateaux']['flash_erreur'] = t("Formulaire expiré ou invalide : merci de recommencer.");
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
// Gestion de la flotte (liste, création, modification, dévalidation d'un bateau) : réservée aux administrateurs :
|
|
$actions_flotte = array('tri_liste', 'creation_bateau', 'modification_bateau', 'demande_suppression_bateau');
|
|
if (!$est_administrateur &&
|
|
((isset($_GET['action']) && in_array($_GET['action'], $actions_flotte, true))
|
|
|| isset($_POST['validation_creation_bateau']) || isset($_POST['validation_modification_bateau'])
|
|
|| isset($_POST['confirmation_suppression_bateau'])))
|
|
{
|
|
$_SESSION['Bateaux']['flash_erreur'] = t("Action réservée aux administrateurs.");
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
|
|
// Variables GET :
|
|
if (isset($_GET['ancre'])) $_SESSION['Bateaux']['ancre'] = $_GET['ancre'];
|
|
|
|
if (isset($_GET['action']))
|
|
{
|
|
switch ($_GET['action'])
|
|
{
|
|
//===================================
|
|
case "navigation":
|
|
{
|
|
// Seules les pages consultables par simple navigation ; création, modification et suppression passent par leurs
|
|
// actions dédiées (contrôlées ci-dessus) :
|
|
if (isset($_GET['destination']) && in_array($_GET['destination'], array('Accueil', 'Details_bateau'), true))
|
|
{
|
|
$_SESSION['Bateaux']['page'] = $_GET['destination'];
|
|
}
|
|
|
|
if (isset($_GET['ancre']))
|
|
{
|
|
$_SESSION['Bateaux']['ancre'] = $_GET['ancre'];
|
|
}
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "changement_langue":
|
|
{
|
|
if (isset($_GET['langue']))
|
|
{
|
|
$_SESSION['langue'] = $_GET['langue'];
|
|
chargement_langue();
|
|
}
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "tri_liste":
|
|
{
|
|
if (isset($_GET['colonne']))
|
|
{
|
|
if ($_SESSION['Bateaux']['tri_liste'] == $_GET['colonne'])
|
|
{
|
|
if ($_SESSION['Bateaux']['sens_tri_liste'] == "ASC") $_SESSION['Bateaux']['sens_tri_liste'] = "DESC";
|
|
else $_SESSION['Bateaux']['sens_tri_liste'] = "ASC";
|
|
}
|
|
else
|
|
{
|
|
$_SESSION['Bateaux']['tri_liste'] = $_GET['colonne'];
|
|
$_SESSION['Bateaux']['sens_tri_liste'] = "ASC";
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "details_bateau":
|
|
{
|
|
if (isset($_GET['id_bateau']))
|
|
{
|
|
$_SESSION['Bateaux']['bateau']['Cle_primaire'] = (int)$_GET['id_bateau'];
|
|
$_SESSION['Bateaux']['type_details_bateau'] = "resume";
|
|
$_SESSION['Bateaux']['generation'] = 0; // Nouveau bateau : génération active
|
|
$_SESSION['Bateaux']['page'] = "Details_bateau";
|
|
|
|
if (isset($_GET['origine'])) $_SESSION['Bateaux']['origine'] = $_GET['origine'];
|
|
else $_SESSION['Bateaux']['origine'] = "";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "type_details_bateau":
|
|
{
|
|
if (isset($_GET['type']) && in_array($_GET['type'], array('resume', 'gerer_les_acces', 'alertes'), true)) $_SESSION['Bateaux']['type_details_bateau'] = $_GET['type'];
|
|
$_SESSION['Bateaux']['ancre'] = "";
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
// Génération consultée (synthèse §50) : 0 = génération active. La valeur n'est utilisée qu'après vérification
|
|
// qu'elle appartient bien au bateau affiché (préparation de la page).
|
|
case "choix_generation":
|
|
{
|
|
$_SESSION['Bateaux']['generation'] = isset($_GET['generation']) ? (int)$_GET['generation'] : 0;
|
|
$_SESSION['Bateaux']['type_details_bateau'] = "resume";
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "modification_bateau":
|
|
{
|
|
if (isset($_GET['id_bateau']))
|
|
{
|
|
$_SESSION['Bateaux']['bateau']['Cle_primaire'] = (int)$_GET['id_bateau'];
|
|
$_SESSION['Bateaux']['page'] = "Modification_bateau";
|
|
$_SESSION['Bateaux']['charger_bateau'] = true; // Valeurs lues en base à l'affichage du formulaire
|
|
}
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "creation_bateau":
|
|
{
|
|
$_SESSION['Bateaux']['page'] = "Creation_bateau";
|
|
// Formulaire vierge - réinitialisé ICI et non plus à chaque affichage : après une erreur de saisie, le formulaire
|
|
// réaffiché garde les valeurs saisies :
|
|
$_SESSION['Bateaux']['bateau'] = array('Cle_primaire' => 0, 'Nom' => "", 'ID_Flotte' => 0, 'Date_mes' => date("Y-m-d"), 'Commentaires' => "");
|
|
break;
|
|
}
|
|
|
|
//===================================
|
|
case "demande_suppression_bateau":
|
|
{
|
|
if (isset($_GET['id_bateau']))
|
|
{
|
|
$_SESSION['Bateaux']['bateau']['Cle_primaire'] = (int)$_GET['id_bateau'];
|
|
$_SESSION['Bateaux']['page'] = "Confirmation_suppression_bateau";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// NB : la confirmation de suppression n'est plus une action GET (un simple lien suffisait à dévalider un bateau) :
|
|
// elle est envoyée en POST, avec le jeton de session (voir plus bas).
|
|
}
|
|
|
|
Fermeture_bdd();
|
|
if ($_SESSION['Bateaux']['ancre'] == "") header('Location:bateaux.php'); // Permet de "vider" la barre d'URL des champs GET et donc de doubler le traitement en cas de raffraichissement de page...
|
|
else header('Location:bateaux.php#'.$_SESSION['Bateaux']['ancre'].'');
|
|
exit();
|
|
}
|
|
|
|
|
|
// Variables POST :
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Création/Modification Bateau (administrateurs - contrôlé plus haut) :
|
|
// NB : l'affectation d'un module ShipBase à un bateau ne se fait plus ici mais via TB_Affectations_bateaux
|
|
// (administration de la flotte, lot 4 - synthèse §44.10). Les anciens champs ID_Carte/Date_carte et les
|
|
// répertoires download/upload/archives du modèle CM4 ont été retirés (synthèse §47).
|
|
// Valeurs enregistrées TELLES QUELLES (requêtes préparées) : l'ancien addslashes() les stockait avec des barres
|
|
// obliques (d\'Artagnan), retirées ensuite à l'affichage par stripcslashes() (synthèse §48).
|
|
if (isset($_POST['validation_creation_bateau']) OR isset($_POST['validation_modification_bateau']))
|
|
{
|
|
// Mémorisation des valeurs saisies pour les réafficher en cas d'erreur :
|
|
$_SESSION['Bateaux']['bateau']['Nom'] = trim((string)($_POST['Nom'] ?? ""));
|
|
$_SESSION['Bateaux']['bateau']['ID_Flotte'] = (int)($_POST['ID_Flotte'] ?? 0);
|
|
$_SESSION['Bateaux']['bateau']['Date_mes'] = trim((string)($_POST['Date_mes'] ?? ""));
|
|
$_SESSION['Bateaux']['bateau']['Commentaires'] = (string)($_POST['Commentaires'] ?? "");
|
|
|
|
// Vérifications :
|
|
$longueur_nom = mb_strlen($_SESSION['Bateaux']['bateau']['Nom']);
|
|
if ($longueur_nom < 1 || $longueur_nom > 50) $message_erreur .= t("Le nom du bateau doit comporter de 1 à 50 caractères.")."<br />";
|
|
$date = date_create_from_format('!Y-m-d', $_SESSION['Bateaux']['bateau']['Date_mes']);
|
|
if ($date === false || $date->format('Y-m-d') !== $_SESSION['Bateaux']['bateau']['Date_mes']) $message_erreur .= t("Date de mise en service invalide (format AAAA-MM-JJ).")."<br />";
|
|
if ($_SESSION['Bateaux']['bateau']['ID_Flotte'] != 0)
|
|
{
|
|
$reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Flottes WHERE Cle_primaire=? AND Validite=1",
|
|
array(array('i', $_SESSION['Bateaux']['bateau']['ID_Flotte'])), __FILE__, __LINE__);
|
|
$donnees = Recherche_ligne_bdd($reponse);
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
if ($donnees['nb'] == 0) $message_erreur .= t("Flotte inconnue.")."<br />";
|
|
}
|
|
|
|
if ($message_erreur == "") // Sinon : le formulaire est réaffiché avec les valeurs saisies et le message d'erreur
|
|
{
|
|
if (isset($_POST['validation_creation_bateau']))
|
|
{
|
|
$requete = 'INSERT INTO TB_Bateaux (Nom, ID_Flotte, Date_mes, Commentaires) VALUES (?, ?, ?, ?)';
|
|
Requete_preparee_bdd($requete,
|
|
array(
|
|
array('s', $_SESSION['Bateaux']['bateau']['Nom']),
|
|
array('i', $_SESSION['Bateaux']['bateau']['ID_Flotte']),
|
|
array('s', $_SESSION['Bateaux']['bateau']['Date_mes']),
|
|
array('s', $_SESSION['Bateaux']['bateau']['Commentaires'])),
|
|
__FILE__, __LINE__);
|
|
$_SESSION['Bateaux']['bateau']['Cle_primaire'] = Recuperation_id_cree_bdd();
|
|
$_SESSION['Bateaux']['flash_information'] = str_replace("[[Nom]]", htmlspecialchars($_SESSION['Bateaux']['bateau']['Nom']), t("Le bateau [[Nom]] vient d'être créé."));
|
|
}
|
|
else
|
|
{
|
|
$requete = 'UPDATE TB_Bateaux SET Nom=?, ID_Flotte=?, Date_mes=?, Commentaires=? WHERE Cle_primaire=? AND Validite=1';
|
|
Requete_preparee_bdd($requete,
|
|
array(
|
|
array('s', $_SESSION['Bateaux']['bateau']['Nom']),
|
|
array('i', $_SESSION['Bateaux']['bateau']['ID_Flotte']),
|
|
array('s', $_SESSION['Bateaux']['bateau']['Date_mes']),
|
|
array('s', $_SESSION['Bateaux']['bateau']['Commentaires']),
|
|
array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
$_SESSION['Bateaux']['flash_information'] = str_replace("[[Nom]]", htmlspecialchars($_SESSION['Bateaux']['bateau']['Nom']), t("Le bateau [[Nom]] vient d'être modifié."));
|
|
}
|
|
$_SESSION['Bateaux']['page'] = "Accueil";
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Dévalidation d'un bateau (administrateurs - contrôlé plus haut ; POST avec jeton) :
|
|
if (isset($_POST['confirmation_suppression_bateau']))
|
|
{
|
|
$id_bateau = (int)$_POST['confirmation_suppression_bateau'];
|
|
Requete_preparee_bdd('UPDATE TB_Bateaux SET Validite=0 WHERE Cle_primaire=? AND Validite=1', array(array('i', $id_bateau)), __FILE__, __LINE__);
|
|
if (Nb_lignes_modifiees() == 1) $_SESSION['Bateaux']['flash_information'] = t("Le bateau a été dévalidé dans la base de données.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = t("Ce bateau n'existe pas ou a déjà été dévalidé.");
|
|
$_SESSION['Bateaux']['page'] = "Accueil";
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// NB : les actions sur les accès et invitations (création, suspension, réactivation, révocation, modification, PDF)
|
|
// sont traitées plus bas, dans la page Details_bateau, APRÈS le calcul des droits de l'utilisateur sur le bateau
|
|
// (elles étaient auparavant exécutées ici, avant tout contrôle - synthèse §47).
|
|
|
|
|
|
|
|
//************************************************************************************************************************************
|
|
// Traitement des données
|
|
//************************************************************************************************************************************
|
|
|
|
// Protection contre les accès directs... :
|
|
$flag_sortie = true;
|
|
do // Dummy loop :
|
|
{
|
|
if (!$_SESSION['acces_valide']) break; // Si on n'est pas identifié, on ne peut pas accéder quoi qu'il arrive...
|
|
|
|
if ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR)
|
|
{
|
|
$flag_sortie = false; // Si on est administrateur, on a toujours accès....
|
|
break;
|
|
}
|
|
|
|
// Recherche des droits d'accès à un bateau en particulier :
|
|
if ($_SESSION['Bateaux']['page'] == "Details_bateau")
|
|
{
|
|
$requete = "SELECT * FROM TB_Lien_bateau_shipmember WHERE Statut='actif' AND Fin_validite >= NOW() AND ID_SHIPMEMBER=? AND ID_BATEAU=? ";
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(
|
|
array('i', $_SESSION['shipmember']['Cle_primaire']),
|
|
array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$flag_sortie = false;
|
|
break;
|
|
}
|
|
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
}
|
|
} while (0);
|
|
|
|
if ($flag_sortie)
|
|
{
|
|
// exit indispensable : sans lui, le reste de la page (actions comprises) s'exécutait malgré la redirection (synthèse §47)
|
|
Fermeture_bdd();
|
|
header('Location:shipmembers.php?action=navigation&destination=Login');
|
|
exit();
|
|
}
|
|
|
|
|
|
switch ($_SESSION['Bateaux']['page'])
|
|
{
|
|
//****************************************************************************************************************************
|
|
case "Accueil":
|
|
{
|
|
//--------------------------------------------------
|
|
// Récupération des informations sur les bateaux :
|
|
$nb_bateaux = 0;
|
|
|
|
switch($_SESSION['Bateaux']['tri_liste'])
|
|
{
|
|
case "Nom": { $tri_liste = " ORDER By Nom ".$_SESSION['Bateaux']['sens_tri_liste']; break; }
|
|
case "Flotte" : { $tri_liste = " ORDER By nom_flotte ".$_SESSION['Bateaux']['sens_tri_liste'].", Nom"; break; }
|
|
case "Adresse_mac": { $tri_liste = " ORDER By adresse_carte ".$_SESSION['Bateaux']['sens_tri_liste']; break; }
|
|
case "Version": { $tri_liste = " ORDER By version ".$_SESSION['Bateaux']['sens_tri_liste'].", Nom"; break; }
|
|
case "Date_maj": { $tri_liste = " ORDER By Date_derniere_maj ".$_SESSION['Bateaux']['sens_tri_liste'].", Nom"; break; }
|
|
default : { $tri_liste = " ORDER By nom_flotte, Nom ".$_SESSION['Bateaux']['sens_tri_liste']; break; }
|
|
}
|
|
|
|
$requete = 'SELECT TB_Bateaux.Cle_primaire AS Cle_primaire, ';
|
|
$requete .= ' TB_Bateaux.Nom AS Nom, ';
|
|
$requete .= ' (SELECT Nom FROM TB_Flottes WHERE Validite=1 AND Cle_primaire=TB_Bateaux.ID_Flotte) AS nom_flotte, ';
|
|
$requete .= ' (SELECT Adresse_mac FROM TB_Affectations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS adresse_carte, ';
|
|
$requete .= ' (SELECT Version_soft FROM TB_Generations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS version, ';
|
|
$requete .= ' TB_Bateaux.Date_derniere_maj AS Date_derniere_maj, ';
|
|
$requete .= ' TB_Bateaux.Commentaires AS Commentaires ';
|
|
$requete .= ' FROM `TB_Bateaux` ';
|
|
$requete .= ' WHERE Validite=1 ';
|
|
$requete .= $tri_liste;
|
|
//echo "Requête = [ ".$requete."; ]<br />";
|
|
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$bateaux[$nb_bateaux] = $donnees;
|
|
|
|
// Mise en forme de certaines variables :
|
|
$bateaux[$nb_bateaux]['version'] = ($donnees['version'] == "") ? '<i>inconnue</i>' : htmlspecialchars($donnees['version']);
|
|
if ($donnees['Date_derniere_maj'] < "2020-01-02") $bateaux[$nb_bateaux]['Date_derniere_maj'] = "<i>Jamais</i>";
|
|
else $bateaux[$nb_bateaux]['Date_derniere_maj'] = '<span data-date-utc="'.htmlspecialchars(str_replace(' ', 'T', $donnees['Date_derniere_maj']).'Z').'">'.htmlspecialchars($donnees['Date_derniere_maj']).' UTC</span>';
|
|
|
|
$nb_bateaux ++;
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
|
|
break;
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
case "Details_bateau":
|
|
{
|
|
//--------------------------------------------------
|
|
// Récupération des informations sur le bateau :
|
|
$nb_bateaux = 0;
|
|
|
|
$requete = 'SELECT *, ';
|
|
$requete .= ' (SELECT Adresse_mac FROM TB_Affectations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS Adresse_mac, ';
|
|
$requete .= ' (SELECT Version_soft FROM TB_Generations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS version, ';
|
|
// Adresse NetBird du module affecté (Live distant possible seulement si elle est connue - synthèse §49) :
|
|
$requete .= ' (SELECT C.Adresse_netbird FROM TB_Affectations_bateaux A JOIN TB_Cartes C ON C.Adresse_mac=A.Adresse_mac WHERE A.ID_Bateau=TB_Bateaux.Cle_primaire AND A.Date_fin IS NULL) AS Adresse_netbird ';
|
|
$requete .= ' FROM TB_Bateaux WHERE Cle_primaire=? ';
|
|
//echo "Requête = [ ".$requete."; ]<br />";
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(
|
|
array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$bateau = $donnees;
|
|
$bateau['Date_derniere_maj_utc'] = $donnees['Date_derniere_maj']; // Brute (UTC), pour l'indicateur de fraîcheur
|
|
|
|
// Mise en forme de textes :
|
|
if ($donnees['Date_derniere_maj'] < "2020-01-02") $bateau['Date_derniere_maj'] = "<i>".t("Jamais")."</i>";
|
|
else {
|
|
$bateau['Date_derniere_maj'] = Formatage_date($donnees['Date_derniere_maj']);
|
|
$_SESSION['Bateaux']['derniere_maj'] = $donnees['Date_derniere_maj'];
|
|
}
|
|
// $bateau['Commentaires'] = htmlspecialchars(stripcslashes($bateau['Commentaires']));
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------
|
|
// Recherche des droits d'accès au bateau :
|
|
//---------------------------------------------
|
|
if ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR) // Dans le cas des administrateurs, on accorde tous les droits, indépendamment des paramètres du bateau
|
|
{
|
|
$acces_profil['Creation_profils'] = 1;
|
|
$acces_profil['Devalidation_profils'] = 2;
|
|
$acces_profil['Consultation_donnees'] = 2;
|
|
$acces_profil['Modification_acces'] = 1;
|
|
}
|
|
else
|
|
{
|
|
// Valeurs par défaut :
|
|
$id_type_acces = 0;
|
|
$acces_profil['Creation_profils'] = 0;
|
|
$acces_profil['Devalidation_profils'] = 0;
|
|
$acces_profil['Consultation_donnees'] = 0;
|
|
$acces_profil['Modification_acces'] = 0;
|
|
|
|
// Recherche de l'ID de l'accès le plus élevé pour ce bateau et ce shipmember :
|
|
// NB : "ORDER BY ... ASC" - plus la clé du type est petite, plus il donne de droits (2 Propriétaire, 3 Installateur,
|
|
// 4 Skipper). L'ancien "DESC" retenait l'accès le plus FAIBLE quand un SM en avait plusieurs (synthèse §47).
|
|
$requete = "SELECT ID_Type_acces_bateau_shipmember FROM TB_Lien_bateau_shipmember WHERE Statut='actif' AND ID_Shipmember=? AND ID_Bateau=? AND Fin_validite>=? ORDER BY ID_Type_acces_bateau_shipmember ASC LIMIT 1";
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(
|
|
array('i', $_SESSION['shipmember']['Cle_primaire']),
|
|
array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire']),
|
|
array('s', date_format(date_create(), 'Y-m-d H:i:s'))),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$id_type_acces = $donnees['ID_Type_acces_bateau_shipmember'];
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
|
|
|
|
// Recherche des caractérirtiques de l'accès le plus élevé trouvé :
|
|
if ($id_type_acces > 0)
|
|
{
|
|
$requete = 'SELECT * FROM TB_Types_acces_bateau_shipmember WHERE Validite=1 AND Cle_primaire=?';
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(
|
|
array('i', $id_type_acces)),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$acces_profil = $donnees;
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
}
|
|
$acces_profil['Modification_acces'] = 0; // Modification d'un accès (type, fin) : réservée aux administrateurs
|
|
}
|
|
|
|
|
|
// Récupération des types d'accès possibles :
|
|
$requete = 'SELECT * FROM TB_Types_acces_bateau_shipmember WHERE Validite=1 ';
|
|
$reponse = Requete_bdd($requete, __FILE__, __LINE__);
|
|
$type_acces_nb = 0;
|
|
while($donnees = Recherche_ligne_bdd($reponse)){
|
|
if ($_SESSION['langue'] == "EN") $donnees['Nom'] = $donnees['Nom_en'];
|
|
else $donnees['Nom'] = $donnees['Nom_fr'];
|
|
|
|
$type_acces[$type_acces_nb++] = $donnees;
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Actions sur les accès et invitations de CE bateau (synthèse §47). Traitées ici, APRÈS le calcul des droits :
|
|
// - chaque action vérifie que l'accès visé appartient bien au bateau affiché ;
|
|
// - créer une invitation, télécharger son PDF : administrateur, ou Creation_profils = 1 ;
|
|
// - suspendre, réactiver, révoquer : administrateur, ou Devalidation_profils = 2 (tous types), ou = 1 (tous
|
|
// sauf Propriétaire) ; une invitation en attente peut aussi être révoquée par qui peut en créer ;
|
|
// - modifier le type ou la fin d'un accès : administrateur.
|
|
// Après chaque action : redirection vers la page (motif POST -> redirection -> GET).
|
|
//------------------------------------------------------------------------------------------------------------
|
|
$est_administrateur = ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR);
|
|
$peut_inviter = ($est_administrateur || $acces_profil['Creation_profils'] == 1);
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Live distant (synthèse §49) : création d'un ticket (aléatoire, 60 s, usage unique, lié à l'utilisateur et au
|
|
// bateau) puis redirection vers https://b<N>.ship2net.fr/_auth?ticket=... - le vérificateur du VPS l'échange
|
|
// contre une session (cookie propre au sous-domaine du bateau). Seule l'empreinte du ticket est enregistrée.
|
|
// Droit : arriver jusqu'ici suffit (administrateur, ou accès actif à CE bateau - contrôlé plus haut) ; le
|
|
// vérificateur revérifie de toute façon à l'ouverture de la session puis à chaque requête.
|
|
$live_possible = ($bateau['Adresse_netbird'] ?? '') != '';
|
|
if (isset($_POST['ouvrir_live']))
|
|
{
|
|
if (!$live_possible)
|
|
{
|
|
$_SESSION['Bateaux']['flash_erreur'] = t("Aucun module ShipBase joignable n'est affecté à ce bateau : le Live est indisponible.");
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
$ticket = bin2hex(random_bytes(32));
|
|
Requete_preparee_bdd("INSERT INTO TB_Tickets_live (Empreinte, ID_Shipmember, ID_Bateau, Expiration) VALUES (?, ?, ?, NOW() + INTERVAL 60 SECOND)",
|
|
array(array('s', hash('sha256', $ticket)), array('i', $_SESSION['shipmember']['Cle_primaire']), array('i', $bateau['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
Fermeture_bdd();
|
|
header('Location: https://b'.(int)$bateau['Cle_primaire'].'.'.URL_SITE.'/_auth?ticket='.$ticket, true, 303);
|
|
exit();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Alertes (synthèse §51, étape E) : chacun ne règle que SES préférences et SON numéro ; réactivation d'une
|
|
// suspension : administrateur, ou droit d'inviter sur ce bateau (Creation_profils). Redirection après action.
|
|
if (isset($_POST['enregistrer_preferences_alertes']) || isset($_POST['enregistrer_telephone']) || isset($_POST['envoyer_code_telephone'])
|
|
|| isset($_POST['verifier_code_telephone']) || isset($_POST['reactiver_suspension']))
|
|
{
|
|
$id_sm = (int)$_SESSION['shipmember']['Cle_primaire'];
|
|
if (isset($_POST['enregistrer_preferences_alertes']))
|
|
{
|
|
$sms = !empty($_POST['alertes_sms']);
|
|
Enregistrer_preferences_alertes($id_sm, $bateau['Cle_primaire'], !empty($_POST['alertes_email']), $sms);
|
|
$_SESSION['Bateaux']['flash_information'] = t("Vos préférences d'alerte sont enregistrées.");
|
|
if ($sms && !Telephone_shipmember($id_sm)['verifie'])
|
|
$_SESSION['Bateaux']['flash_information'] .= ' '.t("Les SMS ne partiront qu'une fois votre numéro vérifié (ci-dessous).");
|
|
}
|
|
else if (isset($_POST['enregistrer_telephone']))
|
|
{
|
|
$erreur = Enregistrer_telephone($id_sm, (string)($_POST['telephone'] ?? ''), Telephone_shipmember($id_sm)['id_pays']);
|
|
if ($erreur == '') $_SESSION['Bateaux']['flash_information'] = t("Numéro enregistré.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = $erreur;
|
|
}
|
|
else if (isset($_POST['envoyer_code_telephone']))
|
|
{
|
|
$resultat = Envoyer_code_telephone($id_sm);
|
|
$_SESSION['Bateaux'][$resultat['ok'] ? 'flash_information' : 'flash_erreur'] = $resultat['message'];
|
|
}
|
|
else if (isset($_POST['verifier_code_telephone']))
|
|
{
|
|
$erreur = Verifier_code_telephone($id_sm, (string)($_POST['code_telephone'] ?? ''));
|
|
if ($erreur == '') $_SESSION['Bateaux']['flash_information'] = t("Votre numéro est vérifié : vous recevrez les alertes par SMS.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = $erreur;
|
|
}
|
|
else
|
|
{
|
|
if ($est_administrateur || (int)($acces_profil['Creation_profils'] ?? 0) == 1)
|
|
{
|
|
if (Reactiver_suspension((int)$_POST['reactiver_suspension'], $bateau['Cle_primaire'], $id_sm)) $_SESSION['Bateaux']['flash_information'] = t("Alertes réactivées pour tous les destinataires.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = t("Cette suspension n'existe pas ou a déjà été levée.");
|
|
}
|
|
else $_SESSION['Bateaux']['flash_erreur'] = t("Votre type d'accès ne permet pas de réactiver les alertes.");
|
|
}
|
|
$_SESSION['Bateaux']['type_details_bateau'] = "alertes";
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
if (isset($_POST['creer_invitation']))
|
|
{
|
|
if (!$peut_inviter) $_SESSION['Bateaux']['flash_erreur'] = t("Vous n'avez pas le droit d'inviter sur ce bateau.");
|
|
else
|
|
{
|
|
$resultat = Creer_invitation($bateau['Cle_primaire'], $_POST['email_invite'] ?? '', $_POST['prenom_invite'] ?? '',
|
|
$_POST['nom_invite'] ?? '', (int)($_POST['type_acces'] ?? 0), $_POST['fin_acces'] ?? '',
|
|
$_SESSION['shipmember']['Cle_primaire']);
|
|
if ($resultat['ok']) $_SESSION['Bateaux']['flash_information'] = str_replace("[[Code]]", $resultat['code'],
|
|
t("Invitation créée (code [[Code]]). Téléchargez son PDF dans la liste ci-dessous."));
|
|
else $_SESSION['Bateaux']['flash_erreur'] = $resultat['raison'];
|
|
}
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
if (isset($_POST['action_acces']) && isset($_POST['ID']))
|
|
{
|
|
$lien = Lien_du_bateau((int)$_POST['ID'], $bateau['Cle_primaire']);
|
|
$action = (string)$_POST['action_acces'];
|
|
|
|
if ($lien === null) $_SESSION['Bateaux']['flash_erreur'] = t("Accès introuvable pour ce bateau.");
|
|
else if ($action == 'pdf')
|
|
{
|
|
if (!$peut_inviter || $lien['Statut'] != 'en_attente') $_SESSION['Bateaux']['flash_erreur'] = t("Action non autorisée.");
|
|
else
|
|
{
|
|
$invitation = Invitation_par_code($lien['Code']);
|
|
$invitation['Code'] = $lien['Code'];
|
|
$invitation['Expiration_invitation'] = $lien['Expiration_invitation'];
|
|
Fermeture_bdd();
|
|
code_acces_pdf($invitation);
|
|
exit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$autorise = Peut_devalider_acces($acces_profil, $est_administrateur, $lien['ID_Type_acces_bateau_shipmember'])
|
|
|| ($action == 'revoquer' && $lien['Statut'] == 'en_attente' && $peut_inviter);
|
|
$transitions = array( 'suspendre' => array("'actif'", 'suspendu'),
|
|
'reactiver' => array("'suspendu'", 'actif'),
|
|
'revoquer' => array("'en_attente','actif','suspendu'", 'revoque'));
|
|
if (!$autorise || !isset($transitions[$action])) $_SESSION['Bateaux']['flash_erreur'] = t("Action non autorisée.");
|
|
else
|
|
{
|
|
$requete = "UPDATE TB_Lien_bateau_shipmember SET Statut=? WHERE ID=? AND ID_Bateau=? AND Statut IN (".$transitions[$action][0].")";
|
|
Requete_preparee_bdd($requete, array(
|
|
array('s', $transitions[$action][1]), array('i', $lien['ID']), array('i', $bateau['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
if (Nb_lignes_modifiees() == 1) $_SESSION['Bateaux']['flash_information'] = t("Accès mis à jour.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = t("Cet accès a déjà changé d'état.");
|
|
}
|
|
}
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
if (isset($_POST['modifierAcces']) && isset($_POST['ID']))
|
|
{
|
|
$lien = Lien_du_bateau((int)$_POST['ID'], $bateau['Cle_primaire']);
|
|
$date = date_create_from_format('Y-m-d', (string)($_POST['fin_validite'] ?? ''));
|
|
if (!$est_administrateur || $lien === null || $lien['Statut'] == 'revoque') $_SESSION['Bateaux']['flash_erreur'] = t("Action non autorisée.");
|
|
else if ($date === false) $_SESSION['Bateaux']['flash_erreur'] = t("Date de fin d'accès invalide.");
|
|
else
|
|
{
|
|
$requete = "UPDATE TB_Lien_bateau_shipmember SET Fin_validite=?, ID_Type_acces_bateau_shipmember=?
|
|
WHERE ID=? AND ID_Bateau=? AND EXISTS (SELECT 1 FROM TB_Types_acces_bateau_shipmember WHERE Cle_primaire=? AND Validite=1)";
|
|
Requete_preparee_bdd($requete, array(
|
|
array('s', $date->format('Y-m-d').' 23:59:59'), array('i', (int)$_POST['type_profil']),
|
|
array('i', $lien['ID']), array('i', $bateau['Cle_primaire']), array('i', (int)$_POST['type_profil'])),
|
|
__FILE__, __LINE__);
|
|
if (Nb_lignes_modifiees() == 1) $_SESSION['Bateaux']['flash_information'] = t("Accès mis à jour.");
|
|
else $_SESSION['Bateaux']['flash_erreur'] = t("Aucune modification enregistrée (type d'accès invalide, ou valeurs identiques).");
|
|
}
|
|
Fermeture_bdd();
|
|
header('Location: bateaux.php');
|
|
exit();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// Recherche des informations complémentaires sur le bateau :
|
|
switch($_SESSION['Bateaux']['type_details_bateau'])
|
|
{
|
|
//=========================
|
|
case "gerer_les_acces":
|
|
{
|
|
// Accès et invitations du bateau, hors révoqués (synthèse §47) - une seule requête au lieu de 3 par ligne :
|
|
$colonne_type = ($_SESSION['langue'] == "EN") ? 'Nom_en' : 'Nom_fr';
|
|
$requete = "SELECT L.*, S.Prenom AS sm_Prenom, S.Nom AS sm_Nom, S.Email AS sm_Email,
|
|
C.Prenom AS cree_par_Prenom, C.Nom AS cree_par_Nom, T.".$colonne_type." AS Niveau_acces_nom,
|
|
(L.Expiration_invitation IS NOT NULL AND L.Expiration_invitation <= NOW()) AS Expiree
|
|
FROM TB_Lien_bateau_shipmember L
|
|
LEFT JOIN TB_Shipmembers S ON S.ID = L.ID_Shipmember
|
|
LEFT JOIN TB_Shipmembers C ON C.ID = L.Cree_par
|
|
LEFT JOIN TB_Types_acces_bateau_shipmember T ON T.Cle_primaire = L.ID_Type_acces_bateau_shipmember
|
|
WHERE L.ID_Bateau=? AND L.Statut <> 'revoque'
|
|
ORDER BY FIELD(L.Statut, 'actif', 'suspendu', 'en_attente'), L.Date_creation";
|
|
$reponse = Requete_preparee_bdd($requete, array(array('i', $bateau['Cle_primaire'])), __FILE__, __LINE__);
|
|
$utilisateur_acces_nb = 0;
|
|
while ($donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC))
|
|
{
|
|
// Destinataire affiché : le ShipMember s'il a accepté, sinon la personne invitée :
|
|
if ($donnees['Statut'] == 'en_attente') $donnees['Destinataire'] = trim($donnees['Prenom_invite'].' '.$donnees['Nom_invite']).' ['.$donnees['Email_invite'].']';
|
|
else $donnees['Destinataire'] = trim($donnees['sm_Prenom'].' '.$donnees['sm_Nom']).' ['.$donnees['sm_Email'].']';
|
|
$donnees['Cree_par_nom'] = trim($donnees['cree_par_Prenom'].' '.$donnees['cree_par_Nom']);
|
|
unset($donnees['Code']); // Jamais envoyé au navigateur (le JSON de chaque ligne alimente le popup)
|
|
$utilisateur_acces[$utilisateur_acces_nb++] = $donnees;
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
break;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------------
|
|
// Alertes (synthèse §51, étape E) :
|
|
case "alertes":
|
|
{
|
|
$vue_alertes = array(
|
|
'preferences' => Preferences_alertes($_SESSION['shipmember']['Cle_primaire'], $bateau['Cle_primaire']),
|
|
'telephone' => Telephone_shipmember($_SESSION['shipmember']['Cle_primaire']),
|
|
'alertes' => Alertes_bateau($bateau['Cle_primaire'], 30),
|
|
'suspensions' => Suspensions_actives($bateau['Cle_primaire']),
|
|
'peut_reactiver' => ($est_administrateur || (int)($acces_profil['Creation_profils'] ?? 0) == 1));
|
|
break;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------
|
|
// Données du bateau (synthèse §50) : lues dans la base de la génération consultée (active par défaut), par
|
|
// une connexion dédiée - la connexion principale du site n'est pas touchée.
|
|
default:
|
|
{
|
|
$consultation = array('autorisee' => ($est_administrateur || (int)($acces_profil['Consultation_donnees'] ?? 0) >= 1),
|
|
'generations' => array(), 'generation' => null, 'domaines' => array(), 'erreur' => null,
|
|
'periode_archivage' => CONSULTATION_PERIODE_ARCHIVAGE_DEFAUT);
|
|
if (!$consultation['autorisee']) break;
|
|
|
|
$consultation['generations'] = Generations_bateau($bateau['Cle_primaire']);
|
|
foreach ($consultation['generations'] as $generation)
|
|
{
|
|
// Génération demandée si elle appartient bien à ce bateau, sinon la génération active :
|
|
if ((int)$generation['Cle_primaire'] == (int)($_SESSION['Bateaux']['generation'] ?? 0)) { $consultation['generation'] = $generation; break; }
|
|
if ($generation['Date_fin'] === null && $consultation['generation'] === null) $consultation['generation'] = $generation;
|
|
}
|
|
if ($consultation['generation'] === null) break; // Aucune archive reçue pour ce bateau
|
|
|
|
$db_generation = Connexion_generation($consultation['generation']['Nom_base']);
|
|
if ($db_generation === null) { $consultation['erreur'] = t("Les données de ce bateau sont momentanément inaccessibles."); break; }
|
|
$consultation['periode_archivage'] = Periode_archivage_bateau($db_generation);
|
|
$consultation['alarmes'] = Episodes_alarmes($db_generation, 20);
|
|
foreach (array('circuits' => t("Circuits"), 'capteurs' => t("Capteurs"), 'batteries' => t("Batteries"), 'reservoirs' => t("Réservoirs")) as $domaine => $titre)
|
|
{
|
|
$consultation['domaines'][$domaine] = array('titre' => $titre, 'familles' => Elements_domaine($db_generation, $domaine));
|
|
}
|
|
$db_generation->close();
|
|
|
|
// Génération active : demande d'archive immédiate (si les données ne sont pas déjà récentes - étape 3d) :
|
|
$consultation['demande'] = ($consultation['generation']['Date_fin'] === null)
|
|
? Demander_archive_si_besoin($bateau['Cle_primaire'], $bateau['Date_derniere_maj_utc']) : null;
|
|
$consultation['demande_archive'] = (($consultation['demande']['statut'] ?? '') == 'demandee');
|
|
break;
|
|
}
|
|
|
|
} // Fin du switch 'type_details_bateau'
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
case "Creation_bateau":
|
|
case "Modification_bateau":
|
|
{
|
|
//--------------------------------------------------
|
|
// Préparation des variables du bateau. Création : formulaire réinitialisé par l'action "creation_bateau".
|
|
// Modification : valeurs lues en base à l'ouverture du formulaire seulement (après une erreur de saisie, les
|
|
// valeurs saisies sont conservées) :
|
|
if ($_SESSION['Bateaux']['page'] == "Modification_bateau" && !empty($_SESSION['Bateaux']['charger_bateau']))
|
|
{
|
|
unset($_SESSION['Bateaux']['charger_bateau']);
|
|
$requete = 'SELECT Cle_primaire, Nom, ID_Flotte, Date_mes, Commentaires FROM TB_Bateaux WHERE Cle_primaire=? AND Validite=1';
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(
|
|
array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire'])),
|
|
__FILE__, __LINE__);
|
|
$donnees = Recherche_ligne_bdd($reponse);
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
if ($donnees) $_SESSION['Bateaux']['bateau'] = $donnees;
|
|
else $_SESSION['Bateaux']['page'] = "Accueil";
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// Recherche des flottes existantes :
|
|
$nb_flottes = 0;
|
|
|
|
$flottes[$nb_flottes]['Cle_primaire'] = 0;
|
|
$flottes[$nb_flottes]['Nom'] = '--- Affecté à aucune flotte ---';
|
|
$nb_flottes ++;
|
|
|
|
$requete = 'SELECT * FROM TB_Flottes WHERE Validite=1 ORDER BY Nom ASC';
|
|
$reponse = Requete_preparee_bdd($requete,
|
|
array(),
|
|
__FILE__, __LINE__);
|
|
while ($donnees = Recherche_ligne_bdd($reponse))
|
|
{
|
|
$flottes[$nb_flottes ++] = $donnees;
|
|
}
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
case "Confirmation_suppression_bateau":
|
|
{
|
|
// Nom du bateau à confirmer, relu en base (auparavant : le dernier nom chargé en session, qui pouvait être celui
|
|
// d'un AUTRE bateau) :
|
|
$reponse = Requete_preparee_bdd('SELECT Cle_primaire, Nom FROM TB_Bateaux WHERE Cle_primaire=? AND Validite=1',
|
|
array(array('i', $_SESSION['Bateaux']['bateau']['Cle_primaire'])), __FILE__, __LINE__);
|
|
$bateau_a_supprimer = Recherche_ligne_bdd($reponse);
|
|
Liberation_jeu_de_resultat_bdd($reponse);
|
|
if (!$bateau_a_supprimer) $_SESSION['Bateaux']['page'] = "Accueil";
|
|
break;
|
|
}
|
|
|
|
//****************************************************************************************************************************
|
|
default:
|
|
{
|
|
$_SESSION['Bateaux']['page'] = "Accueil";
|
|
break;
|
|
}
|
|
}
|
|
|
|
Fermeture_bdd(); // Déconnexion de MySQL
|
|
|
|
|
|
//************************************************************************************************************************************
|
|
// Affichage
|
|
//************************************************************************************************************************************
|
|
|
|
Entete_page_html();
|
|
|
|
//********************************************************************************************************************************
|
|
// Affichage principal :
|
|
echo '<div id="Bateaux">';
|
|
|
|
//=========================================================================================
|
|
// Entête :
|
|
Entete_page($message_erreur, $message_information);
|
|
|
|
switch ($_SESSION['Bateaux']['page'])
|
|
{
|
|
//************************************************************************************************************************
|
|
case "Accueil":
|
|
{
|
|
echo '<div id="accueil" class="page_principale">';
|
|
echo '<h1>Bateaux</h1>';
|
|
echo '<div class="table_wrapper">';
|
|
echo '<table class="bateau">
|
|
<thead>
|
|
<tr>';
|
|
echo '<th><a href="bateaux.php?action=tri_liste&colonne=Nom">Nom</a></th>';
|
|
echo '<th><a href="bateaux.php?action=tri_liste&colonne=Flotte">Flotte</a></th>';
|
|
// echo '<th><a href="bateaux.php?action=tri_liste&colonne=Carte">ShipBase</a></th>';
|
|
echo '<th><a href="bateaux.php?action=tri_liste&colonne=Adresse_mac">Adresse MAC</a></th>';
|
|
echo '<th><a href="bateaux.php?action=tri_liste&colonne=Version">Version</a></th>';
|
|
echo '<th><a href="bateaux.php?action=tri_liste&colonne=Date_maj">'.t("Dernières données reçues").'</a></th>';
|
|
// echo '<th>Commentaires</th>';
|
|
echo '</tr>
|
|
</thead>
|
|
<tbody>';
|
|
for ($i = 0; $i < $nb_bateaux; $i ++)
|
|
{
|
|
echo '<tr>
|
|
<td>
|
|
<a href="bateaux.php?action=details_bateau&id_bateau='.$bateaux[$i]['Cle_primaire'].'">
|
|
'.htmlspecialchars($bateaux[$i]['Nom']).'
|
|
</a>
|
|
</td>';
|
|
echo '<td>'.htmlspecialchars((string)$bateaux[$i]['nom_flotte']).'</td>';
|
|
// echo '<td>'.$bateaux[$i]['nom_carte'].'</td>';
|
|
echo '<td>'.htmlspecialchars((string)$bateaux[$i]['adresse_carte']).'</td>';
|
|
echo '<td>'.$bateaux[$i]['version'].'</td>';
|
|
echo '<td>'.$bateaux[$i]['Date_derniere_maj'].'</td>';
|
|
// echo '<td>'.$bateaux[$i]['Commentaires'].'</td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</tbody>
|
|
</table>';
|
|
echo '</div>';
|
|
|
|
echo '<div class="boutons_bas_de_page">';
|
|
echo '<a title="" href="administration.php?action=navigation&destination=Accueil">Retour au menu Administration</a>';
|
|
echo '<a title="" href="bateaux.php?action=creation_bateau">Créer un nouveau bateau</a>';
|
|
echo '</div>';
|
|
|
|
echo '</div>';
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//****************************************************************************************************************************
|
|
case "Details_bateau":
|
|
{
|
|
echo '<div id="details_bateau" class="page_principale">';
|
|
|
|
echo '<h1>Bateau <span class="gras">'.htmlspecialchars($bateau['Nom']).'</span></h1>';
|
|
|
|
echo '<div class="entete_bateau">';
|
|
if ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR || $acces_profil['Consultation_donnees'] == 2)
|
|
{
|
|
echo '<div class="bouton_gestion_acces">';
|
|
echo '<a title="" href="bateaux.php?action=type_details_bateau&type=gerer_les_acces"';
|
|
if ($_SESSION['Bateaux']['type_details_bateau'] == "gerer_les_acces") echo ' class="actif" '; echo '>
|
|
'.t("Gestion des accès").'
|
|
</a>';
|
|
echo '</div>';
|
|
}
|
|
|
|
//--------------------------------
|
|
// Bouton Live (synthèse §49) - formulaire POST avec jeton (un navigateur qui pré-charge les liens ne crée pas
|
|
// de tickets), ouvert dans un nouvel onglet :
|
|
echo '<div class="bouton_live">';
|
|
if ($live_possible)
|
|
{
|
|
echo '<form action="bateaux.php" method="post" target="_blank">'.Champ_jeton_csrf();
|
|
echo '<button type="submit" name="ouvrir_live" title="'.t("Ouvrir le bateau en temps réel (nouvel onglet)").'">'.t("Live").'</button>';
|
|
echo '</form>';
|
|
}
|
|
else echo '<span class="live_indisponible" title="'.t("Aucun module ShipBase joignable n'est affecté à ce bateau").'">'.t("Live indisponible").'</span>';
|
|
echo '</div>';
|
|
|
|
// Indicateur de fraîcheur (synthèse §44.6, §50) : date UTC transmise au navigateur, qui l'affiche dans le fuseau
|
|
// de l'utilisateur et colore selon l'ancienneté (seuil orange/rouge = 1,25 x la période d'archivage du bateau) :
|
|
echo '<div class="derniere_maj">'.t("Dernières données reçues").' : ';
|
|
if ($bateau['Date_derniere_maj_utc'] === null || $bateau['Date_derniere_maj_utc'] < "2020-01-02") echo '<span class="gras"><i>'.t("Jamais").'</i></span>';
|
|
else echo '<span class="gras fraicheur" data-date-utc="'.htmlspecialchars(str_replace(' ', 'T', $bateau['Date_derniere_maj_utc']).'Z').'" data-seuil-rouge="'
|
|
.(int)round(1.25 * ($consultation['periode_archivage'] ?? CONSULTATION_PERIODE_ARCHIVAGE_DEFAUT)).'">'.htmlspecialchars($bateau['Date_derniere_maj_utc']).' UTC</span>';
|
|
|
|
// Mise à jour à l'ouverture (étape 3d) : ce qui a été fait, et pourquoi (synthèse §50) :
|
|
$minutes = function($secondes) { return max(1, (int)ceil($secondes / 60)); };
|
|
switch ($consultation['demande']['statut'] ?? '')
|
|
{
|
|
case 'demandee':
|
|
echo '<span class="info_mise_a_jour en_cours" id="info_mise_a_jour">'.t("Mise à jour demandée au bateau...").'</span>';
|
|
break;
|
|
case 'donnees_recentes':
|
|
echo '<span class="info_mise_a_jour">'.str_replace("[[M]]", $minutes($consultation['demande']['attente']),
|
|
t("Données récentes : pas de nouvelle demande au bateau avant [[M]] min.")).'</span>';
|
|
break;
|
|
case 'deja_demandee':
|
|
echo '<span class="info_mise_a_jour">'.str_replace("[[M]]", $minutes($consultation['demande']['attente']),
|
|
t("Mise à jour déjà demandée récemment : nouvelle demande possible dans [[M]] min.")).'</span>';
|
|
break;
|
|
}
|
|
echo '</div>';
|
|
echo '</div>';
|
|
//--------------------------------
|
|
// Boutons de haut de page (les anciens onglets Carte/Batteries/Circuits/Réservoirs/Capteurs/Système, liés au
|
|
// modèle CM4 centralisé, ont été retirés - la consultation des données sera refaite sur la base de la génération
|
|
// active du bateau, lot 3 - synthèse §44.10) :
|
|
echo '<div class="boutons_haut_de_page">';
|
|
echo '<div>';
|
|
echo '<a title="" href="bateaux.php?action=type_details_bateau&type=resume"';
|
|
if ($_SESSION['Bateaux']['type_details_bateau'] == "resume") echo ' class="actif" '; echo '>'.t("Données du bateau").'</a>';
|
|
echo '</div>';
|
|
echo '<div>';
|
|
echo '<a title="" href="bateaux.php?action=type_details_bateau&type=alertes"';
|
|
if ($_SESSION['Bateaux']['type_details_bateau'] == "alertes") echo ' class="actif" '; echo '>'.t("Alertes").'</a>';
|
|
echo '</div>';
|
|
echo '</div>';
|
|
|
|
echo '<div class="corps_details_bateau">';
|
|
switch($_SESSION['Bateaux']['type_details_bateau'])
|
|
{
|
|
//===========================================================
|
|
// Alertes (synthèse §51, étape E)
|
|
case "alertes":
|
|
{
|
|
$p = $vue_alertes['preferences'];
|
|
$tel = $vue_alertes['telephone'];
|
|
echo '<div class="alertes">';
|
|
|
|
// --- Mes alertes pour ce bateau ---
|
|
echo '<section class="bloc_alertes">';
|
|
echo '<h2>'.t("Mes alertes pour ce bateau").'</h2>';
|
|
if (!$p['a_un_acces']) echo '<p>'.t("Vous n'avez pas d'accès à ce bateau : vous ne recevez pas ses alertes.").'</p>';
|
|
else
|
|
{
|
|
echo '<p class="explication">'.t("Vous recevez les alarmes prévues par e-mail ou par SMS dans la configuration du bateau, sur les moyens cochés ci-dessous, ainsi que la perte et le retour de sa connexion.").'</p>';
|
|
echo '<form action="bateaux.php" method="post" class="preferences_alertes">'.Champ_jeton_csrf();
|
|
echo '<label><input type="checkbox" name="alertes_email" value="1"'.($p['email'] ? ' checked' : '').'> '.t("Par e-mail").'</label>';
|
|
echo '<label><input type="checkbox" name="alertes_sms" value="1"'.($p['sms'] ? ' checked' : '').'> '.t("Par SMS").'</label>';
|
|
echo '<button type="submit" name="enregistrer_preferences_alertes">'.t("Enregistrer").'</button>';
|
|
echo '</form>';
|
|
if ($p['sms'] && !$tel['verifie']) echo '<p class="avertissement">'.t("SMS en attente : votre numéro doit être vérifié pour que les SMS partent.").'</p>';
|
|
}
|
|
echo '</section>';
|
|
|
|
// --- Mon numéro de téléphone (dès que les SMS sont cochés, ou qu'un numéro existe) ---
|
|
if ($p['a_un_acces'] && ($p['sms'] || $tel['telephone'] != ''))
|
|
{
|
|
echo '<section class="bloc_alertes">';
|
|
echo '<h2>'.t("Mon numéro pour les SMS").'</h2>';
|
|
echo '<form action="bateaux.php" method="post" class="telephone_alertes">'.Champ_jeton_csrf();
|
|
echo '<input type="tel" name="telephone" value="'.htmlspecialchars($tel['telephone']).'" placeholder="+33 6 12 34 56 78" maxlength="24">';
|
|
echo '<button type="submit" name="enregistrer_telephone">'.t("Enregistrer").'</button>';
|
|
echo $tel['verifie'] ? '<span class="statut statut_actif">'.t("Vérifié").'</span>' : '<span class="statut statut_suspendu">'.t("Non vérifié").'</span>';
|
|
echo '</form>';
|
|
if (!$tel['verifie'] && $tel['telephone'] != '')
|
|
{
|
|
echo '<form action="bateaux.php" method="post" class="code_telephone">'.Champ_jeton_csrf();
|
|
echo '<button type="submit" name="envoyer_code_telephone">'.($tel['code_en_cours'] ? t("Renvoyer un code") : t("Recevoir un code par SMS")).'</button>';
|
|
echo '</form>';
|
|
if ($tel['code_en_cours'])
|
|
{
|
|
echo '<form action="bateaux.php" method="post" class="code_telephone">'.Champ_jeton_csrf();
|
|
echo '<input type="text" name="code_telephone" inputmode="numeric" autocomplete="one-time-code" maxlength="6" placeholder="123456">';
|
|
echo '<button type="submit" name="verifier_code_telephone">'.t("Valider le code").'</button>';
|
|
echo '</form>';
|
|
}
|
|
}
|
|
echo '<p class="explication">'.t("Ce numéro est celui de votre profil : le modifier ici ou dans votre profil demande une nouvelle vérification.").'</p>';
|
|
echo '</section>';
|
|
}
|
|
|
|
// --- Alertes suspendues ---
|
|
if (count($vue_alertes['suspensions']) > 0)
|
|
{
|
|
echo '<section class="bloc_alertes">';
|
|
echo '<h2>'.t("Alertes suspendues").'</h2>';
|
|
echo '<p class="explication">'.t("Une alarme qui se déclenche et s'arrête trop souvent suspend ses alertes (e-mail et SMS) pour tous les destinataires : sa configuration est à vérifier. Le son, la LED et les messages à bord ne sont pas concernés.").'</p>';
|
|
echo '<table>';
|
|
foreach ($vue_alertes['suspensions'] as $suspension)
|
|
{
|
|
echo '<tr><td class="date_alerte"><span data-date-utc="'.htmlspecialchars(str_replace(' ', 'T', $suspension['Date_suspension']).'Z').'"></span></td>';
|
|
echo '<td class="nom_alerte">'.htmlspecialchars((string)$suspension['Nom_event']).'</td><td>'.htmlspecialchars($suspension['Motif']).'</td><td>';
|
|
if ($vue_alertes['peut_reactiver'])
|
|
{
|
|
echo '<form action="bateaux.php" method="post">'.Champ_jeton_csrf()
|
|
.'<button type="submit" name="reactiver_suspension" value="'.(int)$suspension['ID'].'">'.t("Réactiver").'</button></form>';
|
|
}
|
|
echo '</td></tr>';
|
|
}
|
|
echo '</table>';
|
|
echo '</section>';
|
|
}
|
|
|
|
// --- Alertes envoyées ---
|
|
echo '<section class="bloc_alertes">';
|
|
echo '<h2>'.t("Dernières alertes").'</h2>';
|
|
if (count($vue_alertes['alertes']) == 0) echo '<p class="explication">'.t("Aucune alerte pour ce bateau.").'</p>';
|
|
else
|
|
{
|
|
$libelles_types = array('declenchement' => t("Alarme"), 'fin' => t("Fin d'alarme"), 'perte_connexion' => t("Connexion perdue"),
|
|
'retour_connexion' => t("Connexion rétablie"), 'suspension' => t("Alertes suspendues"));
|
|
$libelles_statuts = array('a_envoyer' => t("en cours d'envoi"), 'envoyee' => t("envoyée"), 'pendant_coupure' => t("pendant une coupure (comptée)"),
|
|
'suspendue' => t("non envoyée (suspendue)"), 'sans_destinataire' => t("sans destinataire"));
|
|
echo '<table>';
|
|
echo '<tr><th>'.t("Date").'</th><th>'.t("Type").'</th><th>'.t("Alarme").'</th><th>'.t("Envoi").'</th></tr>';
|
|
foreach ($vue_alertes['alertes'] as $alerte)
|
|
{
|
|
$envoi = $libelles_statuts[$alerte['Statut']] ?? $alerte['Statut'];
|
|
if ($alerte['Statut'] == 'envoyee') $envoi .= ' ('.(int)$alerte['Envoyes'].((int)$alerte['Echecs'] > 0 ? ', '.(int)$alerte['Echecs'].' '.t("échec(s)") : '').')';
|
|
echo '<tr class="alerte_'.htmlspecialchars($alerte['Type']).'">';
|
|
echo '<td class="date_alerte"><span data-date-utc="'.htmlspecialchars(str_replace(' ', 'T', $alerte['Date_evenement']).'Z').'"></span></td>';
|
|
echo '<td>'.($libelles_types[$alerte['Type']] ?? htmlspecialchars($alerte['Type'])).'</td>';
|
|
echo '<td class="nom_alerte">'.htmlspecialchars((string)$alerte['Nom_event']).'</td>';
|
|
echo '<td>'.$envoi.'</td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</table>';
|
|
}
|
|
echo '</section>';
|
|
|
|
echo '</div>';
|
|
break;
|
|
}
|
|
|
|
//===========================================================
|
|
case "gerer_les_acces":
|
|
{
|
|
// Convention (synthèse §48) : les textes traduits (t()) sont affichés tels quels - ils sont écrits dans les
|
|
// fichiers de langue, pas saisis par les utilisateurs ; seules les DONNÉES (base, saisie) sont échappées.
|
|
|
|
echo '<div class="gestion_acces">';
|
|
|
|
//--------------------------------------------
|
|
// Formulaire d'invitation nominative (synthèse §47) :
|
|
if ($peut_inviter)
|
|
{
|
|
echo '<div class="generer_code">';
|
|
echo ' <h3>'.t("Inviter une personne sur ce bateau").'</h3>';
|
|
echo ' <form action="bateaux.php" method="POST">'; echo Champ_jeton_csrf();
|
|
echo ' <input type="email" name="email_invite" required maxlength="254" placeholder="'.t("Adresse e-mail du destinataire").'">';
|
|
echo ' <input type="text" name="prenom_invite" maxlength="50" placeholder="'.t("Prénom (facultatif)").'">';
|
|
echo ' <input type="text" name="nom_invite" maxlength="50" placeholder="'.t("Nom (facultatif)").'">';
|
|
echo ' <select name="type_acces" required>';
|
|
echo ' <option value="" disabled selected> -- '.t("Type d'accès").' -- </option>';
|
|
for ($i = 0; $i < $type_acces_nb; $i++) echo '<option value="'.(int)$type_acces[$i]['Cle_primaire'].'">'.htmlspecialchars((string)$type_acces[$i]['Nom']).'</option>';
|
|
echo ' </select>';
|
|
echo ' <label class="fin_acces"><span>'.t("Fin de l'accès (facultatif)").'</span><input type="date" name="fin_acces"></label>';
|
|
echo ' <button type="submit" name="creer_invitation">'.t("Inviter").'</button>';
|
|
echo ' </form>';
|
|
echo '</div>';
|
|
}
|
|
|
|
//--------------------------------------------
|
|
// Liste des accès et des invitations en attente :
|
|
echo '<div class="users">';
|
|
echo ' <table>';
|
|
echo ' <tr>';
|
|
echo ' <th>'.t("Statut").'</th>';
|
|
echo ' <th>'.t("Destinataire").'</th>';
|
|
echo ' <th>'.t("Créé par").'</th>';
|
|
echo ' <th>'.t("Type de profil").'</th>';
|
|
echo ' <th>'.t("Fin de validité").'</th>';
|
|
echo ' <th></th>';
|
|
echo ' </tr>';
|
|
if ($utilisateur_acces_nb == 0) echo '<tr><td colspan="6">'.t("Aucun accès ni invitation en cours.").'</td></tr>';
|
|
for ($i = 0; $i < $utilisateur_acces_nb; $i++)
|
|
{
|
|
$acces = $utilisateur_acces[$i];
|
|
|
|
// Statut affiché :
|
|
switch ($acces['Statut'])
|
|
{
|
|
case 'actif': $statut = t("Actif"); $classe_statut = 'actif'; break;
|
|
case 'suspendu': $statut = t("Suspendu"); $classe_statut = 'suspendu'; break;
|
|
default:
|
|
if ($acces['Expiree']) { $statut = t("Invitation expirée"); $classe_statut = 'expiree'; }
|
|
else
|
|
{
|
|
$statut = str_replace("[[Date]]", date_format(date_create($acces['Expiration_invitation']), 'd/m/Y'), t("Invitation en attente (jusqu'au [[Date]])"));
|
|
$classe_statut = 'en_attente';
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Actions permises sur cette ligne :
|
|
$peut_devalider = Peut_devalider_acces($acces_profil, $est_administrateur, $acces['ID_Type_acces_bateau_shipmember']);
|
|
$actions = array();
|
|
if ($acces['Statut'] == 'actif' && $peut_devalider) $actions['suspendre'] = t("Suspendre");
|
|
if ($acces['Statut'] == 'suspendu' && $peut_devalider) $actions['reactiver'] = t("Réactiver");
|
|
if ($acces['Statut'] == 'en_attente' && !$acces['Expiree'] && $peut_inviter) $actions['pdf'] = t("PDF");
|
|
if ($peut_devalider || ($acces['Statut'] == 'en_attente' && $peut_inviter)) $actions['revoquer'] = t("Révoquer");
|
|
|
|
echo ' <tr>';
|
|
echo ' <td><span class="statut statut_'.$classe_statut.'">'.$statut.'</span></td>';
|
|
echo ' <td>'.htmlspecialchars((string)$acces['Destinataire']).'</td>';
|
|
echo ' <td>'.htmlspecialchars((string)$acces['Cree_par_nom']).'</td>';
|
|
echo ' <td>'.htmlspecialchars((string)$acces['Niveau_acces_nom']).'</td>';
|
|
echo ' <td>'.((substr($acces['Fin_validite'], 0, 10) == "9999-12-31") ? t("Aucune") : date_format(date_create($acces['Fin_validite']), 'd/m/Y')).'</td>';
|
|
echo ' <td><div class="actions_acces">';
|
|
if (count($actions) > 0)
|
|
{
|
|
echo '<form action="bateaux.php" method="POST">'; echo Champ_jeton_csrf();
|
|
echo '<input type="hidden" name="ID" value="'.(int)$acces['ID'].'">';
|
|
foreach ($actions as $code_action => $libelle)
|
|
{
|
|
echo '<button type="submit" name="action_acces" value="'.$code_action.'" class="action_'.$code_action.'"';
|
|
if ($code_action == 'revoquer') echo ' onclick="return window.confirm(\''.addslashes(t("Révoquer définitivement cet accès ?")).'\');"';
|
|
echo '>'.$libelle.'</button> ';
|
|
}
|
|
echo '</form>';
|
|
}
|
|
if ($acces_profil['Modification_acces'] == 1)
|
|
{
|
|
echo '<span class="modifier_acces" title="'.t("Modifier le type ou la fin de l'accès").'" onclick="popUpEditAccess('.htmlspecialchars((string)json_encode($acces)).')"></span>';
|
|
}
|
|
echo '</div> </td>';
|
|
echo ' </tr>';
|
|
}
|
|
echo ' </table>';
|
|
echo '</div>';
|
|
|
|
echo '</div>'; // Fin de class="gestion_acces"
|
|
|
|
//--------------------------------------------
|
|
// Popup de modification (administrateurs) : type d'accès et fin de validité.
|
|
// (La suppression se fait désormais par le bouton "Révoquer" de chaque ligne.)
|
|
if ($acces_profil['Modification_acces'] == 1)
|
|
{
|
|
echo '<div id="popUpAcces" style="display: none;">';
|
|
echo ' <div id="popUpAccesModal">';
|
|
echo ' <div id="top_part">';
|
|
echo ' <p>'.t("Modification des droits").'</p>';
|
|
echo ' <p id="close" onclick="closeEditAccess();"> </p>';
|
|
echo ' </div>';
|
|
echo ' <form action="bateaux.php" method="POST">'; echo Champ_jeton_csrf();
|
|
echo ' <label for="shipmemberForm" id="ShipMember">';
|
|
echo ' <input id="shipmemberForm" type="text" value="" readonly>';
|
|
echo ' <span>'.t("Shipmember").'</span>';
|
|
echo ' </label>';
|
|
echo ' <label for="cree_parForm" id="Cree_par">';
|
|
echo ' <input id="cree_parForm" type="text" value="" readonly>';
|
|
echo ' <span>'.t("Créé par").'</span>';
|
|
echo ' </label>';
|
|
echo ' <label for="type_profilForm" id="Type_profil">';
|
|
echo ' <select name="type_profil" id="type_profilForm" required>';
|
|
echo ' <option value="" disabled selected> -- '.t("Type d'accès").' -- </option>';
|
|
for ($i = 0; $i < $type_acces_nb; $i++) echo '<option value="'.(int)$type_acces[$i]['Cle_primaire'].'">'.htmlspecialchars((string)$type_acces[$i]['Nom']).'</option>';
|
|
echo ' </select>';
|
|
echo ' <span>'.t("Type de profil").'</span>';
|
|
echo ' </label>';
|
|
echo ' <label for="fin_validiteForm" id="Fin_validite">';
|
|
echo ' <input id="fin_validiteForm" type="date" value="" name="fin_validite" required>';
|
|
echo ' <span>'.t("Fin de validite").'</span>';
|
|
echo ' </label>';
|
|
echo ' <input type="hidden" id="idForm" name="ID" value="">';
|
|
echo ' <button type="submit" name="modifierAcces">'.t("Mettre à jour").'</button>';
|
|
echo ' </form>';
|
|
echo ' </div>';
|
|
echo '</div>';
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//===========================================================
|
|
// 'resume' (et toute ancienne valeur de session) :
|
|
default:
|
|
{
|
|
if (!$consultation['autorisee']) { echo '<p>'.t("Votre type d'accès ne permet pas de consulter les données de ce bateau.").'</p>'; break; }
|
|
if ($consultation['generation'] === null) { echo '<p>'.t("Aucune donnée n'a encore été reçue de ce bateau.").'</p>'; break; }
|
|
if ($consultation['erreur'] !== null) { echo '<p>'.htmlspecialchars($consultation['erreur']).'</p>'; break; }
|
|
|
|
echo '<div class="consultation">';
|
|
|
|
// Rafraîchissement à l'arrivée d'une nouvelle archive (synthèse §50) - génération active seulement :
|
|
if ($consultation['generation']['Date_fin'] === null)
|
|
{
|
|
echo '<span id="surveillance_archive" hidden data-id-bateau="'.(int)$bateau['Cle_primaire'].'" data-date="'.htmlspecialchars((string)$bateau['Date_derniere_maj_utc']).'"'
|
|
.(!empty($consultation['demande_archive']) ? ' data-demande="1"' : '').'></span>';
|
|
}
|
|
|
|
//--------------------------------
|
|
// Génération consultée, et choix d'une génération antérieure :
|
|
$format_date = function($date) { return ($date === null) ? '' : date_format(date_create($date), 'd/m/Y'); };
|
|
echo '<form class="choix_generation" action="bateaux.php" method="get">';
|
|
echo '<input type="hidden" name="action" value="choix_generation">';
|
|
echo '<label for="generation">'.t("Données").' : </label>';
|
|
echo '<select name="generation" id="generation" onchange="this.form.submit()">';
|
|
foreach ($consultation['generations'] as $generation)
|
|
{
|
|
$libelle = str_replace("[[N]]", (int)$generation['Numero_generation'], t("Génération [[N]]"));
|
|
if ($generation['Date_fin'] === null) $libelle .= ' - '.str_replace("[[Date]]", $format_date($generation['Date_debut']), t("en cours, depuis le [[Date]]"));
|
|
else $libelle .= ' - '.str_replace(array("[[Debut]]", "[[Fin]]"), array($format_date($generation['Date_debut']), $format_date($generation['Date_fin'])), t("du [[Debut]] au [[Fin]]"));
|
|
if ($generation['Version_soft'] !== null) $libelle .= ' (v'.$generation['Version_soft'].')';
|
|
echo '<option value="'.(int)$generation['Cle_primaire'].'"'.(((int)$generation['Cle_primaire'] == (int)$consultation['generation']['Cle_primaire']) ? ' selected' : '').'>'.htmlspecialchars($libelle).'</option>';
|
|
}
|
|
echo '</select>';
|
|
echo '<noscript><button type="submit">'.t("Afficher").'</button></noscript>';
|
|
echo '</form>';
|
|
if ($consultation['generation']['Date_fin'] !== null)
|
|
{
|
|
echo '<p class="generation_ancienne">'.t("Vous consultez une génération antérieure : les valeurs ci-dessous sont celles de sa dernière archive, pas l'état actuel du bateau.").'</p>';
|
|
}
|
|
|
|
//--------------------------------
|
|
// Dernières alarmes (synthèse §50) : déclenchements, du plus récent au plus ancien, dates dans le fuseau du
|
|
// navigateur. (Pas d'état "alarme en cours" : vieux de jusqu'à une période d'archivage, il ne serait ni
|
|
// fiable ni exploitable.)
|
|
echo '<section class="dernieres_alarmes">';
|
|
echo '<h2>'.t("Dernières alarmes").'</h2>';
|
|
if (count($consultation['alarmes'] ?? array()) == 0) echo '<p class="aucune_alarme">'.t("Aucune alarme enregistrée.").'</p>';
|
|
else
|
|
{
|
|
$date_affichee = function($iso) { return '<span data-date-utc="'.htmlspecialchars($iso).'">'.htmlspecialchars($iso).'</span>'; };
|
|
echo '<table>';
|
|
echo '<tr><th>'.t("Début").'</th><th>'.t("Fin").'</th><th>'.t("Durée").'</th><th>'.t("Alarme").'</th><th></th></tr>';
|
|
foreach ($consultation['alarmes'] as $alarme)
|
|
{
|
|
switch ($alarme['statut'])
|
|
{
|
|
case 'termine': $fin = $date_affichee($alarme['fin_utc']); break;
|
|
case 'en_cours': $fin = '<span class="alarme_en_cours">'.t("en cours à la dernière archive").'</span>'; break;
|
|
case 'fin_non_enregistree': $fin = '<i>'.t("fin non enregistrée").'</i>'; break;
|
|
case 'debut_anterieur': $fin = $date_affichee($alarme['fin_utc']); break;
|
|
default: $fin = '<i>'.t("ponctuel").'</i>'; break;
|
|
}
|
|
echo '<tr class="alarme_'.$alarme['statut'].'">';
|
|
echo '<td class="date_alarme">'.(($alarme['debut_utc'] !== null) ? $date_affichee($alarme['debut_utc']) : '<i>'.t("début antérieur").'</i>').'</td>';
|
|
echo '<td class="date_alarme">'.$fin.'</td>';
|
|
echo '<td class="duree_alarme">'.(($alarme['duree'] !== null) ? htmlspecialchars(Duree_lisible($alarme['duree'])) : '').'</td>';
|
|
echo '<td class="nom_alarme">'.htmlspecialchars($alarme['nom']).'</td>';
|
|
echo '<td class="message_alarme">'.htmlspecialchars($alarme['message']).'</td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</table>';
|
|
}
|
|
echo '</section>';
|
|
|
|
//--------------------------------
|
|
// Les quatre domaines, par famille, avec leurs dernières valeurs :
|
|
foreach ($consultation['domaines'] as $domaine => $donnees_domaine)
|
|
{
|
|
if (count($donnees_domaine['familles']) == 0) continue;
|
|
echo '<section class="domaine domaine_'.$domaine.'">';
|
|
echo '<h2>'.htmlspecialchars($donnees_domaine['titre']).'</h2>';
|
|
foreach ($donnees_domaine['familles'] as $famille)
|
|
{
|
|
echo '<div class="famille">';
|
|
echo '<h3>'.htmlspecialchars($famille['nom']).'</h3>';
|
|
echo '<table>';
|
|
foreach ($famille['elements'] as $element)
|
|
{
|
|
// Élément avec courbe historique : ligne cliquable (synthèse §50, étape 3c)
|
|
$classes = array();
|
|
if ($element['defaut'] !== null) $classes[] = 'en_defaut';
|
|
if ($element['courbe']) $classes[] = 'avec_courbe';
|
|
echo '<tr'.((count($classes) > 0) ? ' class="'.implode(' ', $classes).'"' : '')
|
|
.($element['courbe'] ? ' data-type="'.$domaine.'" data-element="'.(int)$element['id'].'" data-nom="'.htmlspecialchars($element['nom']).'" title="'.t("Afficher la courbe").'"' : '').'>';
|
|
echo '<td class="nom_element">'.htmlspecialchars($element['nom']).'</td>';
|
|
echo '<td class="valeurs_element">'.htmlspecialchars(implode(' ', $element['valeurs'])).'</td>';
|
|
echo '<td class="defaut_element">'.(($element['defaut'] !== null) ? htmlspecialchars($element['defaut']) : '').'</td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</table>';
|
|
echo '</div>';
|
|
}
|
|
echo '</section>';
|
|
}
|
|
//--------------------------------
|
|
// Panneau de courbe historique (synthèse §50, étape 3c), ouvert au clic sur un élément :
|
|
echo '<div id="panneau_courbe" hidden data-bateau="'.(int)$bateau['Cle_primaire'].'" data-generation="'.(int)$consultation['generation']['Cle_primaire'].'">';
|
|
echo '<div class="fenetre_courbe" role="dialog" aria-modal="true" aria-labelledby="titre_courbe">';
|
|
echo '<div class="entete_courbe"><h3 id="titre_courbe"></h3><button type="button" id="fermer_courbe" title="'.t("Fermer").'">×</button></div>';
|
|
echo '<div class="choix_courbe" id="choix_variable"></div>';
|
|
echo '<div class="choix_courbe" id="choix_periode"></div>';
|
|
echo '<div class="zone_courbe"><canvas id="canvas_courbe"></canvas><p id="message_courbe" hidden></p></div>';
|
|
echo '<p id="stats_courbe"></p>';
|
|
echo '</div>';
|
|
echo '</div>';
|
|
echo '<script src="/vendor/chartjs/chart.umd.min.js"></script>';
|
|
|
|
echo '</div>'; // Fin de class="consultation"
|
|
break;
|
|
}
|
|
}
|
|
echo '</div>'; // Fin de class="corps_details_bateau"
|
|
|
|
|
|
|
|
//--------------------------------
|
|
echo '<div class="boutons_bas_de_page">';
|
|
if ($_SESSION['Bateaux']['origine'] == "shipmember") echo '<a title="" href="shipmembers.php?action=navigation&destination=Accueil">Retour au menu ShipMember</a>';
|
|
else
|
|
{
|
|
echo '<a title="" href="administration.php?action=navigation&destination=Accueil">Retour au menu Administration</a>';
|
|
echo '<a title="" href="bateaux.php?action=navigation&destination=Accueil">Revenir à la liste des bateaux</a>';
|
|
echo '<a href="bateaux.php?action=modification_bateau&id_bateau='.$bateau['Cle_primaire'].'">Modifier</a>';
|
|
}
|
|
echo '</div>';
|
|
|
|
|
|
//-------------------------------
|
|
// Help
|
|
echo '<div class="help_note" id="help_note" style="display:none;">';
|
|
echo '<p>';
|
|
switch($_SESSION['Bateaux']['type_details_bateau'])
|
|
{
|
|
case 'gerer_les_acces':
|
|
{
|
|
echo t("Aide accès");
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
echo '</p>';
|
|
echo '</div>';
|
|
echo '<div class="help help_open" id="help" onclick="toggle_help();">';
|
|
echo '</div>';
|
|
|
|
|
|
echo '</div>';
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//************************************************************************************************************************
|
|
case "Creation_bateau":
|
|
case "Modification_bateau":
|
|
{
|
|
echo '<div id="creation_bateau" class="page_principale">';
|
|
|
|
echo '<h1>Bateaux</h1>';
|
|
|
|
echo '<form action="bateaux.php" method="post">'; echo Champ_jeton_csrf();
|
|
|
|
|
|
|
|
if ($_SESSION['Bateaux']['page'] == "Creation_bateau") echo '<h2>Nouveau bateau</h2>';
|
|
else echo '<h2>Modification du bateau <span class="gras">'.htmlspecialchars($_SESSION['Bateaux']['bateau']['Nom']).'</span></h2>';
|
|
|
|
//----------------------------------------
|
|
// Nom :
|
|
echo '<div class="champs">
|
|
<div class="champs_titre">
|
|
Nom
|
|
</div>
|
|
<div class="champs_valeur">
|
|
<input name="Nom" placeholder="Par exemple : Et Si... 2" value="'.htmlspecialchars($_SESSION['Bateaux']['bateau']['Nom']).'" maxlength="50" >
|
|
</div>
|
|
</div>';
|
|
|
|
//----------------------------------------
|
|
// Date de mise en service :
|
|
echo '<div class="champs">
|
|
<div class="champs_titre">
|
|
Date de mise en service
|
|
</div>
|
|
<div class="champs_valeur">
|
|
<input name="Date_mes" placeholder="Format Y-m-d" value="'.htmlspecialchars($_SESSION['Bateaux']['bateau']['Date_mes']).'" >
|
|
</div>
|
|
</div>';
|
|
|
|
//----------------------------------------
|
|
// Flotte :
|
|
echo '<div class="champs">
|
|
<div class="champs_titre">
|
|
Flotte à laquelle le bateau appartient
|
|
</div>
|
|
<div class="champs_valeur">
|
|
<select name="ID_Flotte">';
|
|
for ($i=0; $i<$nb_flottes; $i++)
|
|
{
|
|
echo ' <option value="'.$flottes[$i]['Cle_primaire'].'" '; if ($flottes[$i]['Cle_primaire'] == $_SESSION['Bateaux']['bateau']['ID_Flotte']) echo 'selected'; echo '>
|
|
'.htmlspecialchars($flottes[$i]['Nom']).'
|
|
</option>';
|
|
}
|
|
|
|
echo '</select>
|
|
</div>
|
|
</div>';
|
|
|
|
//----------------------------------------
|
|
// Commentaires :
|
|
echo '<div class="champs">
|
|
<div class="champs_titre">
|
|
Commentaires
|
|
</div>
|
|
<div class="champs_valeur">
|
|
<textarea cols="" rows="" name="Commentaires" placeholder="Vos commentaires sur la bateau" autocomplete="off">'.htmlspecialchars($_SESSION['Bateaux']['bateau']['Commentaires']).'</textarea>
|
|
</div>
|
|
</div>';
|
|
|
|
//----------------------------------------
|
|
// Boutons de bas de page :
|
|
echo '<div class="boutons_bas_de_page">';
|
|
if ($_SESSION['Bateaux']['page'] == "Creation_bateau") // Création :
|
|
{
|
|
echo '<a title="" href="bateaux.php?action=navigation&destination=Accueil">Annuler</a>';
|
|
echo '<button type="submit" name="validation_creation_bateau" title="Valider la création de la bateau">Créer la bateau</button>';
|
|
}
|
|
else // Modification :
|
|
{
|
|
echo '<a title="" href="bateaux.php?action=navigation&destination=Accueil">Annuler</a>';
|
|
echo '<button type="submit" name="validation_modification_bateau" title="Valider les modification de la bateau">Enregistrer les modifications</button>';
|
|
echo '<a title="" href="bateaux.php?action=demande_suppression_bateau&id_bateau='.(int)$_SESSION['Bateaux']['bateau']['Cle_primaire'].'">Supprimer le bateau</a>';
|
|
}
|
|
echo '</div>';
|
|
|
|
echo '</form>';
|
|
|
|
echo '</div>';
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//****************************************************************************************************************************
|
|
case "Confirmation_suppression_bateau":
|
|
{
|
|
echo '<div id="confirmation_suppression_bateau" class="page_principale">';
|
|
|
|
echo '<h1>Bateaux</h1>';
|
|
|
|
echo '<div class="message_confirmation">';
|
|
echo '<p>Confirmez-vous la suppression du bateau <span class="gras">'.htmlspecialchars($bateau_a_supprimer['Nom']).'</span> ?</p>';
|
|
echo '<p>Si oui, son entrée dans la base de données sera dévalidée.</p>';
|
|
echo '</div>';
|
|
|
|
// Confirmation envoyée en POST avec le jeton de session (auparavant : un simple lien GET suffisait) :
|
|
echo '<form action="bateaux.php" method="post" class="boutons_bas_de_page">';
|
|
echo Champ_jeton_csrf();
|
|
echo '<a title="" href="bateaux.php?action=navigation&destination=Accueil">Annuler</a>';
|
|
echo '<button type="submit" name="confirmation_suppression_bateau" value="'.(int)$bateau_a_supprimer['Cle_primaire'].'">Confirmer la suppression</button>';
|
|
echo '</form>';
|
|
|
|
echo '</div>';
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
echo '</div>';
|
|
|
|
Pied_de_page();
|
|
?>
|
|
|
|
|
|
<script>
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Dates UTC (attribut data-date-utc) affichées dans le fuseau du navigateur ; indicateur de fraîcheur coloré si
|
|
// l'élément porte un seuil (data-seuil-rouge, en secondes) : vert < 5 min, orange jusqu'au seuil, rouge au-delà.
|
|
// Mis à jour toutes les 30 s : la couleur évolue sans recharger la page (synthèse §44.6, §50).
|
|
//------------------------------------------------------------------------------------------------------------
|
|
const TEXTES_FRAICHEUR = <?php echo json_encode(array('instant' => t("à l'instant"), 'minutes' => t("il y a [[M]] min"), 'heures' => t("il y a [[H]] h [[M]] min"), 'jours' => t("il y a [[J]] j")), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>;
|
|
function Afficher_dates_et_fraicheur()
|
|
{
|
|
document.querySelectorAll('[data-date-utc]').forEach(function (element) {
|
|
const date = new Date(element.dataset.dateUtc);
|
|
if (isNaN(date)) return;
|
|
let texte = date.toLocaleString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
|
if (element.dataset.seuilRouge)
|
|
{
|
|
const age = Math.max(0, (Date.now() - date.getTime()) / 1000);
|
|
let relatif;
|
|
if (age < 60) relatif = TEXTES_FRAICHEUR.instant;
|
|
else if (age < 3600) relatif = TEXTES_FRAICHEUR.minutes.replace('[[M]]', Math.floor(age / 60));
|
|
else if (age < 86400) relatif = TEXTES_FRAICHEUR.heures.replace('[[H]]', Math.floor(age / 3600)).replace('[[M]]', Math.floor((age % 3600) / 60));
|
|
else relatif = TEXTES_FRAICHEUR.jours.replace('[[J]]', Math.floor(age / 86400));
|
|
texte += ' (' + relatif + ')';
|
|
element.classList.remove('fraicheur_verte', 'fraicheur_orange', 'fraicheur_rouge');
|
|
element.classList.add(age < 300 ? 'fraicheur_verte' : (age <= Number(element.dataset.seuilRouge) ? 'fraicheur_orange' : 'fraicheur_rouge'));
|
|
}
|
|
element.textContent = texte;
|
|
});
|
|
}
|
|
Afficher_dates_et_fraicheur();
|
|
setInterval(Afficher_dates_et_fraicheur, 30000);
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Courbes historiques (synthèse §50, étape 3c). Un clic sur un élément ouvre sa courbe (données : bateau_mesures.php).
|
|
// Courbe moyenne entourée d'une bande min-max (un pic bref reste visible sur une longue période). L'état de la
|
|
// courbe ouverte est mémorisé dans l'adresse (#courbe=type.element.variable.periode) : après un rechargement de la
|
|
// page (nouvelle archive), elle se rouvre d'elle-même.
|
|
//------------------------------------------------------------------------------------------------------------
|
|
const TEXTES_COURBE = <?php echo json_encode(array(
|
|
'periodes' => array('24h' => t("24 h"), '7j' => t("7 jours"), '30j' => t("30 jours"), '3m' => t("3 mois"), '1a' => t("1 an")),
|
|
'variables' => array(
|
|
'circuits' => array('intensite' => t("Intensité"), 'tension' => t("Tension"), 'consigne' => t("Consigne"), 'frequence' => t("Fréquence")),
|
|
'batteries' => array('soc' => t("SOC"), 'tension' => t("Tension"), 'temperature' => t("Température"), 'courant_charge' => t("Courant de charge"),
|
|
'courant_decharge' => t("Courant de décharge"), 'courant_differentiel' => t("Courant différentiel"), 'soh' => t("SOH"), 'autonomie' => t("Autonomie"))),
|
|
'moyenne' => t("Moyenne"), 'bande' => t("Min - max"), 'min' => t("Min"), 'max' => t("Max"),
|
|
'aucune' => t("Aucune mesure sur cette période."), 'erreur' => t("Courbe indisponible."), 'chargement' => t("Chargement..."),
|
|
'bibliotheque' => t("Bibliothèque de graphiques introuvable (vendor/chartjs).")),
|
|
JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>;
|
|
const VARIABLE_PAR_DEFAUT = { circuits: 'intensite', batteries: 'soc' };
|
|
|
|
(function () {
|
|
const panneau = document.getElementById('panneau_courbe');
|
|
if (!panneau) return;
|
|
let etat = null, graphique = null, requete = 0;
|
|
|
|
const nombre = (v, p) => (v === null || v === undefined) ? '' : Number(v).toLocaleString(undefined, { minimumFractionDigits: p, maximumFractionDigits: p });
|
|
const date_heure = (ms) => new Date(ms).toLocaleString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
|
const format_axe = function (ms) {
|
|
const d = new Date(ms);
|
|
if (etat.periode === '24h') return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
|
|
if (etat.periode === '7j') return d.toLocaleDateString(undefined, { weekday: 'short', day: '2-digit' }); // graduations à minuit
|
|
if (etat.periode === '1a') return d.toLocaleDateString(undefined, { month: 'short', year: '2-digit' });
|
|
return d.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit' });
|
|
};
|
|
|
|
// Graduations de l'axe du temps sur des instants "ronds" de l'heure LOCALE (toutes les 3 h sur 24 h, chaque jour
|
|
// à minuit sur 7 jours, tous les 5 jours sur 30 jours, le 1er et le 15 sur 3 mois, chaque 1er du mois sur 1 an) :
|
|
function Graduations(debut, fin)
|
|
{
|
|
const graduations = [];
|
|
const d = new Date(debut);
|
|
d.setMinutes(0, 0, 0);
|
|
if (etat.periode === '24h') d.setHours(Math.ceil(d.getHours() / 3) * 3); // prochaine heure multiple de 3
|
|
else { d.setHours(0); d.setDate(d.getDate() + 1); } // minuit suivant
|
|
for (let garde = 0; d.getTime() <= fin && garde < 400; garde++) {
|
|
const jour = d.getDate();
|
|
let retenir = true;
|
|
if (etat.periode === '30j') retenir = (jour % 5 === 1);
|
|
else if (etat.periode === '3m') retenir = (jour === 1 || jour === 15);
|
|
else if (etat.periode === '1a') retenir = (jour === 1);
|
|
if (retenir && d.getTime() >= debut) graduations.push({ value: d.getTime() });
|
|
if (etat.periode === '24h') d.setHours(d.getHours() + 3); else d.setDate(d.getDate() + 1);
|
|
}
|
|
return graduations;
|
|
}
|
|
|
|
function Boutons(conteneur, choix, actif, action)
|
|
{
|
|
conteneur.innerHTML = '';
|
|
Object.keys(choix).forEach(function (cle) {
|
|
const bouton = document.createElement('button');
|
|
bouton.type = 'button';
|
|
bouton.textContent = choix[cle];
|
|
if (cle === actif) bouton.classList.add('actif');
|
|
bouton.addEventListener('click', function () { action(cle); });
|
|
conteneur.appendChild(bouton);
|
|
});
|
|
}
|
|
|
|
function Message(texte)
|
|
{
|
|
const p = document.getElementById('message_courbe');
|
|
p.textContent = texte || '';
|
|
p.hidden = !texte;
|
|
}
|
|
|
|
function Afficher()
|
|
{
|
|
document.getElementById('titre_courbe').textContent = etat.nom;
|
|
const variables = TEXTES_COURBE.variables[etat.type];
|
|
const zone_variables = document.getElementById('choix_variable');
|
|
if (variables) Boutons(zone_variables, variables, etat.variable, function (v) { etat.variable = v; Afficher(); });
|
|
else zone_variables.innerHTML = '';
|
|
Boutons(document.getElementById('choix_periode'), TEXTES_COURBE.periodes, etat.periode, function (p) { etat.periode = p; Afficher(); });
|
|
history.replaceState(null, '', '#courbe=' + [etat.type, etat.element, etat.variable || '-', etat.periode].join('.'));
|
|
document.getElementById('stats_courbe').textContent = '';
|
|
Message(TEXTES_COURBE.chargement);
|
|
|
|
const numero = ++requete; // Seule la réponse à la DERNIÈRE demande est affichée
|
|
const parametres = new URLSearchParams({ bateau: panneau.dataset.bateau, generation: panneau.dataset.generation, type: etat.type,
|
|
element: etat.element, variable: etat.variable || '', periode: etat.periode });
|
|
// Causes distinguées (synthèse §50) : bibliothèque absente, refus motivé du serveur, erreur technique (détail
|
|
// dans la console du navigateur) - un message unique masquait la vraie raison.
|
|
if (typeof Chart === 'undefined') { Message(TEXTES_COURBE.bibliotheque); return; }
|
|
fetch('bateau_mesures.php?' + parametres.toString(), { cache: 'no-store', credentials: 'same-origin' })
|
|
.then(r => r.json().then(d => ({ ok: r.ok, d: d })))
|
|
.then(function (reponse) {
|
|
if (numero !== requete) return;
|
|
if (!reponse.ok) { Message(TEXTES_COURBE.erreur + (reponse.d && reponse.d.erreur ? ' (' + reponse.d.erreur + ')' : '')); return; }
|
|
Dessiner(reponse.d);
|
|
})
|
|
.catch(function (erreur) {
|
|
console.error('Courbe :', erreur);
|
|
if (numero === requete) Message(TEXTES_COURBE.erreur);
|
|
});
|
|
}
|
|
|
|
function Dessiner(donnees)
|
|
{
|
|
if (graphique) { graphique.destroy(); graphique = null; }
|
|
if (donnees.points.length === 0) { Message(TEXTES_COURBE.aucune); return; }
|
|
Message('');
|
|
const p = donnees.precision, u = donnees.unite ? ' ' + donnees.unite : '';
|
|
const serie = (cle) => donnees.points.map(pt => ({ x: pt.t, y: pt[cle] }));
|
|
graphique = new Chart(document.getElementById('canvas_courbe'), {
|
|
type: 'line',
|
|
data: { datasets: [
|
|
{ label: TEXTES_COURBE.bande, data: serie('max'), borderWidth: 0, pointRadius: 0, fill: '+1', backgroundColor: 'rgba(115, 183, 214, 0.30)' },
|
|
{ label: 'min', data: serie('min'), borderWidth: 0, pointRadius: 0, fill: false },
|
|
{ label: TEXTES_COURBE.moyenne, data: serie('v'), borderColor: '#004563', borderWidth: 1.5, pointRadius: 0, fill: false }
|
|
] },
|
|
options: {
|
|
animation: false, maintainAspectRatio: false, normalized: true,
|
|
interaction: { mode: 'index', intersect: false },
|
|
scales: {
|
|
x: { type: 'linear', min: donnees.debut, max: donnees.fin, afterBuildTicks: (axe) => { axe.ticks = Graduations(donnees.debut, donnees.fin); },
|
|
ticks: { autoSkip: false, maxRotation: 0, callback: (v) => format_axe(v) } },
|
|
y: { title: { display: !!donnees.unite, text: donnees.unite }, ticks: { callback: (v) => nombre(v, p) } }
|
|
},
|
|
plugins: {
|
|
legend: { labels: { filter: (item) => item.datasetIndex !== 1 } },
|
|
tooltip: { callbacks: {
|
|
title: (items) => items.length ? date_heure(items[0].parsed.x) : '',
|
|
label: function (item) {
|
|
if (item.datasetIndex === 1) return null;
|
|
if (item.datasetIndex === 0) {
|
|
const pt = donnees.points[item.dataIndex];
|
|
return TEXTES_COURBE.bande + ' : ' + nombre(pt.min, p) + ' / ' + nombre(pt.max, p) + u;
|
|
}
|
|
return TEXTES_COURBE.moyenne + ' : ' + nombre(item.parsed.y, p) + u;
|
|
}
|
|
} }
|
|
}
|
|
}
|
|
});
|
|
if (donnees.stats) {
|
|
const s = donnees.stats;
|
|
document.getElementById('stats_courbe').textContent =
|
|
TEXTES_COURBE.min + ' : ' + nombre(s.min.v, p) + u + ' (' + date_heure(s.min.t) + ') ' +
|
|
TEXTES_COURBE.max + ' : ' + nombre(s.max.v, p) + u + ' (' + date_heure(s.max.t) + ') ' +
|
|
TEXTES_COURBE.moyenne + ' : ' + nombre(s.moyenne, p) + u;
|
|
}
|
|
}
|
|
|
|
function Ouvrir(type, element, nom, variable, periode)
|
|
{
|
|
etat = { type: type, element: element, nom: nom,
|
|
variable: (TEXTES_COURBE.variables[type] && TEXTES_COURBE.variables[type][variable]) ? variable : (VARIABLE_PAR_DEFAUT[type] || null),
|
|
periode: TEXTES_COURBE.periodes[periode] ? periode : '24h' };
|
|
panneau.hidden = false;
|
|
Afficher();
|
|
}
|
|
|
|
function Fermer()
|
|
{
|
|
panneau.hidden = true;
|
|
requete++;
|
|
if (graphique) { graphique.destroy(); graphique = null; }
|
|
etat = null;
|
|
history.replaceState(null, '', location.pathname + location.search);
|
|
}
|
|
|
|
document.addEventListener('click', function (e) {
|
|
const ligne = e.target.closest('tr.avec_courbe');
|
|
if (ligne && !panneau.contains(ligne)) Ouvrir(ligne.dataset.type, ligne.dataset.element, ligne.dataset.nom, null, null);
|
|
});
|
|
document.getElementById('fermer_courbe').addEventListener('click', Fermer);
|
|
panneau.addEventListener('click', function (e) { if (e.target === panneau) Fermer(); });
|
|
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !panneau.hidden) Fermer(); });
|
|
|
|
// Réouverture après rechargement (adresse #courbe=type.element.variable.periode), si l'élément existe toujours :
|
|
const m = location.hash.match(/^#courbe=(\w+)\.(\d+)\.([\w-]+)\.(\w+)$/);
|
|
if (m) {
|
|
const ligne = document.querySelector('tr.avec_courbe[data-type="' + m[1] + '"][data-element="' + m[2] + '"]');
|
|
if (ligne) Ouvrir(m[1], m[2], ligne.dataset.nom, m[3], m[4]);
|
|
}
|
|
})();
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Arrivée d'une nouvelle archive (synthèse §50) : sur la vue "Données du bateau" (génération active), toutes les
|
|
// 30 s, la page demande au portail la date de la dernière archive traitée pour ce bateau (bateau_etat.php : une
|
|
// requête légère, droits contrôlés) ; si elle a changé depuis l'affichage, la page se recharge. Arrêt en cas de
|
|
// session expirée ou de refus.
|
|
//------------------------------------------------------------------------------------------------------------
|
|
// Juste après une demande d'archive (étape 3d, data-demande) : vérification toutes les 3 s pendant 90 s, pour une mise
|
|
// à jour quasi immédiate dès que l'archive est chargée ; ensuite (ou sans demande) : toutes les 30 s.
|
|
const TEXTE_SANS_REPONSE = <?php echo json_encode(t("Pas de réponse du bateau pour l'instant (hors connexion ?) : affichage des dernières données reçues."), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>;
|
|
(function () {
|
|
const surveillance = document.getElementById('surveillance_archive');
|
|
if (!surveillance) return;
|
|
const fin_rapide = (surveillance.dataset.demande === '1') ? Date.now() + 90000 : 0;
|
|
function Verifier()
|
|
{
|
|
fetch('bateau_etat.php?id=' + encodeURIComponent(surveillance.dataset.idBateau), { cache: 'no-store', credentials: 'same-origin' })
|
|
.then(function (reponse) {
|
|
if (!reponse.ok) return 'arret'; // session expirée ou accès refusé : on arrête de vérifier
|
|
return reponse.json();
|
|
})
|
|
.then(function (etat) {
|
|
if (etat === 'arret') return;
|
|
if (etat && (etat.date || '') !== surveillance.dataset.date) { location.reload(); return; }
|
|
const info = document.getElementById('info_mise_a_jour');
|
|
if (info && fin_rapide && Date.now() >= fin_rapide && info.classList.contains('en_cours')) {
|
|
info.classList.remove('en_cours');
|
|
info.textContent = TEXTE_SANS_REPONSE;
|
|
}
|
|
setTimeout(Verifier, Date.now() < fin_rapide ? 3000 : 30000);
|
|
})
|
|
.catch(function () { setTimeout(Verifier, 30000); }); // réseau momentanément indisponible
|
|
}
|
|
setTimeout(Verifier, fin_rapide ? 3000 : 30000);
|
|
})();
|
|
|
|
|
|
function toggle_help(){
|
|
var help = document.getElementById("help");
|
|
var help_note = document.getElementById("help_note");
|
|
|
|
if (help_note.style.display == "none"){
|
|
help_note.style.display = "block";
|
|
help.classList.remove("help_open");
|
|
help.classList.add("help_close");
|
|
}
|
|
else {
|
|
help_note.style.display = "none";
|
|
help.classList.remove("help_close");
|
|
help.classList.add("help_open");
|
|
}
|
|
}
|
|
|
|
|
|
function closeEditAccess(){
|
|
document.getElementById('popUpAcces').style.display = "none";
|
|
}
|
|
|
|
function popUpEditAccess(acces){
|
|
document.getElementById('popUpAcces').style.display = "block";
|
|
document.getElementById('shipmemberForm').value = acces['Destinataire'];
|
|
document.getElementById('cree_parForm').value = acces['Cree_par_nom'];
|
|
document.getElementById('idForm').value = acces['ID'];
|
|
|
|
// Sélection du type actuel (comparaison en texte : la valeur d'une option est une chaîne, l'ID reçu un nombre) :
|
|
const selectElement = document.getElementById('type_profilForm');
|
|
for (let i = 0; i < selectElement.options.length; i++) {
|
|
if (selectElement.options[i].value === String(acces['ID_Type_acces_bateau_shipmember'])) {
|
|
selectElement.options[i].selected = true;
|
|
break;
|
|
}
|
|
}
|
|
document.getElementById('fin_validiteForm').value = acces['Fin_validite'].substring(0,10);
|
|
}
|
|
|
|
</script>
|