commit 82f431b3ae1eb2f0016ff2af4ec9fb849da863be
Author: Pierre
Date: Mon Sep 28 08:04:25 2026 +0000
Import initial du site ship2net.fr : pages PHP, config, traductions t(), composer.json
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2ccc8f8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+config/config.php
+config/confidentiel.php
+config/lang/*.bak
+temp/
+*.bak
+vendor/*
+!vendor/phpqrcode/
+vendor/phpqrcode/cache/
+html/vendor_OLD/
diff --git a/config/definitions.php b/config/definitions.php
new file mode 100644
index 0000000..168cbb1
--- /dev/null
+++ b/config/definitions.php
@@ -0,0 +1,175 @@
+connect_error)
+ {
+ $_SESSION['erreurs_connexion_db'][$i] = $_SESSION['db']->connect_errno;
+ $_SESSION['db'] = NULL;
+ sleep($i+1); // Attente avant de recommencer (en secondes). NB : le délai d'attente augmente à chaque tentative...
+ }
+ else break;
+ }
+
+
+ if ($_SESSION['db'] == NULL) // Erreur de la fonction de connexion, malgré plusieurs tentatives :
+ {
+ if (PRODUCTION)
+ {
+ // Préparation du texte d'erreur en dur et dans les 2 langues car si la BDD n'est pas accessible, on aura du mal à aller y chercher les messages d'erreur... :
+ $message_erreur = '
Oups ! Une erreur est survenue.
+
Essayez de rafraichir la page mais si le problème persiste, n\'hésitez pas à nous contacter à : '.$_SESSION['Config__Adresse_email_contact'].'
+
NB : Ce message a été automatiquement envoyé aux services techniques de Koriolan pour être traité. Car vous offrir un service irréprochable est notre priorité !
Try to refresh the page but if the problem persists, please contact us at : '.$_SESSION['Config__Adresse_email_contact'].'
+
NB: This message has automatically been sent to Koriolan technical services for processing. Because offering you an irreproachable service is our permanent concern !
Oups ! Une erreur est survenue lors de l\'ouverture de la BDD.
+
'.MAX_TENTATIVES_CONNEXIONS_BDD.' tentatives ont été effectuées et ont toutes échouées
';
+ echo '
Les erreurs récupérées sont :';
+ for ($i=0; $i';
+ echo '
';
+ }
+
+ exit(); // Arrêt du script.
+ }
+ else
+ {
+ if ($_SESSION['erreurs_connexion_db'][0] != 0) // Si on s'est connecté mais qu'au moins une erreur a été détectée, on enregistre dans l'historique l'évènement).
+ {
+ for ($i=0; (($_SESSION['erreurs_connexion_db'][$i] != 0) AND ($i'.$message_erreur.'';
+
+ // Enregistrement dans le fichier de logs :
+ Enregistrement_log("\tOups ! Erreur MYSQL. Requête = [".$requete."], message d'erreur = [".mysqli_error($_SESSION['db'])."]", $fichier, $ligne);
+
+ exit(); // Arrêt du script.
+ }
+ else return($retour); // Requete revenue sans erreur...
+
+}
+
+//************************************************************************************************************
+// Fonction : Exécution d'une requête préparée dans la BDD
+// Reçoit : La requête à exécuter, un array avec les paramètres de la requête, le fichier d'origine et la ligne d'origine
+// Retour : Le retour de la requête si aucune erreur. En cas d'erreur, le script est arrêté après l'affichage d'une page d'erreur
+//************************************************************************************************************
+function Requete_preparee_bdd($requete, $parametres, $fichier, $ligne)
+{
+ $texte_erreur = "";
+
+
+ if ($stmt = mysqli_prepare($_SESSION['db'], $requete)) // Création de la requête préparée
+ {
+ // Récupération des éléments du tableau de paramètres reçu :
+ $types = "";
+ $nb = 0;
+ foreach ($parametres as $parametre)
+ {
+ $types .= $parametre[0]; // Concaténation des types pour obtenir une chaine de tous les types, les uns derrière les autres
+ $valeurs[$nb ++] = $parametre[1]; // Récupération de la valeur de chaque paramètre dans un tableau temporaire
+ }
+
+ // Création d'un tableau avec tous les paramètres de la fonction 'mysqli_stmt_bind_param' :
+ if ($nb > 0)
+ {
+ $params[] = $stmt; // Premier paramètre : identifiant de la requête préparée
+ $params[] = &$types; // Second paramètre : chaine de caractère avec tous les types
+ for ($i=0; $i<$nb; $i++) $params[] = &$valeurs[$i]; // Troisième et au-delà : une valeur par paramètre
+
+ // Appel de la fonction 'mysqli_stmt_bind_param' à travers 'call_user_func_array' qui permet de passer un tableau en argument :
+ if (call_user_func_array("mysqli_stmt_bind_param", $params) == false) // NB : le retour de 'call_user_func_array' est celui de 'mysqli_stmt_bind_param'
+ {
+ $texte_erreur = "\tOups ! Le binding de la requête préparée [".$requete."] a échoué avec le message d'erreur [".mysqli_error($_SESSION['db'])."]";
+ }
+ } // Si le nombre de paramètres est nul, c'est qu'il n'y a pas de variable dans la requet et qu'on peut donc l'exécuter sans binding...
+
+ // exécution de la requête préparée avec le binding des variables fait :
+ if (mysqli_stmt_execute($stmt) == false) // Exécution de la requête
+ {
+ $texte_erreur = "\tOups ! L'exécution de la requête préparée [".$requete."] a échoué avec le message d'erreur [".mysqli_error($_SESSION['db'])."]";
+ }
+
+ // Récupération du jeu de résultats :
+ $retour = mysqli_stmt_get_result($stmt);
+ if (($retour == false) AND (mysqli_error($_SESSION['db']) != "")) // Il se peut que 'mysqli_stmt_get_result' renvoie 'false' si la requête exécutée n'attend pas de réponse (UPDATE, INSERT). On vériife donc si une erreur MySql a été déclenchée avant de passer en erreur...
+ {
+ $texte_erreur = "\tOups ! La recherche des résultats de la requête préparée [".$requete."] a échoué avec le message d'erreur [".mysqli_error($_SESSION['db'])."]";
+ }
+
+ }
+ else $texte_erreur = "\tOups ! La création de la requête préparée [".$requete."] a échoué avec le message d'erreur [".mysqli_error($_SESSION['db'])."]";
+
+ mysqli_stmt_close($stmt); // Libération de la requête préparée
+
+ // Traitement des cas d'erreur :
+ // (NB : $texte_erreur est différent de $message_erreur !!)
+ if ($texte_erreur != "")
+ {
+ // Préparation du texte d'erreur :
+ $message_erreur = nl2br($_SESSION['textes']['fonctions_bdd']['all']['retour']['message_erreur']);
+
+ // Mise à jour des parametres du texte :
+ $message_erreur = str_replace("[[Email]]", $_SESSION['Config__Adresse_email_contact'], $message_erreur);
+
+ // Affichage du message d'erreur :
+ echo '
'.$message_erreur.'
';
+
+ // Enregistrement dans le fichier de logs :
+ Enregistrement_log($texte_erreur, $fichier, $ligne);
+
+ exit(); // Arrêt du script.
+ }
+ else return($retour); // Requete revenue sans erreur...
+}
+
+
+//************************************************************************************************************
+// Fonction : Fetch de ligne de résultat MYSQL
+// Reçoit : La réponse complète de la requête (au sein de laquelle la ligne doit être cherchée)
+// Retour : La ligne de resultat suivante ou FALSE si aucune ligne supplémentaire n'existe.
+//************************************************************************************************************
+function Recherche_ligne_bdd($reponse)
+{
+ // Récupération du type de retour à renvoyer :
+ if (func_num_args() > 1)
+ {
+ $resulttype = func_get_arg(1);
+ switch(func_get_arg(1))
+ {
+ case MYSQLI_ASSOC : {$resulttype = MYSQLI_ASSOC; break;} // ne renvoi que la version associative du tableau de retour
+ case MYSQLI_NUM : {$resulttype = MYSQLI_NUM; break;} // ne renvoi que la version numérique du tableau de retour
+ case MYSQLI_BOTH :
+ default : {$resulttype = MYSQLI_BOTH; break;} // renvoi les deux versions (associative et numérique) du tableau de retour
+ }
+ }
+ else $resulttype = MYSQLI_BOTH; // Valeur par défaut de la fonction.
+
+ // Recherche de la ligne
+ return(mysqli_fetch_array($reponse, $resulttype));
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Libération de jeu de résultat MYSQL
+// Reçoit : La variable à libérer
+// Retour : Rien
+//************************************************************************************************************
+function Liberation_jeu_de_resultat_bdd($reponse)
+{
+ mysqli_free_result($reponse);
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Récupération de l'id créé
+// Reçoit : Rien
+// Retour : L'ID de l'enregistrement qui vient d'être ajouté
+//************************************************************************************************************
+function Recuperation_id_cree_bdd()
+{
+ return(mysqli_insert_id($_SESSION['db']));
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Liste des champs de base de données
+// Reçoit : Rien
+// Retour : La liste des champs
+//************************************************************************************************************
+function Champs_bdd($reponse)
+{
+ return(mysqli_fetch_fields($reponse));
+}
+
+
+//************************************************************************************************************
+// Fonction : Comptage des résultats de la requête
+// Reçoit : La requête dont le comptage est recherché
+// Retour : Le nombre de lignes de résultats de la requête
+//************************************************************************************************************
+function Comptage_resultats_requete($requete)
+{
+
+ $reponse = Requete_bdd('SELECT COUNT(*) AS total FROM ('.$requete.') AS Total', __FILE__, __LINE__); // Le second AS est nécessaire mais ne sert pas...
+ $donnees = Recherche_ligne_bdd($reponse);
+ Liberation_jeu_de_resultat_bdd($reponse);
+ return($donnees['total']);
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Protection d'une requête contre les injections SQL
+// Reçoit : La requête à traiter
+// Retour : La requête après traitement
+//************************************************************************************************************
+function Protection_mysql($requete)
+{
+
+ return(mysqli_real_escape_string($_SESSION['db'], $requete));
+}
+
+
+//************************************************************************************************************
+// Fonction : Fermeture de base de données
+// Reçoit : Rien
+// Retour : Rien
+//************************************************************************************************************
+function Fermeture_bdd()
+{
+ if ($_SESSION['db'] != NULL) // SDIR # 289
+ {
+ mysqli_close($_SESSION['db']);
+ $_SESSION['db'] = NULL; // SDIR # 289
+ }
+}
+
+
+//************************************************************************************************************
+// Fonction : Encryptage d'un message
+// Reçoit : Le message à encrypter
+// Retour : Le message encrypté
+//************************************************************************************************************
+function Encryptage($message)
+{
+ // La clé (secrète) est le mdp du user :
+ $key = $_SESSION['login_key'];
+ // algorythme choisi selon http://fr.php.net/manual/fr/mcrypt.ciphers.php
+ $algo = MCRYPT_BLOWFISH;
+ // mode choisi selon http://fr.php.net/manual/fr/mcrypt.constants.php
+ $mode = MCRYPT_MODE_CBC ;
+
+ //Réduction de la taille de la clé selon la taille maximale supportée par l'algorythme mcrypt choisi :
+ $key = substr($key, 0, mcrypt_module_get_algo_key_size($algo));
+ // Création d'un vecteur d'initialisation selon la taille maximale possible :
+ $iv = mcrypt_create_iv(mcrypt_get_iv_size($algo, $mode), MCRYPT_DEV_URANDOM);
+
+ // Chiffrage du message :
+ $data = mcrypt_encrypt($algo, $key, $message, $mode, $iv);
+
+ // Retour du message après ajouté devant l'IV qui sera nécessaire au décodage (l'IV n'est pas confidentiel)
+ // puis avoir encodé le tout en base64 (utile si l'on veut mettre le résultat dans une base de donnée)
+ return(base64_encode($iv.$data));
+}
+
+
+//************************************************************************************************************
+// Fonction : Décryptage d'un message
+// Reçoit : Le message crypté
+// Retour : Le message décrypté
+//************************************************************************************************************
+function Decryptage($message_crypte)
+{
+
+ // La clé (secrète) est le mdp du user :
+ $key = $_SESSION['login_key'];
+ // algorythme choisi selon http://fr.php.net/manual/fr/mcrypt.ciphers.php
+ $algo = MCRYPT_BLOWFISH;
+ // mode choisi selon http://fr.php.net/manual/fr/mcrypt.constants.php
+ $mode = MCRYPT_MODE_CBC ;
+
+ //Réduction de la taille de la clé selon la taille maximale supportée par l'algorythme mcrypt choisi :
+ $key = substr($key, 0, mcrypt_module_get_algo_key_size($algo));
+ // Rechjerche de la taille du vecteur d'initialisation (dépend uniquement du mode de cryptage) :
+ $size = mcrypt_get_iv_size($algo, $mode);
+ // Désencodage en base64 :
+ $message_crypte = base64_decode($message_crypte);
+ // Extraction de l'IV qui est stocké en début de message :
+ $iv = substr($message_crypte, 0, $size); // On extrait l'IV (qui est de longueur connue) du début du message.
+ // Extraction du message crypté proprement dit :
+ $message_crypte = substr($message_crypte, $size); // On ne garde que la fin du message (sans l'IV).
+ // Retour du message décrypté :
+ return(trim(mcrypt_decrypt($algo, $key, $message_crypte, $mode, $iv))); // Le TRIM permet de supprimer les 0x00 qui sont ajoutés à la fin pour former des blocs de 8 octets...
+}
+
+
+//************************************************************************************************************
+// Fonction : Rencryptage d'un message
+// Reçoit : Le message à encrypter (les variables de session $_SESSION['login_key'] et $_SESSION['new_login_key'] sont utilisées pour assurer le décryptage puis le rencryptage)
+// Retour : Le message encrypté
+//************************************************************************************************************
+function Rencryptage($message_crypte)
+{
+ // Décodage avec ancienne clé :
+
+ // La clé (secrète) est l'ancien mdp du user :
+ $key = $_SESSION['login_key'];
+ // algorythme choisi selon http://fr.php.net/manual/fr/mcrypt.ciphers.php
+ $algo = MCRYPT_BLOWFISH;
+ // mode choisi selon http://fr.php.net/manual/fr/mcrypt.constants.php
+ $mode = MCRYPT_MODE_CBC ;
+
+ //Réduction de la taille de la clé selon la taille maximale supportée par l'algorythme mcrypt choisi :
+ $key = substr($key, 0, mcrypt_module_get_algo_key_size($algo));
+ // Rechjerche de la taille du vecteur d'initialisation (dépend uniquement du mode de cryptage) :
+ $size = mcrypt_get_iv_size($algo, $mode);
+ // Désencodage en base64 :
+ $message_crypte = base64_decode($message_crypte);
+ // Extraction de l'IV qui est stocké en début de message :
+ $iv = substr($message_crypte, 0, $size); // On extrait l'IV (qui est de longueur connue) du début du message.
+ // Extraction du message crypté proprement dit :
+ $message_crypte = substr($message_crypte, $size); // On ne garde que la fin du message (sans l'IV).
+ // Retour du message décrypté :
+ $message_decrypte = trim(mcrypt_decrypt($algo, $key, $message_crypte, $mode, $iv)); // Le TRIM permet de supprimer les 0x00 qui sont ajoutés à la fin pour former des blocs de 8 octets...
+
+
+ // Encodage avec nouvelle clé :
+
+ // La clé (secrète) est le nouveau mdp du user :
+ $key = $_SESSION['new_login_key'];
+
+ //Réduction de la taille de la clé selon la taille maximale supportée par l'algorythme mcrypt choisi :
+ $key = substr($key, 0, mcrypt_module_get_algo_key_size($algo));
+ // Création d'un vecteur d'initialisation selon la taille maximale possible :
+ $iv = mcrypt_create_iv(mcrypt_get_iv_size($algo, $mode), MCRYPT_DEV_URANDOM);
+
+ // Chiffrage du message :
+ $data = mcrypt_encrypt($algo, $key, $message_decrypte, $mode, $iv);
+
+ // Retour du message après ajouté devant l'IV qui sera nécessaire au décodage (l'IV n'est pas confidentiel)
+ // puis avoir encodé le tout en base64 (utile si l'on veut mettre le résultat dans une base de donnée)
+ return(base64_encode($iv.$data));
+}
+
+
+//************************************************************************************************************
+// Fonction : Génération de mot de passe aléatoire
+// Reçoit : La longueur du mot de passe à générer
+// Retour : Le mot de passe généré
+//************************************************************************************************************
+function Mdp_aleatoire($longueur)
+{
+
+ $password = "";
+ $prec = 0;
+ for($i = 0; $i < $longueur; $i++)
+ {
+ $random = rand(48,122); // chiffres de 48 à 57, maj de 65 à 90 et min de 97 à 122
+
+ // Suppression des caractères non alphanumériques :
+ if (($random == ord("0")) OR
+ (($random >= 58) AND ($random <= 64)) OR
+ ($random == ord("I")) OR
+ ($random == ord("O")) OR
+ ($random == ord("l")) OR
+ ($random == ord("o")) OR
+ (($random >= 91) AND ($random <= 96)) OR
+ ($random == $prec)) // Evite les doublons de caractères identiques (difficile à lire)
+ {
+ $i--;
+ continue;
+ }
+ $password .= chr($random);
+ $prec = $random;
+ }
+ return($password);
+}
+
+
+//************************************************************************************************************
+// Fonction : Vérification de la conformité d'un mot de passe (longueur, caractères utilisés, etc...)
+// Reçoit : Le mot de passe à vérifier
+// Retour : La liste des champs
+//************************************************************************************************************
+function Check_mdp($mdp)
+{
+global $message_erreur;
+
+// $pattern = "#(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{".MIN_TAILLE_MOTS_DE_PASSE.",".MAX_TAILLE_MOTS_DE_PASSE."}#";
+
+ // Nombre de caractères entre MIN et MAX, caractères minuscules ou majuscules ou chiffres ou '_' et '-' :
+// $pattern = "#^[a-zA-Z0-9_-]{".MIN_TAILLE_MOTS_DE_PASSE.",".."}#";
+
+ $longueur = strlen($mdp);
+ if ($longueur < MIN_TAILLE_MOTS_DE_PASSE) return(0);
+ if ($longueur > MAX_TAILLE_MOTS_DE_PASSE) return(0);
+
+ for ($i = 0; $i < $longueur; $i ++)
+ {
+ if (($mdp[$i] >= 'A') AND ($mdp[$i] <= 'Z')) continue;
+ if (($mdp[$i] >= 'a') AND ($mdp[$i] <= 'z')) continue;
+ if (($mdp[$i] >= '0') AND ($mdp[$i] <= '9')) continue;
+ if ($mdp[$i] == '_') continue;
+ if ($mdp[$i] == '-') continue;
+ if ($mdp[$i] == '!') continue;
+ if ($mdp[$i] == '*') continue;
+ if ($mdp[$i] == '=') continue;
+ if ($mdp[$i] == '.') continue;
+
+ return(-3);
+ }
+
+ return(1);
+
+ // if (preg_match($pattern, $mdp)) return(true);
+ // else return(false);
+}
+
+
+//************************************************************************************************************
+// Fonction : Dump de base de données
+// Reçoit : La BDD à dumper, la table concernée, le nom du fichier de sortie et le type de sortie attendu. Voir documentation de la fonction PHP 'mysqldump' pour détails).
+// Retour : La liste des champs
+//************************************************************************************************************
+function Dump_bdd($bdd, $table, $fichier_sortie, $type_sortie)
+{
+
+ // Appel de la fonction de DUMP :
+ system('mysqldump --host='.BDD_HOST.' --user='.BDD_USER.' --password='.BDD_PASSWORD.' '.BDD_DATABASE.' --tables '.$table.' '.$type_sortie.' '.$fichier_sortie.' ');
+
+}
+
+
+//==================================================================================================================
+// Fonction : Création d'un fichier d'archive (compressé et encodé) à partir d'un répertoire
+// Reçoit : Le chemin complet du répertoire à archiver et le nom du fichier de sortie.
+// Retour : 0 si Ok, <0 si erreur
+//==================================================================================================================
+function creer_archive_chiffree(string $repertoire_a_chiffrer, string $fichier_sortie)
+{
+ if (!is_dir($repertoire_a_chiffrer)) return (-1);
+
+ $tarFile = $fichier_sortie . '.tar';
+ $gzFile = $tarFile . '.gz';
+ $encFile = $gzFile . '.enc';
+
+ foreach ([$tarFile, $gzFile] as $f) {
+ if (file_exists($f)) {
+ unlink($f);
+ }
+ }
+
+ // 1. Archivage + compression (tar.gz)
+ $phar = new PharData($tarFile);
+// $phar->buildFromDirectory($repertoire_a_chiffrer);
+ $phar->buildFromDirectory(".");
+ $phar->compress(Phar::GZ);
+ unset($phar);
+ unlink($tarFile);
+
+ // 2. Chiffrement AES-256-CBC avec clé dérivée par PBKDF2
+ $contenu = file_get_contents($gzFile);
+ $salt = openssl_random_pseudo_bytes(16);
+ $iv = openssl_random_pseudo_bytes(16);
+ $iterations = 100000;
+
+ $cle = openssl_pbkdf2(BDD_BATEAU_PASSWORD, $salt, 32, $iterations, 'sha256');
+
+ $chiffre = openssl_encrypt($contenu, 'aes-256-cbc', $cle, OPENSSL_RAW_DATA, $iv);
+
+ if ($chiffre === false) return (-2);
+
+ // Format du fichier : salt(16) + iv(16) + données chiffrées
+ file_put_contents($encFile, $salt . $iv . $chiffre);
+
+ unlink($gzFile);
+
+ return(0);
+}
+
+
+
+//==================================================================================================================
+// Fonction :
+// Reçoit : Le chemin complet du répertoire à archiver et le nom du fichier de sortie.
+// Retour : 0 si Ok, <0 si erreur
+//==================================================================================================================
+function dechiffrer_archive(string $fichierChiffre, string $dossierDestination, string $motDePasse)
+{
+ $contenu = file_get_contents($fichierChiffre);
+ $salt = substr($contenu, 0, 16);
+ $iv = substr($contenu, 16, 16);
+ $donnees = substr($contenu, 32);
+
+ $iterations = 100000;
+ $cle = openssl_pbkdf2($motDePasse, $salt, 32, $iterations, 'sha256');
+
+ $dechiffre = openssl_decrypt($donnees, 'aes-256-cbc', $cle, OPENSSL_RAW_DATA, $iv);
+
+ if ($dechiffre === false) return (-1);
+
+ if (!is_dir($dossierDestination)) mkdir($dossierDestination, 0755, true);
+
+ $tmpGz = tempnam(sys_get_temp_dir(), 'restore_') . '.tar.gz';
+ file_put_contents($tmpGz, $dechiffre);
+
+ $phar = new PharData($tmpGz);
+ $phar->extractTo($dossierDestination, null, true);
+
+ unlink($tmpGz);
+ echo "Archive extraite dans : ".$dossierDestination."\n";
+ return (0);
+}
+
+
diff --git a/config/fonctions_fichiers.php b/config/fonctions_fichiers.php
new file mode 100644
index 0000000..824759b
--- /dev/null
+++ b/config/fonctions_fichiers.php
@@ -0,0 +1,250 @@
+isFile())
+ {
+ $filePath = $fileInfo->getPathname();
+ $modificationTimestamp = $fileInfo->getMTime();
+ $modificationDate = new DateTime('@' . $modificationTimestamp);
+
+ if ($modificationDate < $date_max)
+ {
+ if (unlink($filePath))
+ {
+ if ($verbose) echo "\tLe fichier [".$filePath."] a été supprimé.\n";
+ }
+ else
+ {
+ if ($verbose) echo "\t/!\\ Le fichier [".$filePath."]. est antérieur a ".$nb_jours_anteriorite." jours mais n'a pas pu être supprimé...\n";
+ $retour = -1;
+ }
+ }
+ }
+ }
+ return($retour);
+
+ // Attention : non testé !! (Pierre 09/2024) :
+ // Get a list of all directories inside the root directory and delete the empty ones
+ // $subdirectories = array_filter(glob($repertoire . '*'), 'is_dir');
+
+ // foreach ($subdirectories as $subdirectory) {
+ // if (count(scandir($subdirectory)) === 2) {
+ // // if (rmdir($subdirectory)) {
+ // // if ($verbose) echo "Directory: {$subdirectory} removed. ";
+ // // } else {
+ // // if ($verbose) echo "Error removing directory: {$subdirectory}. ";
+ // // }
+ // echo "Directory: {$subdirectory} removed. ";
+ // }
+ // }
+}
+
+
diff --git a/config/fonctions_ftp.php b/config/fonctions_ftp.php
new file mode 100644
index 0000000..91ce608
--- /dev/null
+++ b/config/fonctions_ftp.php
@@ -0,0 +1,108 @@
+.
+// Au passage, cette fonction étant la première appelée par chaque page (après l'ouverture
+// de session obligatoire pour pouvoir accéder aux variables de session), on vérifie si
+// l'environnement est correctement positionné. Si ce n'est pas le cas, par exemple avec
+// l'accès à la BDD non établi ou aucune langue sélectionnée, elle renvoit immédiatement
+// vers le fichier 'index.php' qui s'en chargera.
+// Reçoit : Rien
+// Retour : Rien
+//************************************************************************************************************
+function Entete_page_html()
+{
+ // Vérification à chaque ouverture de page que les variables de session sont bien initialisées (le test se fait sur quelques "échantillons" supposés représentatifs...) :
+ if ((!isset($_SESSION['langue'])) OR
+ (!isset($_SESSION['display_menu']))) header('Location:index.php?origine='.basename($_SERVER['SCRIPT_FILENAME']));
+
+ // Entête HTML :
+ echo '';
+
+ echo '';
+
+ echo '';
+ echo '';
+ echo 'ShipHeart - Simplicity is true luxury';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+
+
+ echo "";
+ echo "";
+ echo "";
+
+ echo "";
+ echo "";
+
+ // gestion des fichiers CSS (et de scripts spécifiques à certains fichiers) :
+ switch(basename($_SERVER['SCRIPT_FILENAME']))
+ {
+ case "administration.php" : { echo ''; break; }
+ case "textes.php" : { echo ''; break; }
+ case "bateaux.php" : { echo ''; break; }
+ case "cartes.php" : { echo ''; break; }
+ case "mentions_legales.php" : { echo ''; break; }
+ case "shipmembers.php" : { echo ''; break; }
+ case "uploads.php" : { echo ''; break; }
+ case "versions.php" : { echo ''; break; }
+ }
+
+ echo ''; // CSS de Bootstrap
+ echo ''; // CSS général, tous écrans.
+ echo ''; // CSS pour les petits écrans (doit être placé à la fin car il override les CSS globaux)
+
+ // Chargement de srcipts JS généraux :
+ echo ''; // Pour Google Charts (graphiques)
+ echo '';
+
+
+
+
+ echo '';
+
+ echo '';
+}
+
+//************************************************************************************************************
+// Fonction : Insertion de l'entête des pages avec l'affichage du bandeau de titre et les menus.
+// Puis affichage des messages d'erreur et d'information en les marquant de façon différenciée
+// selon que le menu est déployé ou non et/ou que le message d'erreur existe et/ou que le
+// message d'information existe.
+// Reçoit : Les message d'erreur et le message d'information (de plus, on retrouve le nom du fichier via les variables du serveur)
+// Retour : Rien
+//************************************************************************************************************
+function Entete_page($message_erreur, $message_information)
+{
+
+ $nom_page = basename($_SERVER['SCRIPT_FILENAME']);
+
+ // Détermination de la nouvelle langue par rapport à l'actuelle :
+ if ($_SESSION['langue'] == "FR") $new_langue = "EN";
+ else $new_langue = "FR";
+
+
+ echo '
';
+
+
+ //--------------------------------------------------------------------------------------------
+ // Menu afficher / masquer -- Mobile
+ //--------------------------------------------------------------------------------------------
+ echo '';
+
+
+ //--------------------------------------------------------------------------------------------
+ // Script d'affichage/masquage du menu :
+ //--------------------------------------------------------------------------------------------
+
+
+ //--------------------------------------------------------------------------------------------
+ // Affichage des messages d'erreur et d'info sous le menu :
+ //--------------------------------------------------------------------------------------------
+ if (!PRODUCTION) $message_erreur .= "/!\ Base de données de test [".BDD_DATABASE."] ";
+ // else $message_erreur .= "Base de données de production [".BDD_DATABASE."] ";
+
+ if ($message_erreur != "") echo '
'.$message_erreur.'
';
+ if ($message_information != "") echo '
'.$message_information.'
';
+
+}
+
+
+
+
+//************************************************************************************************************
+// Fonction : Insertion du pied de page
+// Reçoit : Rien (mais on retrouve le nom du fichier via les variables du serveur)
+// Retour : Rien
+//************************************************************************************************************
+function Pied_de_page()
+{
+ $nom_page = basename($_SERVER['SCRIPT_FILENAME']);
+
+// echo "";
+
+ echo '
';
+
+
+
+//==================================================================================
+// Script JS pour pieds de page :
+?>';
+
+echo '';
+
+} // Fin de la fonction Pied_de_page()
+
+
+
diff --git a/config/fonctions_mail.php b/config/fonctions_mail.php
new file mode 100644
index 0000000..f2f51a7
--- /dev/null
+++ b/config/fonctions_mail.php
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+ '.$texte_brut.'
+
+ ';
+
+
+ $mail = new PHPMailer();
+
+ $mail->CharSet = 'UTF-8'; // Format d'encodage à utiliser pour les caractères
+
+// $mail->SMTPDebug = 4; // Valeur entre 0 (normal) et 4 (full debug). Attention : mettre cette valeur > 0 dévoule certains paramètres confidentiels !
+
+ $mail->isSMTP();
+ $mail->Host = 'ssl0.ovh.net'; // Adresse IP ou DNS du serveur SMTP
+ $mail->Port = 587; // Port TCP du serveur SMTP (port SMTP chez OVH : 465=avec_SSL et 587=sans_SSL/avec_tls)
+ $mail->SMTPAuth = TRUE; // Utiliser l'authentification
+
+ if($mail->SMTPAuth)
+ {
+ $mail->SMTPSecure = 'tls'; // Protocole de sécurisation des échanges avec le SMTP
+ $mail->Username = 'do_not_answer@koriolan.tech'; // Adresse email à utiliser pour le compte SMTP
+ $mail->Password = SMTP_PASSWORD; // Mot de passe de l'adresse email à utiliser
+ }
+
+ if ($nom_expediteur == "") $mail->setFrom($adresse_expediteur);
+ else $mail->setFrom($adresse_expediteur, $nom_expediteur); // L'email et l'alias (facultatif) à afficher pour l'envoi
+
+ if ($nom_destinataire == "") $mail->AddAddress($adresse_destinataire);
+ else $mail->AddAddress($adresse_destinataire, $nom_destinataire); // L'email du destinataire et son alias (facultatif)
+
+ // $mail->addBCC('do_not_answer@koriolan.tech','ShipHeart by Koriolan'); // Destinataire BCC (idem AddAddress)
+
+ $mail->Subject = $objet; // Le sujet du mail
+
+ $mail->isHTML(TRUE); // Indique que le message dans Body sera en HTML
+ $mail->Body = $message_html;
+ $mail->AltBody = $texte_brut; // Texte brut alternatif
+
+
+ for ($i = 0; $i < 10; $i ++) // Pièces jointes (limité à un maxi de 10 !):
+ {
+ if (!isset($fichiers_joints[$i]['fichier']) OR ($fichiers_joints[$i]['fichier'] == "")) break;
+ else
+ {
+ if (isset($fichiers_joints[$i]['nom']) AND ($fichiers_joints[$i]['nom'] != "")) $mail->AddAttachment($fichiers_joints[$i]['fichier'], $fichiers_joints[$i]['nom']);
+ else $mail->AddAttachment($fichiers_joints[$i]['fichier']);
+ }
+ }
+
+
+ if(!$mail->Send()) // Envoi de l'email et test du résultat
+ {
+// $message_information .= "Erreur lors de l'envoi de l'email. PHPMailer Error = [" . $mail->ErrorInfo."]";
+ }
+ else
+ {
+// $message_information .= "L'email a été envoyé correctement.";
+ }
+}
+
diff --git a/config/fonctions_pdf.php b/config/fonctions_pdf.php
new file mode 100644
index 0000000..5c87b33
--- /dev/null
+++ b/config/fonctions_pdf.php
@@ -0,0 +1,353 @@
+Image('images/logo_koriolan.png', 10, 10, 60, 0, 'PNG', 'https://koriolan.tech');
+ }
+
+ public function Footer() {
+ $this->SetFont('helvetica', '', 7);
+ $this->SetTextColor(150, 150, 150);
+
+ // Partie gauche :
+ $this->SetXY(10, -23);
+ $this->Cell(90, 10, 'Koriolan', 0, false, 'L');
+ $this->SetXY(10, -19);
+ $this->Cell(90, 10, 'SASU au capital social de 50 000€', 0, false, 'L');
+ $this->SetXY(10, -15);
+ $this->Cell(90, 10, '6 rue Grange Bruyère - 69110 Sainte-Foy-Lès-Lyon - FRANCE', 0, false, 'L');
+
+ // Partie droite :
+ $this->SetXY(110, -23);
+ $this->Cell(90, 10, 'RCS Lyon 879 809 341', 0, false, 'R');
+ $this->SetXY(110, -19);
+ $this->Cell(90, 10, 'SIRET 879 809 341 00016', 0, false, 'R');
+ $this->SetXY(110, -15);
+ $this->Cell(90, 10, 'TVA Intraco. FR29 879 809 341', 0, false, 'R');
+ }
+
+ public function CreateTextBox($textval, $x = 0, $y = 0, $width = 0, $height = 10, $fontsize = 10, $fontstyle = '', $align = 'L', $border = 0) {
+ $this->SetXY($x, $y);
+ $this->SetFont('helvetica', $fontstyle, $fontsize);
+ $this->Cell($width, $height, $textval, 0, false, $align);
+ }
+}
+
+
+//********************************************************************************************************************************
+// Fonction : Génère le document PDF à partir des données reçues en paramètres
+// Reçoit : une autre concernant la pièce ('Commande', 'Facture') et ses attributs, une variable concernant le client,
+// et une autre contenant les lignes de la pièce. Le fichier généré est stocké dans 'documents/commandes' ou 'documents/factures'.
+// Retour : 0 si Ok, -1 si erreur
+//********************************************************************************************************************************
+function document_pdf($piece, $client, $lignes)
+{
+
+ switch($piece['type'])
+ {
+ case "commande":
+ {
+ $nom_piece = $_SESSION['textes']['fonctions_pdf']['document']['piece']['type_commande'];
+ $rep = "commandes";
+ break;
+ }
+ case "facture":
+ {
+ $nom_piece = $_SESSION['textes']['fonctions_pdf']['document']['piece']['type_facture'];
+ $rep = "factures";
+ break;
+ }
+ default: return(-1);
+ }
+
+ $pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
+
+ // set document (meta) information :
+ $pdf->SetCreator(PDF_CREATOR);
+ $pdf->SetAuthor('Koriolan');
+ $pdf->SetTitle($nom_piece.' '.$piece['no']);
+ $pdf->SetSubject('ShipHeart');
+ $pdf->SetKeywords('ShipHeart by Koriolan');
+
+ $pdf->SetTextColor(0, 69, 99);
+ $pdf->SetFont('helvetica', '', 10);
+
+ $pdf->AddPage();
+
+ // L'abscisse x varie de 10 à 200 (soit une marge de 10 à gauche et 10 à droite)
+
+ // ---------------------------------------------------------
+ // Adresse du client :
+ $x = 120; // Doit rester < 200 qui est la marge droite !
+ $y = 30;
+ $pdf->CreateTextBox($client['entreprise'], $x, $y, (200 - $x), 12, 12, 'B', 'L');
+ $y += 6;
+ $pdf->CreateTextBox($client['nom_complet'], $x, $y, (200 - $x), 10, 10, '', 'L');
+ $y += 5;
+ $pdf->CreateTextBox($client['rue'], $x, $y, (200 - $x), 10, 10, '', 'L');
+ $y += 5;
+ $pdf->CreateTextBox($client['ville_complete'], $x, $y, (200 - $x), 10, 10, '', 'L');
+ $y += 5;
+ $pdf->CreateTextBox($client['libelle_pays'], $x, $y, (200 - $x), 10, 10, '', 'L');
+
+
+ // ---------------------------------------------------------
+ // N° de commande :
+ $x = 10;
+ $y += 10;
+ $pdf->CreateTextBox($nom_piece.' '.$piece['no'], $x, $y, (200 - $x), 20, 20, 'B', 'L');
+
+ // ---------------------------------------------------------
+ // Date :
+ $x = 120;
+ $y += 15;
+
+ $pdf->CreateTextBox('Date : '.date(''.$_SESSION['textes']['all']['all']['date']['format_d_m_y'].''), $x, $y, (200 - $x), 10, 10, '', 'R');
+
+ // ---------------------------------------------------------
+ // Tableau
+ $x = 10;
+ $y += 15;
+ $pdf->Line($x, $y, (210 - $x), $y); // Ligne horizontale
+
+ $x = 10;
+ // $y += 5;
+ $largeur_1 = 90;
+ $largeur_2 = 20;
+ $largeur_3 = 30;
+ $largeur_4 = 20;
+ $largeur_5 = 30;
+ $pdf->CreateTextBox($_SESSION['textes']['fonctions_pdf']['document']['piece']['designation_titre'], $x, $y, $largeur_1, 10, 10, 'B', 'L', 1); // Ligne d'entête
+ $x += $largeur_1;
+ $pdf->CreateTextBox($_SESSION['textes']['fonctions_pdf']['document']['piece']['quantite_titre'], $x, $y, $largeur_2, 10, 10, 'B', 'C', 1);
+ $x += $largeur_2;
+ $pdf->CreateTextBox($_SESSION['textes']['fonctions_pdf']['document']['piece']['pu_ht_titre'], $x, $y, $largeur_3, 10, 10, 'B', 'R', 1);
+ $x += $largeur_3;
+ $pdf->CreateTextBox($_SESSION['textes']['fonctions_pdf']['document']['piece']['taux_tva_titre'], $x, $y, $largeur_4, 10, 10, 'B', 'C', 1);
+ $x += $largeur_4;
+ $pdf->CreateTextBox($_SESSION['textes']['fonctions_pdf']['document']['piece']['pt_ttc_titre'], $x, $y, $largeur_5, 10, 10, 'B', 'R', 1);
+
+ $x = 10;
+ $y += 10;
+ $pdf->Line($x, $y, (210 - $x), $y); // Ligne horizontale
+
+ $x = 10;
+ // $y += 5;
+ for ($i = 0; $i < $piece['nb_lignes']; $i ++)
+ {
+ $pdf->CreateTextBox($lignes[$i]['designation'],
+ $x, $y, $largeur_1, 10, 10, '', 'L');
+ $x += $largeur_1;
+ $pdf->CreateTextBox($lignes[$i]['qte'],
+ $x, $y, $largeur_2, 10, 10, '', 'C');
+ $x += $largeur_2;
+ $pdf->CreateTextBox(number_format($lignes[$i]['pu_ht'], 2, ".", " ").' €',
+ $x, $y, $largeur_3, 10, 10, '', 'R');
+ $x += $largeur_3;
+ $pdf->CreateTextBox(number_format($lignes[$i]['taux_tva'], 2, ".", " ").' %',
+ $x, $y, $largeur_4, 10, 10, '', 'C');
+ $x += $largeur_4;
+ $pdf->CreateTextBox(number_format(($lignes[$i]['qte'] * $lignes[$i]['pu_ht'] * (1 + ($lignes[$i]['taux_tva']/100))), 2, ".", " ").' €',
+ $x, $y, $largeur_5, 10, 10, '', 'R');
+ $x = 10;
+ $y += 5;
+
+ if ($y > 260) // Détection d'un saut de page nécessaire
+ {
+ $y = 30;
+ $pdf->AddPage();
+ }
+ }
+
+ // Ligne horizontale :
+ $x = 10;
+ $y += 5;
+ $pdf->Line($x, $y, (210 - $x), $y);
+
+ // ---------------------------------------------------------
+ // Vérification si un saut de page est nécessaire :
+ if ($y > 260)
+ {
+ $y = 30;
+ $pdf->AddPage();
+ }
+
+ // ---------------------------------------------------------
+ // Totaux :
+ $x = 10;
+ // $y += 5;
+ $pdf->CreateTextBox('TOTAL', $x, $y, $largeur_1, 10, 11, 'B', 'R');
+ $x += $largeur_1;
+ $x += $largeur_2;
+ $pdf->CreateTextBox(number_format($piece['total_ht'], 2, ".", " ").' €',
+ $x, $y, $largeur_3, 10, 11, '', 'R');
+ $x += $largeur_3;
+ $x += $largeur_4;
+ $pdf->CreateTextBox(number_format($piece['total_ttc'], 2, ".", " ").' €',
+ $x, $y, $largeur_5, 10, 11, 'B', 'R');
+
+
+ // ---------------------------------------------------------
+ // Commentaire :
+ if ($piece['commentaire'] != "")
+ {
+ if ($y > 260) // Vérification si un saut de page est nécessaire
+ {
+ $y = 30;
+ $pdf->AddPage();
+ }
+
+ $x = 10;
+ $y += 30;
+ $pdf->SetFont('helvetica', '', 10);
+ $pdf->MultiCell((200 - $x), 10, $piece['commentaire'],
+ 'TRBL', 'J', 0, 1, $x, $y, true, 0, true, true);
+ }
+
+
+ // ---------------------------------------------------------
+ // Ajout des numéros de page :
+ // NB : les fonctions usuelles ('getAliasNumPage()' et 'getAliasNbPages()') dans le Header ou le Footer fonctionnent bien mais posent des soucis d'alignement...
+ $nb_pages = $pdf->getNumPages();
+
+ for($i = 1; $i < ($nb_pages + 1); $i ++)
+ {
+ $pdf->setPage($i);
+ $pdf->SetFont('helvetica', '', 10);
+ $pdf->SetTextColor(0, 69, 99);
+ $pdf->SetXY(150,10);
+ $pdf->Cell(50, 10, 'Page '.$i.'/'.$nb_pages, 0, false, 'R');
+ }
+
+ // ---------------------------------------------------------
+ // Fermeture de l'objet PDF et enreigistrement du fichier :
+ ob_clean(); // Semble nécessaire ; sinon erreur du 'output' suivant
+ $pdf_string = $pdf->Output('pseudo.pdf', 'S'); // export du flux dans une chaine (le nom du fichier est ignoré...)
+
+ // Copie de cette chaine dans le fichier final (cette solution en 2 temps permet d'éviter de devoir utiliser un nom de fichier absolu (semble obligatoire avec Output et l'option 'F')
+ file_put_contents('../../documents/'.$rep.'/'.$piece['no'].'.pdf', $pdf_string);
+
+ return(0);
+}
+
+
+
+class MYPDF2 extends TCPDF {
+ public function Header() {
+ // $this->Image('images/logo_shipheart.png', 10, 10, 60, 0, 'PNG', 'https://shipheart.tech');
+ }
+
+ public function CreateTextBox($textval, $x = 0, $y = 0, $width = 0, $height = 10, $fontsize = 10, $fontstyle = '', $align = 'J', $border = 0) {
+ $this->SetXY($x, $y);
+ $this->SetFont('helvetica', $fontstyle, $fontsize);
+ $this->SetFillColor(255,255,255);
+ $this->MultiCell($width, $height, $textval, 0, $align, false);
+ }
+}
+
+
+//********************************************************************************************************************************
+// Fonction : Génère le document PDF à partir des données reçues en paramètres
+// Reçoit : type du document (1er code généré pour le bateau, code généré par un autre utilisateur)
+// client concerné a qui on envoie le pdf et le code d'accès pour générer le code QR.
+// Retour : 0 si Ok, -1 si erreur
+//********************************************************************************************************************************
+function code_acces_pdf($code, $no, $no_serie = "XX:XX:XX:XX:XX:XX", $client = "")
+{
+ $nom_piece = $no."_Code_acces_bateau_".$no_serie;
+
+
+
+ $pdf = new MYPDF2('LANDSCAPE', 'px', 'A4', true, 'UTF-8', false);
+
+ // set document (meta) information :
+ $pdf->SetCreator(PDF_CREATOR);
+ $pdf->SetAuthor('Koriolan');
+ $pdf->SetTitle($nom_piece);
+ $pdf->SetSubject('ShipHeart');
+ $pdf->SetKeywords('ShipHeart by Koriolan');
+
+ $pdf->SetTextColor(0, 69, 99);
+ $pdf->SetFont('helvetica', '', 10);
+
+ $pdf->AddPage();
+ $pdf->SetDrawColor(217,217,217);
+ $pdf->Line(421, 0, 421, 595);
+
+ // First page
+ $pdf->Image('images/logo_shipheart.png', 506, 214, 252, 167, 'PNG', 'https://shipheart.tech');
+
+ // Firth page
+ $pdf->Image('images/logo_shipheart.png', 12, 12, 64, 45, 'PNG', 'https://shipheart.tech');
+
+ $pdf->CreateTextBox("Nous vous remercions pour votre confiance et sommes ravis de vous offrir un accès complet aux informations de votre bateau via notre platforme Ship2net.
+
+ We thank you for your trust and are delighted to offer you full access to your boat information via our Ship2net platform.", 52, 166, 317, 195, 12);
+
+ $pdf->CreateTextBox($no_serie, 0, 520, 421, 46, 12, '', 'C', 0);
+
+ // $pdf->AddPage();
+ $pdf->SetDrawColor(217,217,217);
+ $pdf->Line(421, 0, 421, 595);
+
+ // Second page
+ $pdf->Image('images/logo_shipheart.png', 12, 12, 64, 45, 'PNG', 'https://shipheart.tech');
+
+ $pdf->Image('images/logo_shipheart.png', 766, 12, 64, 45, 'PNG', 'https://shipheart.tech');
+
+ $pdf->CreateTextBox("Pour activer votre accès au bateau merci de scanner le QR code ci-joint et de vous connecter à votre profil ShipMember existant ou d'en créer un.
+
+ To activate your boat access please scan the attached QR code and log in to your existing ShipMember profile or create one.", 52, 218, 317, 160, 12);
+
+ // Third page
+ $pdf->Image(generateCode($code), 546, 216, 164, 164, 'PNG', 'https://dev.shipheart.tech/shipmembers.php?code_acces='.$code);
+
+
+ // ---------------------------------------------------------
+ // Fermeture de l'objet PDF et enreigistrement du fichier :
+ ob_clean(); // Semble nécessaire ; sinon erreur du 'output' suivant
+ $pdf_string = $pdf->Output('pseudo.pdf', 'S'); // export du flux dans une chaine (le nom du fichier est ignoré...)
+
+ // Copie de cette chaine dans le fichier final (cette solution en 2 temps permet d'éviter de devoir utiliser un nom de fichier absolu (semble obligatoire avec Output et l'option 'F')
+ file_put_contents('temp/PDF/'.$nom_piece.'.pdf', $pdf_string);
+
+ $_SESSION['pdf']['src'] = 'temp/PDF/'.$nom_piece.'.pdf';
+
+ // Sauvegarder le pdf automatiquement
+ header("Cache-Control: public");
+ header("Content-Description: File Transfer");
+ header("Content-Disposition: attachment; filename=".$nom_piece.".pdf");
+ header("Content-Type: application/zip");
+ header("Content-Transfer-Encoding: binary");
+
+ // Read the file
+ readfile($_SESSION['pdf']['src']);
+
+ // Suppression du pdf et codeqr du serveur
+ unlink('temp/CodeQR/'.$code.'.png');
+ unlink($_SESSION['pdf']['src']);
+ return(0);
+}
+
+function generateCode($code){
+ $tempDir = "temp/CodeQR/";
+
+ $codeContents = 'https://shipheart.tech/shipmembers.php?code_acces='.$code;
+
+ $fileName = $code.'.png';
+
+ $pngAbsoluteFilePath = $tempDir.$fileName;
+ $urlRelativeFilePath = $tempDir.$fileName;
+
+ // generating
+ if (!file_exists($pngAbsoluteFilePath)) {
+ QRcode::png($codeContents, $pngAbsoluteFilePath);
+ }
+ return $urlRelativeFilePath;
+}
+
+
diff --git a/config/fonctions_traductions.php b/config/fonctions_traductions.php
new file mode 100644
index 0000000..d877719
--- /dev/null
+++ b/config/fonctions_traductions.php
@@ -0,0 +1,123 @@
+= NIVEAU_EDITEUR_ACCES_ADMINISTATEUR)){
+
+ if ($_SESSION['modifier_texte'] == 1) $texte = $texte.'';
+ }
+
+ return $texte;
+}
+
+
+//************************************************************************************************************
+// Fonction : Récupération dans la base de données du lien du texte (spécial pour les NAVBAR)
+// Reçoit : Nom du fichier, page, bloc et nom
+// Retour : Lien sous la forme d'un '....." permettant l'accès à la modification du texte
+// Navbar spécial ( class update_navbar pour bien afficher)
+//************************************************************************************************************
+function Ajouter_lien_modif($fichier, $page, $bloc, $nom){
+ $texte = "";
+ if (($_SESSION['acces_valide']) && ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_EDITEUR_ACCES_ADMINISTATEUR)){
+
+ if ($_SESSION['modifier_texte'] == 1) $texte = '';
+ }
+
+ return $texte;
+}
+
+
+//************************************************************************************************************
+// Fonction : Récupération dans la base de données du lien du texte (pour les BUTTONS et les HREF)
+// Reçoit : Nom du fichier, page, bloc et nom
+// Retour : Lien sous la forme d'un '....." permettant l'accès à la modification du texte
+// Le reste des buttons et href (suppression de la classe update_navbar)
+//************************************************************************************************************
+function Ajouter_lien_modif_buttons($fichier, $page, $bloc, $nom){
+ $texte = "";
+ if (($_SESSION['acces_valide']) && ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_EDITEUR_ACCES_ADMINISTATEUR)){
+
+ if ($_SESSION['modifier_texte'] == 1) $texte = '';
+ }
+
+ return $texte;
+}
+
+
+//************************************************************************************************************
+// Fonction : Chargement d'une langue. Cela se traduit par charger dans une GROSSE variable de session tous les textes dans la langue choisie.
+// Reçoit : Rien
+// Retour : Rien (mais met à jour la variable de session contenant tous les textes)
+// Attention : doit être rechargé à chaque fois qu'un texte est modifié dans la base
+// Attention 2 : doit être rechargé à chaque fois qu'on change de langue
+//********************************************************************************************************************************
+function chargement_langue()
+{
+
+ Ouverture_bdd(); // Connexion à MySql
+
+ $requete = "SELECT Fichier, Page, Bloc, Nom, (SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TYPE = ".TYPE_UTILISATEUR_PROFESSIONNEL." AND TB_Textes_traduction.ID_TEXTES=TB_Textes_acces.ID_TEXTES) AS Libelle FROM TB_Textes_acces WHERE Validite='1'";
+ $reponse = Requete_preparee_bdd($requete,
+ array(
+ array('s', $_SESSION['langue'])),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse)) // boucle sur tous les enregistrements de la table
+ {
+ $_SESSION['textes'][''.$donnees['Fichier'].''][''.$donnees['Page'].''][''.$donnees['Bloc'].''][''.$donnees['Nom'].''] = stripcslashes($donnees['Libelle']);
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ Fermeture_bdd(); // Déconnexion de MySQL
+}
+
+
+
+
+
+
diff --git a/config/fonctions_utilitaires.php b/config/fonctions_utilitaires.php
new file mode 100644
index 0000000..3f1b0c3
--- /dev/null
+++ b/config/fonctions_utilitaires.php
@@ -0,0 +1,585 @@
+
+
'.Texte_jour_semaine(date("w")).'
+
'.date("d").' '.Texte_mois(date("n")-1).'
+
'.date("Y").'
+
'.date("H:i").'
+
';
+}
+
+
+//************************************************************************************************************
+// Fonction : Affichage de la date en cours
+// Reçoit : Rien
+// Retour : Rien (le résultat est renvoyé directement sur la page par 'echo')
+//************************************************************************************************************
+function Affichage_date()
+{
+ if (!isset($_SESSION['Prise_rendez_vous']['jour_en_cours'])) $_SESSION['Prise_rendez_vous']['jour_en_cours'] = date("Y-m-d");
+ $current_date = $_SESSION['Prise_rendez_vous']['jour_en_cours'];
+
+ // On regarde si le jour en cours est dans ce mois pour le marquer de façon particulière :
+ $month = substr($current_date, 5, 2);
+ $year = substr($current_date, 0, 4);
+ $day = substr($current_date, 8, 2);
+ $time = mktime(0,0,0, $month, $day, $year);
+
+ echo '
';
+}
+
+
+//********************************************************************************************************************************
+// Fonction : Vérifie la longueur d'un paramètre de formulaire en fonction de valeurs mini et maxi reçues et
+// renvoie une chaine d'erreur dans la langue en cours
+// Reçoit : Le titre du champ, la chaine du champs à vérifier, la longueur mini et la longueur maxi
+// Retour : Une chaine d'erreur. Vide si aucune erreur ou message dans la langue en cours
+//********************************************************************************************************************************
+function check_longueur_champ($titre_champ, $champ, $min, $max)
+{
+ $longueur = strlen($champ);
+ $retour = "";
+ if (($longueur < $min) OR ($longueur > $max))
+ {
+ $retour .= '['.$_SESSION['textes']['all']['all']['all'][''.$titre_champ.''].'] ';
+ $retour .= $_SESSION['textes']['all']['all']['erreurs']['longueur_champ_invalide'];
+ $retour .= ' (min = '.$min.' - max = '.$max.')';
+ $retour .= ' ';
+ }
+ return($retour);
+}
+
+
+//************************************************************************************************************
+// Fonction : Affiche le comparatif des versions de ShipHeart qui est utilisé sur la Boutique et sur la page
+// d'Accueil.
+// Reçoit : Rien
+// Retour : Rien (le résultat est renvoyé directement sur la page par 'echo')
+//************************************************************************************************************
+function comparatif_shiphearts()
+{
+
+ $nom_page = basename($_SERVER['SCRIPT_FILENAME']);
+
+ // Variables spécifiques au fichier d'origine :
+ switch($nom_page)
+ {
+ case 'accueil.php' :
+ {
+ $image_coche = "selection_blanc.png";
+ $type_fond = "fond_fonce";
+ break;
+ }
+ case 'boutique.php' :
+ {
+ $image_coche = "selection.png";
+ $type_fond = "fond_clair";
+ break;
+ }
+ default : { $image_coche = "selection_hover.png"; break; }
+ }
+
+ //=========================================================================
+ // Récupération des données :
+ Ouverture_bdd(); // Connexion à MySql
+
+ // Variables spécifiques à la langue :
+ switch($_SESSION['langue'])
+ {
+ case "EN" : { $texte_coche = "Yes"; break; }
+ case "FR" : { $texte_coche = "Oui"; break; }
+ default : { $texte_coche = "langue_non_féfinie"; break; } // Ce texte ne sera jamais trouvé, donc jamais remplacé....
+ }
+
+ $nb_lignes_comparatif = 0;
+ $nb_renvois_comparatif = 0;
+/* $requete = 'SELECT * FROM TB_Comparatif WHERE Validite=1 AND Visible_public=1 ORDER BY Priorite_affichage, Cle_primaire ';
+ // echo "Requete = ".$requete.' ';
+ $reponse = Requete_bdd($requete, __FILE__, __LINE__);
+*/
+ $requete = 'SELECT ID, ';
+ $requete .= 'Renvoi, ';
+ $requete .= '(SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TEXTES=ID_FONCTION) AS Texte, ';
+ $requete .= 'CONCAT(\'{\',GROUP_CONCAT(CONCAT(\'"\',ID_PRODUITS,\'":"\',(SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TEXTES=ID_RESULTAT),\'"\') ORDER BY ID_PRODUITS ASC SEPARATOR ","),"}") AS Produits ';
+ $requete .= 'FROM TB_Galerie_comparatif LEFT JOIN TB_Comparatif ON TB_Galerie_comparatif.ID_COMPARATIF=TB_Comparatif.ID JOIN TB_Comparatif_produits ON TB_Comparatif.ID=TB_Comparatif_produits.ID_COMPARATIF ';
+ $requete .= 'WHERE TB_Galerie_comparatif.Priorite!=0 GROUP BY TB_Comparatif.ID ORDER BY TB_Galerie_comparatif.Priorite ASC';
+ $reponse = Requete_preparee_bdd($requete,
+ array(
+ array('s', $_SESSION['langue']),
+ array('s', $_SESSION['langue'])),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ $lignes_comparatif[$nb_lignes_comparatif]['id'] = $donnees['ID'];
+ $lignes_comparatif[$nb_lignes_comparatif]['fonction'] = $donnees['Texte'];
+
+ // Traitement des renvois de bas de page :
+ if ($donnees['Renvoi'] > 0)
+ {
+ // Recherche si le même texte de renvoi a déjà été utilisé :
+ $renvoi_deja_utilise = 0;
+ for ($i=0; $i<$nb_renvois_comparatif; $i++)
+ {
+ if ($renvois_comparatif[$i]['id'] == $donnees['Renvoi'])
+ {
+ $renvoi_deja_utilise = $renvois_comparatif[$i]['no'];
+ break;
+ }
+ }
+
+ if ($renvoi_deja_utilise != 0) $lignes_comparatif[$nb_lignes_comparatif]['fonction'] .= '['.$renvoi_deja_utilise.']';
+ else
+ {
+ $renvois_comparatif[$nb_renvois_comparatif]['no'] = $nb_renvois_comparatif + 1;
+ $renvois_comparatif[$nb_renvois_comparatif]['id'] = $donnees['Renvoi'];
+ $renvois_comparatif[$nb_renvois_comparatif]['texte'] = nl2br($_SESSION['textes']['boutique']['accueil']['comparatif']['texte_renvoi_'.$donnees['Renvoi'].'']);
+
+ $lignes_comparatif[$nb_lignes_comparatif]['fonction'] .= '['.($nb_renvois_comparatif + 1).']';
+
+ $nb_renvois_comparatif++;
+ }
+ }
+
+ // Remplacement des textes "Oui"/"Yes" par l'image d'une case cochée + suppression de la langue dans la variable :
+ $lignes_comparatif[$nb_lignes_comparatif]['Produits'] = array();
+ foreach(json_decode($donnees['Produits'], true) as $id_produit => $produit)
+ {
+ $lignes_comparatif[$nb_lignes_comparatif]['Produits'][$id_produit] = str_replace($texte_coche, "", $produit);
+ }
+
+ // Demande de regroupement des lignes pour lesquelles les valeurs sont identiques :
+ $lignes_comparatif[$nb_lignes_comparatif]['type_ligne'] = count(array_unique($lignes_comparatif[$nb_lignes_comparatif]['Produits'])) <= 1 ? "unique" : "multiple";
+
+ $nb_lignes_comparatif ++;
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ $requete = "SELECT CONCAT('{',GROUP_CONCAT(DISTINCT CONCAT('\"',ID_PRODUITS,'\":\"',(SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TEXTES=(SELECT ID_NOM FROM TB_Produits WHERE ID=ID_PRODUITS)),'\"') ORDER BY ID_PRODUITS ASC SEPARATOR ','),'}') AS resultat FROM TB_Comparatif_produits";
+ $reponse = Requete_preparee_bdd($requete,
+ array(
+ array('s', $_SESSION['langue'])),
+ __FILE__, __LINE__);
+ $donnees = Recherche_ligne_bdd($reponse);
+ $title_comparatif = json_decode($donnees['resultat'], true);
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ Fermeture_bdd(); // Déconnexion de MySQL
+
+
+ //=========================================================================
+ // Affichage :
+ echo '
';
+
+ //-----------------------------------------------------------------
+ // Bouton de sélection de l'affichage ou non de la table de comparaison :
+ echo '
';
+
+ //-----------------------------------------------------------------
+ // Table principale avec fonctions et valeur pour chaque version :
+ echo '
';
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Convertit une valeur de latitude/longitude décimale en chaine de caratères comprenant Degrés, Minutes et Secondes
+// Reçoit : L'indication "LAT" ou "LON" et la valeur décimale
+// Retour : La chaine convertie
+//************************************************************************************************************
+function Conversion_DECtoDMS($lat_lon, $valeur)
+{
+ if ($lat_lon == "LAT") $direction = $valeur < 0 ? 'S': 'N';
+ elseif ($lat_lon == "LON") $direction = $valeur < 0 ? 'W': 'E';
+ else $direction = "";
+
+ $signe = $valeur < 0 ? '-': '';
+
+ $degres = floor(abs($valeur ?? 0));
+
+ $decimale = (abs($valeur ?? 0)-$degres) * 3600;
+
+ $min = floor($decimale / 60);
+ $sec = round($decimale - ($min * 60), 1);
+
+ return (sprintf("%s%s°%02d'%04.1f\" %s", $signe, $degres, $min, $sec, $direction));
+
+}
+
+
+//************************************************************************************************************
+// Fonction : Enregistrement dans le fichier de log de l'évènement
+// Reçoit : Le texte à afficher dans le log, le fichier d'origine et la ligne d'origine
+// Retour : Rien
+//************************************************************************************************************
+function Enregistrement_log($texte, $fichier, $ligne)
+{
+ $repertoire_logs = REPERTOIRE_LOGS."/www_shipheart";
+ if (!file_exists($repertoire_logs) OR !is_dir($repertoire_logs)) // On vérifie d'abord que le répertoire existe, sinon on le créé.
+ {
+ mkdir($repertoire_logs);
+ $message_information .= "Le répertoire de logs [".$repertoire_logs."] n'existait pas. Il vient d'êtrer crééé ";
+ }
+
+ $fichier_log = $repertoire_logs.'/log_shipheart_'.date("Y-m-d").'.txt';
+ $prefixe_ligne = date("d/m/Y H:i:s")." [".basename($fichier)." - ".$ligne."] \t: ";
+ $suffixe_ligne = "\r\n";
+
+ $log = fopen($fichier_log, "a+"); // Ouverture en lecture/écriture en mode append...
+
+ fputs($log, $prefixe_ligne); // Entete de ligne
+ fputs($log, $texte); // Ecriture du texte de log
+ fputs($log, $suffixe_ligne); // Fin de ligne
+
+ fclose($log); // Fermetue du fichier.
+
+ if(SHOW_DEBUG)
+ {
+ echo $prefixe_ligne . $texte . $suffixe_ligne;
+ }
+}
+
+
+//************************************************************************************************************
+// Fonction : Conversion de format de dates
+// Reçoit : Date à convertir
+// Retour : la chaine mise en forme
+//************************************************************************************************************
+function Formatage_date($date)
+{
+ if ($_SESSION['langue'] == "FR")
+ {
+ switch (substr($date, 5, 2))
+ {
+ case 1: {$mois="janvier"; break;}
+ case 2: {$mois="février"; break;}
+ case 3: {$mois="mars"; break;}
+ case 4: {$mois="avril"; break;}
+ case 5: {$mois="mai"; break;}
+ case 6: {$mois="juin"; break;}
+ case 7: {$mois="juillet"; break;}
+ case 8: {$mois="août"; break;}
+ case 9: {$mois="septembre"; break;}
+ case 10: {$mois="octobre"; break;}
+ case 11: {$mois="novembre"; break;}
+ case 12: {$mois="décembre"; break;}
+ default: $mois = substr($date, 5, 2);
+ }
+
+ $retour = substr($date, 8, 2).' '.$mois.' '.substr($date, 0, 4);
+
+ $suffixe_heure = 'à';
+ }
+ else
+ {
+ switch (substr($date, 5, 2))
+ {
+ case 1: {$mois="January"; break;}
+ case 2: {$mois="February"; break;}
+ case 3: {$mois="March"; break;}
+ case 4: {$mois="April"; break;}
+ case 5: {$mois="May"; break;}
+ case 6: {$mois="June"; break;}
+ case 7: {$mois="July"; break;}
+ case 8: {$mois="August"; break;}
+ case 9: {$mois="September"; break;}
+ case 10: {$mois="October"; break;}
+ case 11: {$mois="November"; break;}
+ case 12: {$mois="December"; break;}
+ default: $mois = substr($date, 5, 2);
+ }
+
+ $jour = ltrim(substr($date, 8, 2), "0"); // Suppression du zéro initial s'il y en a un...
+ switch($jour)
+ {
+ case 1:
+ case 21:
+ case 31: { $suffixe_jour = 'st'; break; }
+ case 2:
+ case 22: { $suffixe_jour = 'nd'; break; }
+ case 3:
+ case 23: { $suffixe_jour = 'rd'; break; }
+ default: { $suffixe_jour = 'th'; break; }
+ }
+
+ $retour = $mois.' '.$jour.''.$suffixe_jour.', '.substr($date, 0, 4);
+
+ $suffixe_heure = 'at';
+ }
+
+ if (strpos($date, ' ') !== false) $retour .= ' '.$suffixe_heure.' '.substr($date, 11); // Ajout de l'heure si la chaine d'entrée comprend un espace (entre date et heure)
+
+ return($retour);
+}
+
+
+//********************************************************************************************************************************
+// Fonction : Recherche dans le BDD le prochain numéro de document, le formatte, et retourne le n° formatté du nouveau document.
+// Reçoit : le type de document (cd dans definition.php) et l'année concernée
+// Retour : Le numéro formatté du nouveau document, -1 si erreur de paramètres, -2 si dépassement de numérotation
+// ATTENTION : l'accès à la BDD doit être ouvert AVANT d'accéder à cette fonction.
+// NB : on pourrait l'ouvrir dedans mais on ne sait alors pas s'il faut la refermer...
+//********************************************************************************************************************************
+function nouveau_numero_document($type, $annee)
+{
+ $nb_digits_numerotation = 4;
+ $erreur = 0;
+
+
+ // Contrôles et définition du préfixe au passage :
+ switch($type)
+ {
+ case NUM_TYPE_DEVIS : { $prefixe = "D"; $fonctionRequete = ""; break; }
+ case NUM_TYPE_COMMANDE : { $prefixe = "C"; $fonctionRequete = "porchainNoCommande"; break; }
+ case NUM_TYPE_FACTURE : { $prefixe = "F"; $fonctionRequete = "porchainNoFacture"; break; }
+ case NUM_TYPE_AVOIR : { $prefixe = "A"; $fonctionRequete = ""; break; }
+ default: return (-1);
+ }
+
+ // Recherche d'une numérotation existante :
+ $dernier_no = -1;
+ $requete = "SELECT $fonctionRequete(?) AS No";
+ $reponse = Requete_preparee_bdd($requete,
+ array(
+ array('i', $annee)),
+ __FILE__, __LINE__);
+ $donnees = Recherche_ligne_bdd($reponse);
+ $dernier_no = $donnees['No'];
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ // Formattage du n° de document :
+ if ($erreur != 0) return($erreur);
+ else return($prefixe.($annee-2000).str_pad($dernier_no, $nb_digits_numerotation, '0', STR_PAD_LEFT));
+}
+
+
+//************************************************************************************************************
+// Fonction : Affichage du nom du jour de la semaine en français en version courte (3 lettres)
+// Reçoit : Jour à convertir (entre 0: dimanche et 6: samedi voire 7:dimanche)
+// Retour : Le texte court du jour en français
+//************************************************************************************************************
+function Texte_court_jour_semaine($jour)
+{
+
+ switch ($jour)
+ {
+ case 0: {$texte_jour_semaine="Dim"; break;}
+ case 1: {$texte_jour_semaine="Lun"; break;}
+ case 2: {$texte_jour_semaine="Mar"; break;}
+ case 3: {$texte_jour_semaine="Mer"; break;}
+ case 4: {$texte_jour_semaine="Jeu"; break;}
+ case 5: {$texte_jour_semaine="Ven"; break;}
+ case 6: {$texte_jour_semaine="Sam"; break;}
+ case 7: {$texte_jour_semaine="Dim"; break;}
+ default: $texte_jour_semaine = "";
+ }
+ return($texte_jour_semaine);
+}
+
+
+//************************************************************************************************************
+// Fonction : Affichage du nom du jour de la semaine en français
+// Reçoit : Jour à convertir (entre 0: dimanche et 6: samedi voire 7:dimanche)
+// Retour : Le texte du jour en français
+//************************************************************************************************************
+function Texte_jour_semaine($jour)
+{
+
+ switch ($jour)
+ {
+ case 0: {$texte_jour_semaine="dimanche"; break;}
+ case 1: {$texte_jour_semaine="lundi"; break;}
+ case 2: {$texte_jour_semaine="mardi"; break;}
+ case 3: {$texte_jour_semaine="mercredi"; break;}
+ case 4: {$texte_jour_semaine="jeudi"; break;}
+ case 5: {$texte_jour_semaine="vendredi"; break;}
+ case 6: {$texte_jour_semaine="samedi"; break;}
+ case 7: {$texte_jour_semaine="dimanche"; break;}
+ default: $texte_jour_semaine = "";
+ }
+ return($texte_jour_semaine);
+}
+
+
+//************************************************************************************************************
+// Fonction : Affichage du nom du mois en français
+// Reçoit : Mois à convertir (entre 0: janvier et 11: décembre)
+// Retour : Le texte du mois en français
+//************************************************************************************************************
+function Texte_mois($mois)
+{
+
+ switch ($mois)
+ {
+ case 0: {$texte_mois="janvier"; break;}
+ case 1: {$texte_mois="février"; break;}
+ case 2: {$texte_mois="mars"; break;}
+ case 3: {$texte_mois="avril"; break;}
+ case 4: {$texte_mois="mai"; break;}
+ case 5: {$texte_mois="juin"; break;}
+ case 6: {$texte_mois="juillet"; break;}
+ case 7: {$texte_mois="août"; break;}
+ case 8: {$texte_mois="septembre"; break;}
+ case 9: {$texte_mois="octobre"; break;}
+ case 10: {$texte_mois="novembre"; break;}
+ case 11: {$texte_mois="décembre"; break;}
+ default: $texte_mois = "";
+ }
+ return($texte_mois);
+}
+
+
+//************************************************************************************************************
+// Fonction : Vérification d'un numero de téléphone + formatage
+// Reçoit : Chaine représentant le numéro
+// Retour : Le numéro formaté soud la forme "06 12 34 56 78 90"
+//************************************************************************************************************
+function Verification_numero_telephone($chaine_numero)
+{
+ $numero_ok = true;
+ do
+ {
+ switch (strlen($chaine_numero))
+ {
+ case 10 : // Aucun séparateur n'est présent dans la chaine reçue :
+ {
+ for ($i=0; $i<5; $i++) $numeros[$i] = substr($chaine_numero, ($i*2), 2);
+ $chaine_numero = $numeros[0];
+ for ($i=1; $i<5; $i++) $chaine_numero .= ' '.$numeros[$i]; // Ajout des ' ' comme séparateur
+ break;
+ }
+ case 14 : // Un séparateur existe entre chaque groupe de deux chiffres :
+ {
+ for ($i=1; $i<5; $i++) $chaine_numero[($i*3)-1] = ' '; // Remplacement des séparateurs existants par des ' '
+ break;
+ }
+ default : // Comibinaison de nombre de séparateurs ou de chiffres incohérent :
+ {
+ $numero_ok = false;
+ break;
+ }
+ }
+ if (! $numero_ok) break; // On sort de la boucle DO si une erreur a déjà été trouvée.
+
+ // On décompose les 5 groupes :
+ $numeros = explode(" ", $chaine_numero);
+ if (count($numeros) != 5) {$numero_ok = false; break;}
+ foreach($numeros as $n)
+ {
+ if (strlen($n) != 2) {$numero_ok = false; break;}
+ if (($n < 0) OR ($n > 99)) {$numero_ok = false; break;}
+ }
+ if (($numeros[0] < 1) OR ($numeros[0] > 9)) {$numero_ok = false; break;}
+ } while (0);
+
+ if ($numero_ok) return($chaine_numero);
+ else return ("Numéro non valide");
+}
+
diff --git a/config/lang/en.php b/config/lang/en.php
new file mode 100644
index 0000000..e026469
--- /dev/null
+++ b/config/lang/en.php
@@ -0,0 +1,219 @@
+ '[Email] is invalid!',
+ '[Email] est déjà utilisée par un ShipMember. Si vous êtes le propriétaire de cette adresse, merci de vous connecter avec cette adresse. Sinon, merci de choisir une autre adresse.'
+ => '[Email] is already in use by a ShipMember. If you are the owner of this email address, please log in using it. Otherwise, please choose a different email address.',
+ '
Félicitations !
+
Votre compte ShipMember est créé, vous y êtes presque.
+
Nom : [[Prenom]] [[Nom]]
+
Email : [[Email]]
+
Mot de passe provisoire : [[Password]]
+
Plus qu’un clic : Connectez-vous sur ShipHeart.tech pour valider votre compte ShipMember en modifiant votre mot de passe provisoire (*). C’est une garantie de sécurité à ne pas négliger.
+
Et ensuite, où que vous soyez et dès que votre ShipHeart est installé, accédez à votre bateau via votre accès privilégié ShipMember !
+
+
Bienvenue parmi nous !
+
L\'équipage ShipHeart
+
+
(*) L\'activation devra être faite avant le [[Date_maxi_activation]]. Mais pas d\'inquiétude ; si vous ne le faites pas avant, vous pourrez toujours demander un nouveau mot de passe provisoire.
'
+ => '
Congratulations!
+
Your ShipMember account has been created – you’re almost there.
+
Surname: [[First name]] [[Surname]]
+
Email: [[Email]]
+
Temporary password: [[Password]]
+
Just one more click: Log in to ShipHeart.tech to verify your ShipMember account by changing your temporary password (*). This is an important security measure that should not be overlooked.
+
And then, wherever you are and as soon as your ShipHeart is installed, access your boat via your exclusive ShipMember account!
+
+
Welcome on board!
+
The ShipHeart crew
+
+
(*) Activation must be completed by [[Date_maxi_activation]]. But don’t worry; if you don’t manage to do so by then, you can always request a new temporary password.
',
+ 'Votre accès ShipMember - Un dernier clic pour accéder à votre bateau !'
+ => 'Your ShipMember login – Just one more click to access your boat!',
+ 'Un email vient d\'être envoyé à [[Prenom]] [[Nom]] ([[Email]]) avec un mot de passe provisoire.'
+ => 'An email has just been sent to [[First name]] [[Surname]] ([[Email]]) containing a temporary password.',
+ 'Désolé, votre compte n\'a pas été activé dans le délai maximal. Vous pouvez demander la génération d\'un nouveau mot de passe provisoire en cliquant sur le bouton ci-dessous. Il vous sera adressé immédiatement par email.'
+ => 'Sorry, your account has not been activated within the maximum time limit. You can request a new temporary password by clicking the button below. It will be sent to you immediately by email.',
+ 'Bienvenue !'
+ => 'Welcome!',
+ 'Pour des raisons de sécurité, votre nouveau mot de passe ne peut pas être identique au mot de passe provisoire.'
+ => 'For safety reason, your new password can\'t be the same than the temporary password.',
+ 'Félicitations : votre compte est maintenant activé et vous êtes d\'ores et déjà connecté.e.'
+ => 'Congratulations: your account is now active and you\'re already logged in.',
+ 'Les deux mots de passe saisis doivent être identiques.'
+ => 'Both passwords shall be the same.',
+ 'Le mot de passe renseigné n\'est pas conforme. Pour la sécurité de vos données, il doit contenir entre 6 et 20 caractères, et être composé exclusivement de minuscules, de majuscules, de chiffres ou des caractères spéciaux suivants _-!*=.'
+ => 'The password you have entered not valid. To insure the security of your data, it must contain between 6 and 20 characters, and must consist exclusively of lower-case letters, upper-case letters, numbers or the following special characters _-!*=.',
+ '
Bonjour,
+
Le mot de passe de votre compte ShipMember vient d\'être réinitialisé à votre demande.
+
Nom : [[Prenom]] [[Nom]]
+
Email : [[Email]]
+
Mot de passe provisoire : [[Password]]
+
Plus qu’un clic : Connectez-vous sur Ship2net.fr pour activer votre compte ShipMember en modifiant votre mot de passe provisoire (*). C’est une garantie de sécurité à ne pas négliger...
+
Bienvenue à nouveau parmi nous !
+
L\'équipage ShipHeart
+
+
(*) L\'activation devra être faite avant le [[Date_maxi_activation]]. Mais pas d\'inquiétude ; si vous ne le faites pas avant, vous pourrez toujours demander un nouveau mot de passe provisoire.
'
+ => '
Hello,
+
The password for your ShipMember account has just been reset at your request.
+
Surname: [[First name]] [[Surname]]
+
Email: [[Email]]
+
Temporary password: [[Password]]
+
Just one more click: Log in to Ship2net.fr to activate your ShipMember account by changing your temporary password (*). This is an important security measure that should not be overlooked...
+
Welcome back!
+
The ShipHeart team
+
+
(*) Activation must be completed by [[Date_maxi_activation]]. But don’t worry; if you don’t manage to do so by then, you can always request a new temporary password.
',
+ 'Votre demande concernant votre accès ShipMember'
+ => 'Your enquiry regarding your ShipMember access',
+ 'Un email vient d\'être envoyé à [[Prenom]] [[Nom]] ([[Email]]) avec les informations relatives à votre inscription.'
+ => 'An email has just been sent to [[First name]] [[Surname]] ([[Email]]) containing details of your registration.',
+ '
Bonjour,
+
L\'adresse email associée à votre compte ShipMember vient d\'être modifiée à votre demande :
+
Ancienne adresse : [[Old_email]]
Nouvelle adresse : [[New_email]]
+
Si vous n\'êtes pas à l\'origine de ce changement d\'adresse, merci de contacter sans attendre l\'administrateur du site à : [[Adresse_email_alertes]].\'
+
L\'équipage ShipHeart
'
+ => '
Hello,
+
The email address associated with your ShipMember account has just been changed at your request:
+
Old address: [[Old_email]]
New address: [[New_email]]
+
If you did not request this change of address, please contact the site administrator immediately at: [[Alert_email_address]].\'
+
The ShipHeart team
',
+ 'Modifications de votre accès ShipMember'
+ => 'Changes to your ShipMember access',
+ 'Connectez-vous'
+ => 'Log in',
+ 'Déjà ShipMember ?'
+ => 'Already ShipMember?',
+ 'Merci de vous identifier :'
+ => 'Thank you for logging in:',
+ 'Email'
+ => 'Email',
+ 'Saisissez votre adresse email...'
+ => 'Enter your email address...',
+ 'Mot de passe'
+ => 'Password',
+ 'Saisissez votre mot de passe...'
+ => 'Enter your password...',
+ 'Valider'
+ => 'Confirm',
+ 'Générer un nouveau mot de passe'
+ => 'Generate a new password',
+ 'Pas encore ShipMember ?'
+ => 'Not a ShipMember yet?',
+ 'Merci de renseigner vos coordonnées ; votre espace ShipMember sera créé immédiatement :'
+ => 'Please fill in your details; your ShipMember account will be created immediately.',
+ 'Prénom'
+ => 'First name',
+ 'Nom'
+ => 'Last name',
+ 'Entreprise'
+ => 'Company',
+ 'Numéro et rue'
+ => 'Number and street',
+ 'Numéro et nom de voie, bâtiment, appartement, lotissement, ...'
+ => 'Number and name of lane, building, flat, housing estate,...',
+ 'Code postal'
+ => 'Postcode',
+ 'Ville'
+ => 'City',
+ 'Pays'
+ => 'Country',
+ 'Téléphone'
+ => 'Phone',
+ 'Mon espace ShipMember'
+ => 'My ShipMember account',
+ 'Activation de mon compte'
+ => 'Activation of my account',
+ 'Votre compte n\'a pas encore été activé. Pour cela, merci de changer votre mot de passe provisoire ci-dessous.'
+ => 'Your account has not yet been activated. Please change your temporary password below.',
+ 'Nouveau mot de passe'
+ => 'New password',
+ 'Entre 6 et 20 caractères, et être composé exclusivement de minuscules, majuscules, de chiffres ou des caractères spéciaux suivants _-!*=.'
+ => 'Between 6 and 20 characters, and must consist exclusively of lower-case letters, upper-case letters, numbers or the following special characters _-!*=.',
+ 'Nouveau mot de passe (2)'
+ => 'New password (2)',
+ 'Saisissez à nouveau votre nouveau mot de passe...'
+ => 'Re-enter your new password...',
+ 'Générer un nouveau mot de passe provisoire'
+ => 'Generate a new temporary password',
+ 'Renseignez votre adresse email puis confirmez votre demande. Si votre adresse existe bien dans la liste des ShipMembers, un email vous sera adressé immédiatement avec un nouveau mot de passe provisoire.'
+ => 'Fill in your email address and confirm your request. If your address exists in the ShipMembers list, an email will be sent to you immediately with a new temporary password.',
+ 'Attention : dès validation, seul ce nouveau mot de passe provisoire sera valide pour vous connecter. Si vous n\'avez pas reçu cet email dans les prochaines minutes, merci de vérifier s\'il ne se trouve pas dans vos courriers indésirables. '
+ => 'Caution: as soon as you confirm, only this new temporary password will be valid to connect you. If you have not received this email within the next few minutes, please check your spam folder.',
+ 'Valider votre saisie...'
+ => 'Confirm your entry...',
+ 'Bienvenue dans votre espace ShipMember !'
+ => 'Welcome to your ShipMember area !',
+ 'Vous trouverez ici toutes les informations que nous conservons vous concernant. Notre objectif est de ne conserver que celles nécessaires au traitement de vos commandes et, surtout, vous permettant de profiter pleinement de toutes les fonctions de votre ShipHeart.'
+ => 'You\'ll find here all the information we keep about you. Our aim is to keep only the information necessary to process your orders and, above all, to allow you to take full advantage of all the functions of your ShipHeart.',
+ 'Enfin, nous vous rappelons que Koriolan ne diffuse et ne diffusera jamais la moindre de ces informations à d\'autres sociétés. Pour plus de précisions sur le traitement de vos données personnelles, n\'hésitez pas à consulter nos mentions légales accessibles en bas de cette page.'
+ => 'Finally, we would like to remind you that Koriolan does not and will never distribute any of this information to other companies. For more information on the processing of your personal data, please consult our legal notice at the bottom of this page.',
+ 'Voir les informations liées à votre compte ShipMember...'
+ => 'View your ShipMember account information...',
+ 'Mes informations'
+ => 'My information',
+ 'Accéder à mon bateau...'
+ => 'Getting to my boat...',
+ 'Mon bateau'
+ => 'My boat',
+ 'Fermer votre session...'
+ => 'Close your session...',
+ 'Déconnexion'
+ => 'Logout',
+ 'Numéro de ShipMember'
+ => 'ShipMember number',
+ 'Accéder au menu de modification de vos informations...'
+ => 'Go to the menu for changing your details...',
+ 'Modifier'
+ => 'Edit',
+ 'Retour à mon espace ShipMember...'
+ => 'Back to my ShipMember account...',
+ 'Retour'
+ => 'Back',
+ 'Modifier mes informations'
+ => 'Edit my details',
+ 'Si vous souhaitez modifier votre mot de passe, entrez votre nouveau mot de passe deux fois ci-dessous. Sinon, laissez ces deux champs vides.'
+ => 'If you wish to change your password, enter your new password twice below. Otherwise, leave these two fields blank.',
+ 'Saisissez votre nouveau mot de passe...'
+ => 'Enter your new password...',
+ 'Annuler la saisie...'
+ => 'Cancel your entry...',
+ 'Annuler'
+ => 'Cancel',
+ 'Accès à mes bateaux'
+ => 'Access to my boats',
+ 'Accès à mon bateau'
+ => 'Access to my boat',
+ 'Vous n\'avez pas encore de ShipHeart activé.'
+ => 'You do not yet have a ShipHeart activated.',
+ 'Mais dès que ce sera le cas, vous trouverez ici l\'accès à votre ou vos bateaux.'
+ => 'But as soon as that happens, you’ll find access to your boat or boats here.',
+ 'Pour chacun d\'eux, vous visualiserez l\'état de tous les circuits, leur consommation, le niveau des réservoirs et des batteries, mais aussi toutes les valeurs de vos capteurs : températures, pression atmosphérique, humidité, position géographique, etc... Vous aurez également accès aux courbes historiques de toutes ces valeurs.'
+ => 'For each one, you will be able to view the status of all circuits, their power consumption, the levels of the tanks and batteries, as well as all the readings from your sensors: temperatures, atmospheric pressure, humidity, geographical position, etc... You will also have access to historical trends for all these readings.',
+ 'C\'est également ici que vous pourrez créer des profils [Propriétaire], [Installateur] ou [Skipper] afin de donner accès de façon contrôlée à votre ou à vos bateaux. Sans aucune limite !'
+ => 'This is also where you can create [Owner], [Installer] or [Skipper] profiles to grant controlled access to your boat or boats. There are no limits!',
+ 'Revenir à mon espace ShipMember...'
+ => 'Back to my ShipMember dashboard...',
+ 'ShipHeart by Koriolan, la première centrale de vie à bord pour les bateaux de plaisance'
+ => 'ShipHeart by Koriolan, the first on-board life support system for pleasure craft',
+ 'Retour à l\'accueil'
+ => 'Back to home page...',
+ 'Switch to English...'
+ => 'Revenir en français...',
+ 'Accès validé pour '
+ => 'Access granted to ',
+ 'ShipHeart ™ est une marque de Koriolan'
+ => 'ShipHeart™ is a brand of Koriolan',
+ 'Accéder à notre site Koriolan'
+ => 'Visit our Koriolan website',
+ 'Mentions légales'
+ => 'Legal notices',
+];
+
+/*
+// OBSOLÈTES au 25/09/2026 à 16:53:47 - plus utilisées dans le code.
+// À supprimer manuellement une fois confirmé qu'elles ne servent vraiment plus.
+'Bienvenue à bord' => 'Welcome aboard',
+*/
diff --git a/extraire_traductions.php b/extraire_traductions.php
new file mode 100644
index 0000000..9637905
--- /dev/null
+++ b/extraire_traductions.php
@@ -0,0 +1,89 @@
+getFilename(), ".php")) continue;
+ if (str_contains($fichier->getPathname(), "/lang/")) continue; // ne pas s'auto-analyser
+ if ($fichier->getFilename() === basename(__FILE__)) continue; // idem, pour le script lui-même
+ $contenu = file_get_contents($fichier->getPathname());
+ if (preg_match_all('/\bt\(\s*"((?:[^"\\\\]|\\\\.)*)"\s*\)/', $contenu, $matches)) {
+ foreach ($matches[1] as $chaine) {
+ $chaine = stripcslashes($chaine);
+ $chaines_trouvees[$chaine][] = $fichier->getPathname();
+ }
+ }
+}
+
+$traductions_en = file_exists($fichier_en) ? include $fichier_en : [];
+
+// --- Rapport console, comme avant ---
+echo "=== Chaînes utilisées dans le code : " . count($chaines_trouvees) . " ===\n\n";
+
+$manquantes = [];
+foreach ($chaines_trouvees as $chaine => $fichiers) {
+ if (!array_key_exists($chaine, $traductions_en)) {
+ $manquantes[$chaine] = $fichiers[0];
+ }
+}
+echo count($manquantes) . " nouvelle(s) chaîne(s) à traduire.\n";
+
+$obsoletes = array_diff(array_keys($traductions_en), array_keys($chaines_trouvees));
+echo count($obsoletes) . " entrée(s) obsolète(s), déplacée(s) en commentaire.\n\n";
+
+// --- Régénération de lang/en.php ---
+
+// Sauvegarde de précaution avant d'écraser :
+if (file_exists($fichier_en)) {
+ copy($fichier_en, "$fichier_en.bak");
+}
+
+// Fusion : traductions existantes conservées pour les chaînes encore utilisées,
+// nouvelles chaînes ajoutées avec une valeur vide :
+$nouveau_tableau = [];
+foreach ($chaines_trouvees as $chaine => $fichiers) {
+ $nouveau_tableau[$chaine] = $traductions_en[$chaine] ?? "";
+}
+
+$contenu = " $valeur) {
+ $contenu .= " " . var_export($cle, true) . " => " . var_export($valeur, true) . ",\n";
+}
+$contenu .= "];\n";
+
+if (!empty($obsoletes)) {
+ $contenu .= "\n/*\n";
+ $contenu .= "// OBSOLÈTES au " . date("d/m/Y à H:i:s") . " - plus utilisées dans le code.\n";
+ $contenu .= "// À supprimer manuellement une fois confirmé qu'elles ne servent vraiment plus.\n";
+ foreach ($obsoletes as $chaine) {
+ $contenu .= var_export($chaine, true) . " => " . var_export($traductions_en[$chaine], true) . ",\n";
+ }
+ $contenu .= "*/\n";
+}
+
+file_put_contents($fichier_en, $contenu);
+echo "Fichier régénéré : $fichier_en\n";
+echo "(sauvegarde de l'ancienne version : $fichier_en.bak)\n";
diff --git a/html/.htaccess b/html/.htaccess
new file mode 100644
index 0000000..2aafef8
--- /dev/null
+++ b/html/.htaccess
@@ -0,0 +1,2 @@
+# Activation du HSTS :
+#Header set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" env=HTTPS
diff --git a/html/administration.php b/html/administration.php
new file mode 100644
index 0000000..0a4db70
--- /dev/null
+++ b/html/administration.php
@@ -0,0 +1,461 @@
+'.$bateaux[$i]['nb_elements'].' : ID = '.$donnees['ID'].'
';
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+ }
+
+ //-------------------------------------------------------------------
+ // Boucle sur tous les bateaux pour traiter les entrées redondantes:
+ $nb_total_lignes_supprimees = 0;
+ for ($i = 0; $i < $nb_bateaux ; $i ++)
+ {
+ // Boucle sur tous les éléments :
+ for ($j = 0; $j < $bateaux[$i]['nb_elements']; $j ++)
+ {
+ $nb_redondants = 0;
+ $val_1 = -1;
+ $val_2 = 0;
+ $id_2 = 0;
+ $requete = 'SELECT Cle_primaire, Date, Valeur FROM '.$nom_table.' WHERE ID_Bateau=? AND ID=? ORDER BY Date ';
+ $reponse = Requete_preparee_bdd($requete,
+ array(
+ array('i', $bateaux[$i]['ID_Bateau']),
+ array('i', $bateaux[$i][$j])),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ // Recherche des conditions de redondance :
+ if (($val_1 == $val_2) AND ($val_2 == $donnees['Valeur'])) // Si la valeur actuelle est égale à la précédente et à celle encore avant :
+ {
+ $redondants[$nb_redondants ++] = $id_2; // ... on mémorise l'entrée intermédiaire (N-1)
+ $nb_total_lignes_supprimees ++;
+ if ($nb_total_lignes_supprimees >= $max_lignes_traitees) break;
+ }
+
+ $val_1 = $val_2;
+ $val_2 = $donnees['Valeur'];
+ $id_2 = $donnees['Cle_primaire'];
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+
+ // Suppression des enregistrements redondants :
+ for ($k = 0; $k < $nb_redondants; $k ++)
+ {
+ // Création d'une chaine avec les différentes clés à supprimer afin de réduire le nombre de requêtes DELETE (impact sur temps de traitement d'environ / 50 avec la valeur de 100) :
+ $max_l = 100;
+ $chaine_cles_primaires = '(Cle_primaire='.$redondants[$k ++].' ';
+ if ($k < ($nb_redondants - 1))
+ {
+ for ($l = 1; $l < $max_l; $l ++)
+ {
+ $chaine_cles_primaires .= 'OR Cle_primaire='.$redondants[$k].' ';
+ if (($k >= ($nb_redondants - 1)) OR ($l >= ($max_l - 1))) break;
+ $k ++;
+ }
+ }
+ $chaine_cles_primaires .= ')';
+
+ $requete = 'DELETE FROM '.$nom_table.' WHERE ID_Bateau='.$bateaux[$i]['ID_Bateau'].' AND '.$chaine_cles_primaires.' ';
+// echo 'Requête = ['.$requete.'] ';
+ Requete_bdd($requete, __FILE__, __LINE__); // Pas de requête préparée pour accélérer le traitement...
+ }
+ if ($nb_total_lignes_supprimees >= $max_lignes_traitees) break;
+ }
+ if ($nb_total_lignes_supprimees >= $max_lignes_traitees) break;
+ }
+
+ // Recherche de la taille de la table après traitement :
+ $requete = 'SELECT COUNT(*) AS Nb FROM '.$nom_table.' ';
+ $reponse = Requete_bdd($requete, __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ $taille_table_apres_traitement = $donnees['Nb'];
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ // Calculate script execution time
+ $execution_time = number_format((microtime(true) - $start_time), 1, ".", " ");
+
+
+
+ $tables[$x]['Taille_avant_traitement'] = $taille_table_avant_traitement;
+ $tables[$x]['Nb_total_lignes_supprimees'] = $nb_total_lignes_supprimees;
+ $tables[$x]['Execution_time'] = $execution_time;
+ $tables[$x]['Taille_apres_traitement'] = $taille_table_apres_traitement;
+
+
+ }
+
+
+
+ break;
+ }
+
+ //****************************************************************************************************************************
+ case "Questionnaires":
+ {
+ switch($_SESSION['Administration']['questionnaire'])
+ {
+ default:
+ {
+ $message_erreur .= "Le questionnaire ".$_SESSION['Administration']['questionnaire']." est inconnu. Le questionnaire en cours est repassé à sa valeur par défaut.";
+ $_SESSION['Administration']['questionnaire'] = "Nautic2021";
+ }
+ case "Nautic2021" :
+ {
+ $table_questionnaire = "TB_Nautic2021";
+
+ break;
+ }
+ }
+
+ // Nombre de réponses :
+ $requete = 'SELECT COUNT(*) AS Nb FROM '.$table_questionnaire.' WHERE Validite=1';
+ $reponse = Requete_preparee_bdd($requete,
+ array(),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ $reponses_questionnaire['Nb'] = $donnees['Nb'];
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+
+ // Dernier enregistrement :
+ $requete = 'SELECT DATE_FORMAT(Date_creation,"%d/%m/%Y à %H:%i:%s") as Date_creation FROM '.$table_questionnaire.' WHERE Validite=1 ORDER BY Date_creation DESC LIMIT 1';
+// echo 'Requête = '.$requete.' ';
+ $reponse = Requete_preparee_bdd($requete,
+ array(),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ $reponses_questionnaire['Date_creation'] = $donnees['Date_creation'];
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+ // Nombre d'enregistrements par jour :
+ $nb_jours = 0;
+ $requete = 'SELECT COUNT(*) as Nb_jours, DATE_FORMAT(Date_creation, "%d-%m-%Y") as Date_creations FROM '.$table_questionnaire.' WHERE Validite=1 GROUP BY DATE_FORMAT(Date_creation, "%Y-%m-%d")';
+// echo 'Requête = '.$requete.' ';
+ $reponse = Requete_preparee_bdd($requete,
+ array(),
+ __FILE__, __LINE__);
+ while ($donnees = Recherche_ligne_bdd($reponse))
+ {
+ $reponses_questionnaire['Creations_par_jour'][$nb_jours ++] = $donnees;
+ }
+ Liberation_jeu_de_resultat_bdd($reponse);
+
+
+
+ break;
+ }
+
+
+
+ }
+
+ Fermeture_bdd(); // Déconnexion de MySQL
+
+//************************************************************************************************************************************
+// Affichage
+//************************************************************************************************************************************
+
+
+ //********************************************************************************************************************************
+ // Affichage principal :
+ echo '
Taille de la table AVANT traitement = '.number_format($tables[$x]['Taille_avant_traitement'], 0, ".", " ").' lignes
';
+ echo '
Le nombre total de lignes supprimées est de '.number_format($tables[$x]['Nb_total_lignes_supprimees'], 0, ".", " ").' (maxi = '.number_format($max_lignes_traitees, 0, ".", " ").') en '.$tables[$x]['Execution_time'].' secondes
';
+ echo '
Taille de la table APRES traitement = '.number_format($tables[$x]['Taille_apres_traitement'], 0, ".", " ").' lignes
Off';
+ elseif ($bateau['details_bateau'][$i]['Etat_commande_carte'] == 2) echo 'A DEFINIR en fonction de la valeur de Mode_test de la carte de commande';
+ elseif ($bateau['details_bateau'][$i]['Consigne'] == 0) echo 'capteur_off "> Off';
+ else
+ {
+ echo 'capteur_on "> On';
+ echo '
'.number_format($bateau['details_bateau'][$i]['Intensite'], 2, ".", " ").' A
';
+ if (($bateau['details_bateau'][$i]['Consigne'] > 0) AND
+ ($bateau['details_bateau'][$i]['Consigne'] < 100)) echo '['.$bateau['details_bateau'][$i]['Consigne'].' %]';
+ }
+ echo '
';
+ if ($bateau['details_bateau'][$i]['Etat_commande_carte'] == 1) echo 'Off';
+ elseif ($bateau['details_bateau'][$i]['Etat_commande_carte'] == 2) echo 'A DEFINIR en fonction de la valeur de Mode_test de la carte de commande';
+ elseif ($bateau['details_bateau'][$i]['Consigne'] == 0) echo 'Off';
+ else
+ {
+ echo 'On';
+ if (($bateau['details_bateau'][$i]['Consigne'] > 0) AND
+ ($bateau['details_bateau'][$i]['Consigne'] < 100)) echo '['.$bateau['details_bateau'][$i]['Consigne'].' %]';
+ }
+ echo '
';
+
+ // Données spécifiques en fonction du type de carte :
+ echo ' ';
+ switch($cartes[$i]['Type'])
+ {
+ //---------------------------
+ case TYPE_CARTE_SP812:
+ case TYPE_CARTE_SP8DC:
+ {
+ // Circuits affectés à ce module :
+ for ($j = 0; $j < 8; $j++)
+ {
+ echo '
+
Circuit n° '.($j + 1).'
+
'.$cartes[$i]['circuits'][$j]['Nom'].'
+
';
+ }
+
+ break;
+ }
+
+ //---------------------------
+ case TYPE_CARTE_SP4AC:
+ {
+ // Circuits affectés à ce module :
+ for ($j = 0; $j < 4; $j++)
+ {
+ echo '
+ Confirmez-vous la suppression de la carte
+ '.$types_carte[$_SESSION['Cartes']['carte']['Type']].'
+ n°
+ '.$_SESSION['Cartes']['carte']['No_serie'].' ?
+
+
+
+ ';
+
+ // Affichage des variables de session :
+ echo '
Variables de session
';
+ foreach ($_SESSION as $k=>$v)
+ {
+ if ($k == "db") continue; // Car la valeur stockée est un objet de type "db" qui plante l'affichage....
+
+ echo '