Grosses mises à jour de shipmembers.php et adminstration.php pour revoir le process d'invitation sur un bateau mais aussi les sécurités de base sur les fichiers index.php et les fonctions_html.php

This commit is contained in:
2026-10-05 13:48:45 +00:00
parent 7146bce248
commit b7cb39bbe6
10 changed files with 2752 additions and 2511 deletions
+9 -8
View File
@@ -1,12 +1,13 @@
<?php
require_once("fonctions_bdd.php"); // Contient les fonctions d'accès aux BDD.
require_once("fonctions_fichiers.php"); // Contient les fonctions de traitement des répertoires et des fichiers
require_once("fonctions_ftp.php");
require_once("fonctions_html.php");
require_once("fonctions_mail.php"); // Contient les fonctions d'émission des emails
require_once("fonctions_pdf.php"); // Contient les fonctions de création de fichiers PDF
require_once("fonctions_traductions.php");
require_once("fonctions_utilitaires.php");
require_once(__DIR__.'/fonctions_acces.php'); // Contient les fonctions d'accès au site
require_once(__DIR__.'/fonctions_bdd.php'); // Contient les fonctions d'accès aux BDD.
require_once(__DIR__.'/fonctions_fichiers.php'); // Contient les fonctions de traitement des répertoires et des fichiers
require_once(__DIR__.'/fonctions_ftp.php');
require_once(__DIR__.'/fonctions_html.php');
require_once(__DIR__.'/fonctions_mail.php'); // Contient les fonctions d'émission des emails
require_once(__DIR__.'/fonctions_pdf.php'); // Contient les fonctions de création de fichiers PDF
require_once(__DIR__.'/fonctions_traductions.php');
require_once(__DIR__.'/fonctions_utilitaires.php');
+324
View File
@@ -0,0 +1,324 @@
<?php
//************************************************************************************************************
// fonctions_acces.php - Accès des ShipMembers aux bateaux et invitations (synthèse §47)
//
// Regroupe ce qui était recopié dans shipmembers.php (5 blocs d'activation de code, 7 requêtes "accès actifs")
// et administration.php (1 + 1). Modèle de TB_Lien_bateau_shipmember :
// - Statut : 'en_attente' (invitation non acceptée), 'actif', 'suspendu', 'revoque' ;
// - une invitation désigne une adresse e-mail (Email_invite) : seul un compte ShipMember ACTIVÉ ayant cette
// adresse peut l'accepter (l'activation prouve le contrôle de la boîte - un compte non activé ne le prouve pas) ;
// - Expiration_invitation : date limite pour accepter ; Fin_validite : fin de l'accès accordé.
//
// Toutes les fonctions utilisent la connexion courante ($_SESSION['db'], ouverte par Ouverture_bdd()).
//************************************************************************************************************
// Alphabet des codes d'invitation : majuscules et chiffres SANS les caractères ambigus à la lecture (0/O, 1/I/L) -
// 31 symboles. 8 caractères présentés en deux groupes (ex : K7QX-M3PW), soit 31^8 = 8,5.10^11 combinaisons.
const CODE_INVITATION_ALPHABET = "23456789ABCDEFGHJKMNPQRSTUVWXYZ";
const CODE_INVITATION_LONGUEUR = 8;
// Durée de validité par défaut d'une invitation (jours), si TB_Configuration.Duree_validite_invitation n'est pas
// chargée en session :
const DUREE_VALIDITE_INVITATION_DEFAUT = 30;
//************************************************************************************************************
// Fonction : Nombre de lignes réellement modifiées par la DERNIÈRE requête exécutée sur la connexion courante.
// NB : mysqli_affected_rows() ne convient pas après Requete_preparee_bdd(), qui appelle
// mysqli_stmt_get_result() même pour un UPDATE - ce qui remet le compteur côté PHP à -1 (constaté en
// test). ROW_COUNT() interroge directement le serveur. À appeler IMMÉDIATEMENT après la requête.
// Reçoit : Rien
// Retour : Le nombre de lignes modifiées
//************************************************************************************************************
function Nb_lignes_modifiees()
{
$reponse = Requete_bdd("SELECT ROW_COUNT() AS nb", __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
return (int)$donnees['nb'];
}
//************************************************************************************************************
// Fonction : Liste des accès ACTIFS d'un ShipMember (statut 'actif' et Fin_validite non dépassée).
// Reçoit : La clé primaire du ShipMember
// Retour : Un tableau de lignes ['ID_Bateau', 'ID_Type_acces_bateau_shipmember'] (vide si aucun accès)
//************************************************************************************************************
function Acces_actifs_shipmember($id_shipmember)
{
$acces = [];
$requete = "SELECT ID_Bateau, ID_Type_acces_bateau_shipmember FROM TB_Lien_bateau_shipmember
WHERE ID_Shipmember=? AND Statut='actif' AND Fin_validite >= NOW()";
$reponse = Requete_preparee_bdd($requete, array(array('i', $id_shipmember)), __FILE__, __LINE__);
while ($donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC)) $acces[] = $donnees;
Liberation_jeu_de_resultat_bdd($reponse);
return $acces;
}
//************************************************************************************************************
// Fonction : Indique si le compte d'un ShipMember est activé, c'est-à-dire si son titulaire a prouvé qu'il
// contrôle l'adresse e-mail (connexion avec le mot de passe provisoire reçu, puis choix d'un mot de
// passe : la ligne TB_Activation est alors supprimée).
// Reçoit : La clé primaire du ShipMember
// Retour : true si activé
//************************************************************************************************************
function Compte_est_active($id_shipmember)
{
$reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Activation WHERE ID_SHIPMEMBERS=?",
array(array('i', $id_shipmember)), __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
return ($donnees['nb'] == 0);
}
//************************************************************************************************************
// Fonction : Normalise un code d'invitation saisi ou reçu (majuscules, espaces et tirets retirés, puis tiret
// remis au milieu) et vérifie son format.
// Reçoit : La valeur brute (GET, POST, saisie manuelle...)
// Retour : Le code normalisé (ex : "K7QX-M3PW"), ou false si le format est invalide
//************************************************************************************************************
function Normaliser_code_invitation($valeur)
{
if (!is_string($valeur)) return false; // écarte ?code_acces[]=... (tableau)
$brut = strtoupper(preg_replace('/[\s-]/', '', $valeur));
$moitie = intdiv(CODE_INVITATION_LONGUEUR, 2);
if (preg_match('/^['.CODE_INVITATION_ALPHABET.']{'.CODE_INVITATION_LONGUEUR.'}\z/', $brut) !== 1) return false;
return substr($brut, 0, $moitie).'-'.substr($brut, $moitie);
}
//************************************************************************************************************
// Fonction : Génère un code d'invitation unique (générateur aléatoire cryptographique random_int).
// Reçoit : Rien
// Retour : Le code (ex : "K7QX-M3PW")
//************************************************************************************************************
function Generer_code_invitation()
{
$max = strlen(CODE_INVITATION_ALPHABET) - 1;
do
{
$brut = "";
for ($i = 0; $i < CODE_INVITATION_LONGUEUR; $i++) $brut .= CODE_INVITATION_ALPHABET[random_int(0, $max)];
$code = Normaliser_code_invitation($brut);
$reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Lien_bateau_shipmember WHERE Code=?",
array(array('s', $code)), __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
}
while ($donnees['nb'] != 0);
return $code;
}
//************************************************************************************************************
// Fonction : Crée une invitation nominative (statut 'en_attente').
// Reçoit : Le bateau, l'adresse e-mail du destinataire (obligatoire), son prénom et son nom (facultatifs, pour
// l'affichage), le type d'accès, la fin de l'accès accordé (format 'Y-m-d', vide = sans limite) et le
// ShipMember qui invite.
// Retour : ['ok'=>true, 'id'=>int, 'code'=>string] ou ['ok'=>false, 'raison'=>string]
// NB : les droits de celui qui invite sont vérifiés par la page appelante.
//************************************************************************************************************
function Creer_invitation($id_bateau, $email, $prenom, $nom, $id_type_acces, $fin_acces, $id_cree_par)
{
$email = strtolower(trim((string)$email));
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) return ['ok' => false, 'raison' => t("Adresse e-mail invalide.")];
$prenom = trim((string)$prenom);
$nom = trim((string)$nom);
$prenom = ($prenom === '') ? null : mb_substr($prenom, 0, 50);
$nom = ($nom === '') ? null : mb_substr($nom, 0, 50);
// Type d'accès : doit exister et être valide (l'ancien type "Administrateur" est dévalidé) :
$reponse = Requete_preparee_bdd("SELECT COUNT(*) AS nb FROM TB_Types_acces_bateau_shipmember WHERE Cle_primaire=? AND Validite=1",
array(array('i', $id_type_acces)), __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
if ($donnees['nb'] == 0) return ['ok' => false, 'raison' => t("Type d'accès invalide.")];
// Fin de l'accès accordé : date du jour en fin de journée, ou sans limite :
if ($fin_acces === null || trim($fin_acces) === '') $fin_validite = '9999-12-31 23:59:59';
else
{
$date = date_create_from_format('Y-m-d', trim($fin_acces));
if ($date === false) return ['ok' => false, 'raison' => t("Date de fin d'accès invalide.")];
$fin_validite = $date->format('Y-m-d').' 23:59:59';
}
$duree = isset($_SESSION['Config__Duree_validite_invitation']) ? (int)$_SESSION['Config__Duree_validite_invitation'] : DUREE_VALIDITE_INVITATION_DEFAUT;
if ($duree <= 0) $duree = DUREE_VALIDITE_INVITATION_DEFAUT;
$code = Generer_code_invitation();
$requete = "INSERT INTO TB_Lien_bateau_shipmember
(Statut, ID_Shipmember, ID_Bateau, Email_invite, Prenom_invite, Nom_invite, Code, Expiration_invitation,
Fin_validite, Cree_par, ID_Type_acces_bateau_shipmember)
VALUES ('en_attente', 0, ?, ?, ?, ?, ?, DATE_ADD(NOW(), INTERVAL ? DAY), ?, ?, ?)";
Requete_preparee_bdd($requete, array(
array('i', $id_bateau), array('s', $email), array('s', $prenom), array('s', $nom),
array('s', $code), array('i', $duree), array('s', $fin_validite),
array('i', $id_cree_par), array('i', $id_type_acces)),
__FILE__, __LINE__);
return ['ok' => true, 'id' => Recuperation_id_cree_bdd(), 'code' => $code];
}
//************************************************************************************************************
// Fonction : Informations sur une invitation à partir de son code (bandeau d'accueil d'un lien d'invitation).
// Reçoit : Le code (brut ou normalisé)
// Retour : Un tableau (ID, Statut, Email_invite, Prenom_invite, Nom_invite, Nom_bateau, Type_acces,
// Expiree [0/1]) ou null si le code est inconnu ou mal formé
//************************************************************************************************************
function Invitation_par_code($code)
{
$code = Normaliser_code_invitation($code);
if ($code === false) return null;
$colonne_type = (isset($_SESSION['langue']) && $_SESSION['langue'] == 'EN') ? 'Nom_en' : 'Nom_fr';
$requete = "SELECT L.ID, L.Statut, L.Email_invite, L.Prenom_invite, L.Nom_invite, B.Nom AS Nom_bateau,
T.".$colonne_type." AS Type_acces,
(L.Expiration_invitation IS NOT NULL AND L.Expiration_invitation <= NOW()) AS Expiree
FROM TB_Lien_bateau_shipmember L
JOIN TB_Bateaux B ON B.Cle_primaire = L.ID_Bateau
LEFT JOIN TB_Types_acces_bateau_shipmember T ON T.Cle_primaire = L.ID_Type_acces_bateau_shipmember
WHERE L.Code=?";
$reponse = Requete_preparee_bdd($requete, array(array('s', $code)), __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
return $donnees ?: null;
}
//************************************************************************************************************
// Fonction : Invitations en attente (non expirées) adressées à une adresse e-mail.
// Reçoit : L'adresse e-mail du ShipMember connecté
// Retour : Un tableau de lignes (ID, Nom_bateau, Type_acces, Expiration_invitation, Prenom_invitant, Nom_invitant)
//************************************************************************************************************
function Invitations_en_attente($email)
{
$invitations = [];
$colonne_type = (isset($_SESSION['langue']) && $_SESSION['langue'] == 'EN') ? 'Nom_en' : 'Nom_fr';
$requete = "SELECT L.ID, B.Nom AS Nom_bateau, T.".$colonne_type." AS Type_acces, L.Expiration_invitation,
S.Prenom AS Prenom_invitant, S.Nom AS Nom_invitant
FROM TB_Lien_bateau_shipmember L
JOIN TB_Bateaux B ON B.Cle_primaire = L.ID_Bateau
LEFT JOIN TB_Types_acces_bateau_shipmember T ON T.Cle_primaire = L.ID_Type_acces_bateau_shipmember
LEFT JOIN TB_Shipmembers S ON S.ID = L.Cree_par
WHERE L.Statut='en_attente' AND L.Email_invite=?
AND (L.Expiration_invitation IS NULL OR L.Expiration_invitation > NOW())
ORDER BY L.Date_creation";
$reponse = Requete_preparee_bdd($requete, array(array('s', strtolower(trim((string)$email)))), __FILE__, __LINE__);
while ($donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC)) $invitations[] = $donnees;
Liberation_jeu_de_resultat_bdd($reponse);
return $invitations;
}
//************************************************************************************************************
// Fonction : Accepte une invitation pour le ShipMember connecté.
// Toutes les conditions (en attente, adresse identique, non expirée) sont vérifiées DANS la requête de
// mise à jour elle-même : une invitation ne peut donc être acceptée qu'une fois, même par deux
// requêtes simultanées.
// Reçoit : L'ID de l'invitation, la clé primaire et l'adresse e-mail du ShipMember connecté
// Retour : ['ok'=>true] ou ['ok'=>false, 'raison'=>string]
//************************************************************************************************************
function Accepter_invitation($id_lien, $id_shipmember, $email)
{
if (!Compte_est_active($id_shipmember))
{
return ['ok' => false, 'raison' => t("Votre compte doit être activé (mot de passe provisoire reçu par e-mail) avant de pouvoir accepter une invitation.")];
}
$requete = "UPDATE TB_Lien_bateau_shipmember
SET Statut='actif', ID_Shipmember=?, Date_activation=NOW()
WHERE ID=? AND Statut='en_attente' AND Email_invite=?
AND (Expiration_invitation IS NULL OR Expiration_invitation > NOW())";
Requete_preparee_bdd($requete, array(
array('i', $id_shipmember), array('i', $id_lien), array('s', strtolower(trim((string)$email)))),
__FILE__, __LINE__);
if (Nb_lignes_modifiees() == 1) return ['ok' => true];
return ['ok' => false, 'raison' => t("Cette invitation n'est plus valable (déjà utilisée, expirée, révoquée, ou adressée à une autre adresse).")];
}
//************************************************************************************************************
// Fonction : Refuse une invitation (elle passe à 'revoque').
// Reçoit : L'ID de l'invitation et l'adresse e-mail du ShipMember connecté
// Retour : true si l'invitation a bien été refusée
//************************************************************************************************************
function Refuser_invitation($id_lien, $email)
{
$requete = "UPDATE TB_Lien_bateau_shipmember SET Statut='revoque'
WHERE ID=? AND Statut='en_attente' AND Email_invite=?";
Requete_preparee_bdd($requete, array(array('i', $id_lien), array('s', strtolower(trim((string)$email)))), __FILE__, __LINE__);
return (Nb_lignes_modifiees() == 1);
}
//************************************************************************************************************
// Fonction : Masque partiellement une adresse e-mail pour l'afficher à quelqu'un qui n'est pas encore connecté
// (ex : "pierre.durand@exemple.fr" -> "p•••@exemple.fr").
// Reçoit : L'adresse
// Retour : L'adresse masquée
//************************************************************************************************************
function Masquer_email($email)
{
$position = strpos((string)$email, '@');
if ($position === false || $position == 0) return "•••";
return mb_substr($email, 0, 1).'•••'.substr($email, $position);
}
//************************************************************************************************************
// Clé du type d'accès "Propriétaire" dans TB_Types_acces_bateau_shipmember (cf Devalidation_profils = 1 : tous
// les types SAUF Propriétaire).
//************************************************************************************************************
const TYPE_ACCES_PROPRIETAIRE = 2;
//************************************************************************************************************
// Fonction : Charge un accès (ou une invitation) À CONDITION qu'il appartienne au bateau indiqué - toute action
// sur un accès passe par là, pour qu'un identifiant envoyé par le navigateur ne puisse jamais viser
// l'accès d'un autre bateau.
// Reçoit : L'ID de l'accès et la clé primaire du bateau affiché
// Retour : La ligne (ID, Statut, Code, ID_Type_acces_bateau_shipmember, Expiration_invitation) ou null
//************************************************************************************************************
function Lien_du_bateau($id_lien, $id_bateau)
{
$requete = "SELECT ID, Statut, Code, ID_Type_acces_bateau_shipmember, Expiration_invitation
FROM TB_Lien_bateau_shipmember WHERE ID=? AND ID_Bateau=?";
$reponse = Requete_preparee_bdd($requete, array(array('i', $id_lien), array('i', $id_bateau)), __FILE__, __LINE__);
$donnees = Recherche_ligne_bdd($reponse, MYSQLI_ASSOC);
Liberation_jeu_de_resultat_bdd($reponse);
return $donnees ?: null;
}
//************************************************************************************************************
// Fonction : Indique si l'utilisateur peut suspendre, réactiver ou révoquer un accès d'un type donné.
// Administrateur : toujours. Devalidation_profils = 2 : tous les types. = 1 : tous sauf Propriétaire.
// Reçoit : Les droits de l'utilisateur sur le bateau ($acces_profil), s'il est administrateur, le type visé
// Retour : true si autorisé
//************************************************************************************************************
function Peut_devalider_acces($acces_profil, $est_administrateur, $id_type_cible)
{
if ($est_administrateur) return true;
$niveau = isset($acces_profil['Devalidation_profils']) ? (int)$acces_profil['Devalidation_profils'] : 0;
if ($niveau >= 2) return true;
if ($niveau == 1) return ((int)$id_type_cible != TYPE_ACCES_PROPRIETAIRE);
return false;
}
//************************************************************************************************************
// Fonction : Met à jour en session la liste des accès actifs du ShipMember connecté ($_SESSION['bateau_shipmember']
// et $_SESSION['acces']), à partir de la base - la liste est REMPLACÉE (l'ancien code y ajoutait des
// lignes sans la vider : des accès révoqués pouvaient y rester jusqu'à la déconnexion).
// Reçoit : La clé primaire du ShipMember
// Retour : Rien
//************************************************************************************************************
function Rafraichir_acces_session($id_shipmember)
{
$_SESSION['bateau_shipmember'] = Acces_actifs_shipmember($id_shipmember);
$_SESSION['acces'] = count($_SESSION['bateau_shipmember']);
}
+396 -344
View File
@@ -1,344 +1,396 @@
<?php
//************************************************************************************************************
// Fonction : Initialisation de la session. A appeler avant tout envoi au client.
// Reçoit : Rien
// Retour : Rien
//************************************************************************************************************
function Init_session($nom_page)
{
ini_set('arg_separator.output', '&amp;');
ini_set("url_rewriter.tags","a=href,area=href,frame=src,iframe=src,input=src"); // Ces deux lignes sont destinées à faire ajouter autoamtiquement &amp; au lieu de '&' dans la passage des variables de Session (nécessaire pour W3C validator...)
session_set_cookie_params([
'lifetime' => 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start(); // Ouverture de session (avant tout autre envoi au client!!!)
}
//************************************************************************************************************
// Fonction : Insertion de l'entête des pages HTML ; essentiellement la partie <header></header>.
// 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 '<!DOCTYPE html>';
echo '<html lang="FR">';
echo '<head>';
echo '<meta charset="UTF-8">';
echo '<title>ShipHeart - Simplicity is true luxury</title>';
echo '<meta name="description" content="'.t("ShipHeart by Koriolan, la première centrale de vie à bord pour les bateaux de plaisance").'" />';
echo '<meta name="keywords" content="ShipHeart" />';
echo '<meta content="text/html" http-equiv="content-type" />';
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
echo '<link rel="icon" type="image/png" href="images/favicon.png" />';
echo "<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>";
echo "<link href='https://fonts.googleapis.com/css2?family=Oswald&display=swap' rel='stylesheet'>";
echo "<link rel='stylesheet' href='https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' integrity='sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=' crossorigin=''/>";
echo "<link rel='stylesheet' href='/vendor/noUiSlider/dist/nouislider.min.css'>";
echo "<script src='/vendor/noUiSlider/dist/nouislider.min.js'></script>";
// gestion des fichiers CSS (et de scripts spécifiques à certains fichiers) :
switch(basename($_SERVER['SCRIPT_FILENAME']))
{
case "administration.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_administration.css" />'; break; }
case "textes.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_textes.css" />'; break; }
case "bateaux.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_bateaux.css" />'; break; }
case "cartes.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_cartes.css" />'; break; }
case "mentions_legales.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_mentions_legales.css" />'; break; }
case "shipmembers.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_shipmembers.css" />'; break; }
case "uploads.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_uploads.css" />'; break; }
case "versions.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_versions.css" />'; break; }
}
echo '<link rel="stylesheet" type="text/css" href="/vendor/bootstrap/bootstrap.min.css" />'; // CSS de Bootstrap
echo '<link rel="stylesheet" type="text/css" media="screen" href="format.css" />'; // CSS général, tous écrans.
echo '<link rel="stylesheet" type="text/css" media="screen and (max-device-width : 667px)" href="format__telephone.css" />'; // CSS pour les petits écrans (doit être placé à la fin car il override les CSS globaux)
// Chargement de srcipts JS généraux :
echo '<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>'; // Pour Google Charts (graphiques)
echo '<script src="/vendor/jQuery/jquery-3.6.0.min.js"></script>';
echo '</head>';
echo '<body>';
}
//************************************************************************************************************
// 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 '<div id="entete">';
//----------------------------------
// Bandeau gauche :
echo '<div id="logo_home">
<a id="home" href="">
<a href="shipmembers.php" style="padding: 0;" id="logo_heart_a" title="'.t("Retour à l'accueil").'">
<div id="logo_heart"> </div>
<div id="logo_texte"> </div>
</a>
</a>
</div>';
// echo '<div id="menu_mobile"> </div> ';
// echo '<div id="nav_bar_global" style="display: none;">';
//----------------------------------
// Bandeau droit :
echo '<div id="navbar_right">';
//----------------------------------
// Bouton LANGUE :
echo '<div id="flag_lang">';
echo '<a class="'.$new_langue.'_acc" title="'.t("Switch to English...").'" href="'.$nom_page.'?action=changement_langue&langue='.$new_langue.'">
</a>';
echo '</div>';
//----------------------------------
// Bouton SHIPMEMBER ACCESS :
echo '<div id="logo_shipmember">';
echo '<div id="shipmember">';
if ($_SESSION['acces_valide'])
{
echo '<a title="'.t("Accès validé pour ").' '.$_SESSION['shipmember']['Prenom'].'" href="shipmembers.php?action=navigation&destination=Accueil">
<span class="acces_valide">'.substr($_SESSION['shipmember']['Prenom'],0,1).'</span>
</a>';
}
// else echo '<a title="'.$_SESSION['textes']['fonctions']['entete']['ligne_icones']['shipmember_a_title'].'" href="shipmembers.php?action=navigation&destination=Accueil"> </a>';
echo '</div>';
echo "</div>";
echo '</div>';
echo '</div>';
//--------------------------------------------------------------------------------------------
// Menu afficher / masquer -- Mobile
//--------------------------------------------------------------------------------------------
echo '<script>
window.onload = function () {
pro = document.getElementsByClassName("portail_pro");
par = document.getElementsByClassName("portail_par");
if ($(window).width() > 850) {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=true; } document.getElementById(\'par_p\').hidden = true }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=true; } document.getElementById(\'pro_p\').hidden = true }
document.getElementById(\'img_type\').hidden= false;
document.getElementById(\'logo_heart_a\').href = "bienvenue.php";
} else {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=false; } document.getElementById(\'par_p\').hidden = false }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=false; } document.getElementById(\'pro_p\').hidden = false }
document.getElementById(\'img_type\').hidden= true;
document.getElementById(\'logo_heart_a\').href = "accueil.php";
}
} ;
$(document).ready(function(){
function menu_control(){
hidden_div = document.getElementById("nav_bar_global").style.display;
hidden_div.replace(/[\u200B-\u200D\uFEFF]/g, \'\');
if (hidden_div == "block") hidden_div = "none";
else if (hidden_div == "none") hidden_div = "block";
document.getElementById("nav_bar_global").style.display=hidden_div;
};
$("#menu_mobile").click(menu_control);
});
window.addEventListener(\'click\', function(e){
if (!document.getElementById(\'nav_bar_global\').contains(e.target) && !document.getElementById(\'menu_mobile\').contains(e.target) ){
document.getElementById("nav_bar_global").style.display="none";
}
});
window.addEventListener(\'touchstart\', function(e){
if (!document.getElementById(\'nav_bar_global\').contains(e.target) && !document.getElementById(\'menu_mobile\').contains(e.target) ){
document.getElementById("nav_bar_global").style.display="none";
}
});
$(window).resize(function() {
pro = document.getElementsByClassName("portail_pro");
par = document.getElementsByClassName("portail_par");
if ($(this).width() > 850) {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=true; } document.getElementById(\'par_p\').hidden = true }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=true; } document.getElementById(\'pro_p\').hidden = true }
document.getElementById(\'img_type\').hidden= false;
document.getElementById(\'logo_heart_a\').href = "bienvenue.php";
} else {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=false; } document.getElementById(\'par_p\').hidden = false }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=false; } document.getElementById(\'pro_p\').hidden = false }
document.getElementById(\'img_type\').hidden= true;
document.getElementById(\'logo_heart_a\').href = "accueil.php";
}
});
</script>';
//--------------------------------------------------------------------------------------------
// 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."]<br />";
// else $message_erreur .= "Base de données de production [".BDD_DATABASE."]<br />";
if ($message_erreur != "") echo '<div class="message_erreur">'.$message_erreur.'</div>';
if ($message_information != "") echo '<div class="message_information">'.$message_information.'</div>';
}
//************************************************************************************************************
// 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 "<script src='https://unpkg.com/leaflet@1.9.4/dist/leaflet.js' integrity='sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=' crossorigin=''> </script>";
echo '<div id="pied_de_page" style="display: none;">';
echo '<div class="gauche" id="gauche" style="height: 210px;">';
echo '<div class="marque" id="marque">
<p id="marque_texte">'.t("ShipHeart ™ est une marque de Koriolan").'</p>
<div class="signature" id="signature">
<a style="padding: 0;" title="'.t("Accéder à notre site Koriolan").'" href="https://koriolan.tech/" target="_blank">
<img src="images/logo_koriolan_rvb.png">
</a>
</div>';
echo '</div>';
echo '</div>';
echo '<div class="droite" id="droite" style="height: 210px;">';
echo '<div class="mentions_legales" id="mentions_legales">';
if ($nom_page != "mentions_legales.php")
{
echo '<a title="'.t("Mentions légales").'" href="mentions_legales.php">
'.t("Mentions légales").'
</a>';
}
else
{
echo '<a title="'.t("Retour").'" href="shipmembers.php">
'.t("Retour").'
</a>';
}
echo '</div>';
echo '</div>';
echo '</div>';
echo '<div id="pied_de_page_mobile" >';
echo '</div>';
//==================================================================================
// Script JS pour pieds de page :
?><script>
function footer ()
{
if (window.innerWidth <= 850) //Footer mobile :
{
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('mentions_legales'));
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('marque_texte'));
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('signature'));
}
else //Footer "normal" :
{
document.getElementById('droite').appendChild(document.getElementById('mentions_legales'));
document.getElementById('marque').appendChild(document.getElementById('marque_texte'));
document.getElementById('marque').appendChild(document.getElementById('signature'));
}
}
document.onreadystatechange = function()
{
footer();
page = "<?php echo $nom_page ?>";
if (page == "faq.php")
{
show_hide('0');
for(var k = 0; k < reponses.length; k++)
{
document.getElementById(reponses[k].id).hidden=true;
}
}
};
window.addEventListener('resize', function(event)
{
footer();
});
</script><?php
// Fin du script JS
//====================================================================================
echo '</body>';
echo '</html>';
} // Fin de la fonction Pied_de_page()
<?php
//************************************************************************************************************
// Fonction : Initialisation de la session. A appeler avant tout envoi au client.
// Reçoit : Rien
// Retour : Rien
//************************************************************************************************************
function Init_session($nom_page)
{
ini_set('arg_separator.output', '&amp;');
ini_set("url_rewriter.tags","a=href,area=href,frame=src,iframe=src,input=src"); // Ces deux lignes sont destinées à faire ajouter autoamtiquement &amp; au lieu de '&' dans la passage des variables de Session (nécessaire pour W3C validator...)
// Cookie de session : sans attribut domain (cookie réservé exactement au site), SameSite=Lax (synthèse §47) :
session_set_cookie_params([
'lifetime' => 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start(); // Ouverture de session (avant tout autre envoi au client!!!)
Initialiser_session();
}
//************************************************************************************************************
// Fonction : Initialise une session neuve, UNE fois : valeurs par défaut, paramètres de TB_Configuration, langues,
// textes. Auparavant fait uniquement par index.php : une page ouverte directement (lien d'invitation
// scanné sur un téléphone, favori...) utilisait des variables inexistantes (synthèse §47).
// Reçoit : Rien
// Retour : Rien
//************************************************************************************************************
function Initialiser_session()
{
if (isset($_SESSION['session_initialisee'])) return;
$_SESSION['acces_valide'] = false;
$_SESSION['display_menu'] = false;
$_SESSION['langue'] = 'FR'; // Valeur par défaut, remplacée par Langue_par_defaut de TB_Configuration
$_SESSION['shipmember'] = array('Cle_primaire' => 0, 'Prenom' => "", 'Nom' => "", 'Entreprise' => "", 'Rue' => "",
'Code_postal' => "", 'Ville' => "", 'ID_Pays' => 75, 'Telephone' => "", 'Email' => "",
'Commentaires' => "", 'Niveau_acces' => 0);
$_SESSION['droits_acces'] = 0;
$_SESSION['bateau_shipmember'] = array();
$_SESSION['acces'] = 0;
$_SESSION['modifier_texte'] = 0;
Ouverture_bdd();
// Paramètres de configuration générale :
$reponse = Requete_bdd('SELECT Nom, Valeur FROM TB_Configuration WHERE Validite=1', __FILE__, __LINE__);
while ($donnees = Recherche_ligne_bdd($reponse))
{
if ($donnees['Nom'] == "Langue_par_defaut") $_SESSION['langue'] = $donnees['Valeur'];
else $_SESSION['Config__'.$donnees['Nom']] = $donnees['Valeur'];
}
Liberation_jeu_de_resultat_bdd($reponse);
// ID des langues selon leur préfixe :
$reponse = Requete_bdd('SELECT ID, Prefix FROM TB_Langues', __FILE__, __LINE__);
while ($donnees = Recherche_ligne_bdd($reponse)) $_SESSION['id_langue'][$donnees['Prefix']] = $donnees['ID'];
Liberation_jeu_de_resultat_bdd($reponse);
Fermeture_bdd();
chargement_langue(); // Textes de la langue en cours
$_SESSION['session_initialisee'] = true;
}
//************************************************************************************************************
// Fonction : Insertion de l'entête des pages HTML ; essentiellement la partie <header></header>.
// 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']));
exit();
}
// Entête HTML :
echo '<!DOCTYPE html>';
echo '<html lang="FR">';
echo '<head>';
echo '<meta charset="UTF-8">';
echo '<title>ShipHeart - Simplicity is true luxury</title>';
echo '<meta name="description" content="'.t("ShipHeart by Koriolan, la première centrale de vie à bord pour les bateaux de plaisance").'" />';
echo '<meta name="keywords" content="ShipHeart" />';
echo '<meta content="text/html" http-equiv="content-type" />';
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
echo '<link rel="icon" type="image/png" href="images/favicon.png" />';
echo "<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>";
echo "<link href='https://fonts.googleapis.com/css2?family=Oswald&display=swap' rel='stylesheet'>";
echo "<link rel='stylesheet' href='https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' integrity='sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=' crossorigin=''/>";
echo "<link rel='stylesheet' href='/vendor/noUiSlider/dist/nouislider.min.css'>";
echo "<script src='/vendor/noUiSlider/dist/nouislider.min.js'></script>";
// gestion des fichiers CSS (et de scripts spécifiques à certains fichiers) :
switch(basename($_SERVER['SCRIPT_FILENAME']))
{
case "administration.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_administration.css" />'; break; }
case "textes.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_textes.css" />'; break; }
case "bateaux.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_bateaux.css" />'; break; }
case "cartes.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_cartes.css" />'; break; }
case "mentions_legales.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_mentions_legales.css" />'; break; }
case "shipmembers.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_shipmembers.css" />'; break; }
case "uploads.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_uploads.css" />'; break; }
case "versions.php" : { echo '<link rel="stylesheet" type="text/css" media="screen" href="format_versions.css" />'; break; }
}
echo '<link rel="stylesheet" type="text/css" href="/vendor/bootstrap/bootstrap.min.css" />'; // CSS de Bootstrap
echo '<link rel="stylesheet" type="text/css" media="screen" href="format.css" />'; // CSS général, tous écrans.
echo '<link rel="stylesheet" type="text/css" media="screen and (max-device-width : 667px)" href="format__telephone.css" />'; // CSS pour les petits écrans (doit être placé à la fin car il override les CSS globaux)
// Chargement de srcipts JS généraux :
echo '<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>'; // Pour Google Charts (graphiques)
echo '<script src="/vendor/jQuery/jquery-3.6.0.min.js"></script>';
echo '</head>';
echo '<body>';
}
//************************************************************************************************************
// 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 '<div id="entete">';
//----------------------------------
// Bandeau gauche :
echo '<div id="logo_home">
<a id="home" href="">
<a href="shipmembers.php" style="padding: 0;" id="logo_heart_a" title="'.t("Retour à l'accueil").'">
<div id="logo_heart"> </div>
<div id="logo_texte"> </div>
</a>
</a>
</div>';
// echo '<div id="menu_mobile"> </div> ';
// echo '<div id="nav_bar_global" style="display: none;">';
//----------------------------------
// Bandeau droit :
echo '<div id="navbar_right">';
//----------------------------------
// Bouton LANGUE :
echo '<div id="flag_lang">';
echo '<a class="'.$new_langue.'_acc" title="'.t("Switch to English...").'" href="'.$nom_page.'?action=changement_langue&langue='.$new_langue.'">
</a>';
echo '</div>';
//----------------------------------
// Bouton SHIPMEMBER ACCESS :
echo '<div id="logo_shipmember">';
echo '<div id="shipmember">';
if ($_SESSION['acces_valide'])
{
echo '<a title="'.t("Accès validé pour ").' '.$_SESSION['shipmember']['Prenom'].'" href="shipmembers.php?action=navigation&destination=Accueil">
<span class="acces_valide">'.substr($_SESSION['shipmember']['Prenom'],0,1).'</span>
</a>';
}
// else echo '<a title="'.$_SESSION['textes']['fonctions']['entete']['ligne_icones']['shipmember_a_title'].'" href="shipmembers.php?action=navigation&destination=Accueil"> </a>';
echo '</div>';
echo "</div>";
echo '</div>';
echo '</div>';
//--------------------------------------------------------------------------------------------
// Menu afficher / masquer -- Mobile
//--------------------------------------------------------------------------------------------
echo '<script>
window.onload = function () {
pro = document.getElementsByClassName("portail_pro");
par = document.getElementsByClassName("portail_par");
if ($(window).width() > 850) {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=true; } document.getElementById(\'par_p\').hidden = true }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=true; } document.getElementById(\'pro_p\').hidden = true }
document.getElementById(\'img_type\').hidden= false;
document.getElementById(\'logo_heart_a\').href = "bienvenue.php";
} else {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=false; } document.getElementById(\'par_p\').hidden = false }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=false; } document.getElementById(\'pro_p\').hidden = false }
document.getElementById(\'img_type\').hidden= true;
document.getElementById(\'logo_heart_a\').href = "accueil.php";
}
} ;
$(document).ready(function(){
function menu_control(){
hidden_div = document.getElementById("nav_bar_global").style.display;
hidden_div.replace(/[\u200B-\u200D\uFEFF]/g, \'\');
if (hidden_div == "block") hidden_div = "none";
else if (hidden_div == "none") hidden_div = "block";
document.getElementById("nav_bar_global").style.display=hidden_div;
};
$("#menu_mobile").click(menu_control);
});
window.addEventListener(\'click\', function(e){
if (!document.getElementById(\'nav_bar_global\').contains(e.target) && !document.getElementById(\'menu_mobile\').contains(e.target) ){
document.getElementById("nav_bar_global").style.display="none";
}
});
window.addEventListener(\'touchstart\', function(e){
if (!document.getElementById(\'nav_bar_global\').contains(e.target) && !document.getElementById(\'menu_mobile\').contains(e.target) ){
document.getElementById("nav_bar_global").style.display="none";
}
});
$(window).resize(function() {
pro = document.getElementsByClassName("portail_pro");
par = document.getElementsByClassName("portail_par");
if ($(this).width() > 850) {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=true; } document.getElementById(\'par_p\').hidden = true }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=true; } document.getElementById(\'pro_p\').hidden = true }
document.getElementById(\'img_type\').hidden= false;
document.getElementById(\'logo_heart_a\').href = "bienvenue.php";
} else {
if (pro.length > 0) { for (i = 0; i < pro.length ; i++){ pro[i].hidden=false; } document.getElementById(\'par_p\').hidden = false }
if (par.length > 0) { for (i = 0; i < par.length ; i++){ par[i].hidden=false; } document.getElementById(\'pro_p\').hidden = false }
document.getElementById(\'img_type\').hidden= true;
document.getElementById(\'logo_heart_a\').href = "accueil.php";
}
});
</script>';
//--------------------------------------------------------------------------------------------
// 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."]<br />";
// else $message_erreur .= "Base de données de production [".BDD_DATABASE."]<br />";
if ($message_erreur != "") echo '<div class="message_erreur">'.$message_erreur.'</div>';
if ($message_information != "") echo '<div class="message_information">'.$message_information.'</div>';
}
//************************************************************************************************************
// 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 "<script src='https://unpkg.com/leaflet@1.9.4/dist/leaflet.js' integrity='sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=' crossorigin=''> </script>";
echo '<div id="pied_de_page" style="display: none;">';
echo '<div class="gauche" id="gauche" style="height: 210px;">';
echo '<div class="marque" id="marque">
<p id="marque_texte">'.t("ShipHeart ™ est une marque de Koriolan").'</p>
<div class="signature" id="signature">
<a style="padding: 0;" title="'.t("Accéder à notre site Koriolan").'" href="https://koriolan.tech/" target="_blank">
<img src="images/logo_koriolan_rvb.png">
</a>
</div>';
echo '</div>';
echo '</div>';
echo '<div class="droite" id="droite" style="height: 210px;">';
echo '<div class="mentions_legales" id="mentions_legales">';
if ($nom_page != "mentions_legales.php")
{
echo '<a title="'.t("Mentions légales").'" href="mentions_legales.php">
'.t("Mentions légales").'
</a>';
}
else
{
echo '<a title="'.t("Retour").'" href="shipmembers.php">
'.t("Retour").'
</a>';
}
echo '</div>';
echo '</div>';
echo '</div>';
echo '<div id="pied_de_page_mobile" >';
echo '</div>';
//==================================================================================
// Script JS pour pieds de page :
?><script>
function footer ()
{
if (window.innerWidth <= 850) //Footer mobile :
{
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('mentions_legales'));
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('marque_texte'));
document.getElementById('pied_de_page_mobile').appendChild(document.getElementById('signature'));
}
else //Footer "normal" :
{
document.getElementById('droite').appendChild(document.getElementById('mentions_legales'));
document.getElementById('marque').appendChild(document.getElementById('marque_texte'));
document.getElementById('marque').appendChild(document.getElementById('signature'));
}
}
document.onreadystatechange = function()
{
footer();
page = "<?php echo $nom_page ?>";
if (page == "faq.php")
{
show_hide('0');
for(var k = 0; k < reponses.length; k++)
{
document.getElementById(reponses[k].id).hidden=true;
}
}
};
window.addEventListener('resize', function(event)
{
footer();
});
</script><?php
// Fin du script JS
//====================================================================================
echo '</body>';
echo '</html>';
} // Fin de la fonction Pied_de_page()
+91 -99
View File
@@ -251,92 +251,9 @@ class MYPDF2 extends TCPDF {
//********************************************************************************************************************************
// 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', Url_activation_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);
}
//********************************************************************************************************************************
// Fonction : URL d'activation d'un code d'accès bateau, portée par le QR code ET par le lien cliquable du PDF d'invitation.
// Fonction : URL d'activation d'un code d'invitation, portée par le QR code ET par le lien cliquable du PDF d'invitation.
// Construite à partir de URL_SITE (definitions.php) : plus d'adresse écrite en dur (synthèse §47).
// Reçoit : le code d'accès
// Reçoit : le code d'invitation
// Retour : l'URL complète
//********************************************************************************************************************************
function Url_activation_code_acces($code)
@@ -344,21 +261,96 @@ function Url_activation_code_acces($code)
return 'https://'.URL_SITE.'/shipmembers.php?code_acces='.urlencode($code);
}
function generateCode($code){
$tempDir = "temp/CodeQR/";
$codeContents = Url_activation_code_acces($code);
$fileName = $code.'.png';
$pngAbsoluteFilePath = $tempDir.$fileName;
$urlRelativeFilePath = $tempDir.$fileName;
// generating
if (!file_exists($pngAbsoluteFilePath)) {
QRcode::png($codeContents, $pngAbsoluteFilePath);
}
return $urlRelativeFilePath;
//********************************************************************************************************************************
// Fonction : Image PNG d'un QR code, produite EN MÉMOIRE (aucun fichier temporaire : l'ancienne version écrivait le QR code
// et le PDF dans html/temp/, donc dans la partie publique du site - synthèse §47).
// Reçoit : le texte à encoder
// Retour : le contenu binaire de l'image PNG
//********************************************************************************************************************************
function Qr_code_png($texte)
{
ob_start();
QRcode::png($texte, false, QR_ECLEVEL_M, 8, 2); // false = sortie standard, capturée par le tampon
$png = ob_get_clean();
header_remove('Content-Type'); // phpqrcode a posé un en-tête image/png : retiré, le PDF posera le sien
return $png;
}
//********************************************************************************************************************************
// Fonction : Génère et ENVOIE au navigateur le PDF d'une invitation nominative à accéder à un bateau (synthèse §47).
// La page appelante doit s'arrêter juste après (exit) : rien ne doit suivre le PDF.
// Reçoit : l'invitation (tableau) : Code, Nom_bateau, Email_invite, Prenom_invite, Nom_invite, Type_acces,
// Expiration_invitation
// Retour : rien (le PDF est envoyé en téléchargement)
//********************************************************************************************************************************
function code_acces_pdf($invitation)
{
$code = $invitation['Code'];
$nom_bateau = $invitation['Nom_bateau'];
$email = $invitation['Email_invite'];
$destinataire = trim(($invitation['Prenom_invite'] ?? '').' '.($invitation['Nom_invite'] ?? ''));
$type_acces = $invitation['Type_acces'] ?? '';
$expiration = ($invitation['Expiration_invitation'] != NULL) ? date_format(date_create($invitation['Expiration_invitation']), 'd/m/Y') : '';
$nom_fichier = 'Invitation_'.preg_replace('/[^A-Za-z0-9_-]/', '_', $nom_bateau).'_'.$code.'.pdf';
$pdf = new MYPDF2('LANDSCAPE', 'px', 'A4', true, 'UTF-8', false);
// set document (meta) information :
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Koriolan');
$pdf->SetTitle('Invitation - '.$nom_bateau);
$pdf->SetSubject('ShipHeart');
$pdf->SetKeywords('ShipHeart by Koriolan');
$pdf->SetTextColor(0, 69, 99);
$pdf->SetFont('helvetica', '', 10);
$pdf->SetAutoPageBreak(false); // Mise en page fixe sur 2 feuilles : pas de saut de page automatique (l'ancienne version
// obtenait sa 2e feuille par un saut automatique, déclenché par le texte du bas de la 1re)
//---------------------------------------------------------
// Feuille 1 (pliée en deux : à droite la couverture, à gauche le mot d'accueil) :
$pdf->AddPage();
$pdf->SetDrawColor(217,217,217);
$pdf->Line(421, 0, 421, 595);
$pdf->Image('images/logo_shipheart.png', 506, 214, 252, 167, 'PNG', 'https://shipheart.tech'); // Couverture
$pdf->Image('images/logo_shipheart.png', 12, 12, 64, 45, 'PNG', 'https://shipheart.tech');
$salutation = ($destinataire != '') ? $destinataire.",\n\n" : '';
$pdf->CreateTextBox($salutation."Nous vous remercions pour votre confiance et sommes ravis de vous offrir un accès aux informations du bateau ".$nom_bateau." via notre plateforme Ship2net.\n\n"
."We thank you for your trust and are delighted to offer you access to the information of the boat ".$nom_bateau." via our Ship2net platform.",
52, 150, 317, 230, 12, '', 'L');
$pdf->CreateTextBox($nom_bateau.(($type_acces != '') ? ' - '.$type_acces : ''), 0, 520, 421, 46, 12, 'B', 'C');
//---------------------------------------------------------
// Feuille 2 (à gauche le mode d'emploi, à droite le QR code et le code) :
$pdf->AddPage();
$pdf->SetDrawColor(217,217,217);
$pdf->Line(421, 0, 421, 595);
$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("Cette invitation est réservée à l'adresse ".$email.".\n"
."Pour l'activer, scannez le QR code ci-contre, puis connectez-vous à votre compte ShipMember avec cette adresse (ou créez votre compte avec cette adresse).\n"
."Sans appareil photo : rendez-vous sur ".URL_SITE." et connectez-vous avec cette adresse ; l'invitation apparaîtra dans « Mes invitations ».\n"
.(($expiration != '') ? "Invitation valable jusqu'au ".$expiration.".\n" : '')
."\n"
."This invitation is reserved for the address ".$email.".\n"
."To activate it, scan the QR code opposite, then log in to your ShipMember account with this address (or create your account with this address).\n"
."Without a camera: go to ".URL_SITE." and log in with this address; the invitation will appear in \"My invitations\".\n"
.(($expiration != '') ? "Invitation valid until ".$expiration."." : ''),
52, 110, 317, 400, 11, '', 'L');
$pdf->Image('@'.Qr_code_png(Url_activation_code_acces($code)), 546, 196, 164, 164, 'PNG', Url_activation_code_acces($code));
$pdf->CreateTextBox($code, 506, 372, 244, 30, 18, 'B', 'C');
$pdf->CreateTextBox(URL_SITE, 506, 400, 244, 20, 11, '', 'C');
//---------------------------------------------------------
// Envoi direct au navigateur, sans fichier intermédiaire. Tout ce qui aurait déjà été mis en tampon (espaces,
// avertissements...) est jeté : un seul octet parasite avant le PDF le corromprait.
while (ob_get_level() > 0) ob_end_clean();
$pdf->Output($nom_fichier, 'D');
}