Files
ms1inscription-v5/php/inc_fx_doc.php

519 lines
17 KiB
PHP

<?php
/**
* Module documentation MS1 — overlay paginé HTML (distinct de info / info_aide).
*
* Clés d'appel (doc_anchor_clef) → module (doc_mod_clef) → pages (doc_page).
* Usage :
* fxDocRenderModule('bib_v4_assign_doc'); — overlay complet
* fxDocRenderTrigger('bib_v4_assign_doc'); — bouton ? seul
* afficheDocPage('bib_doc_overview'); — HTML d'une page (comme afficheTexte)
* afficheDocTitre('bib_doc_overview'); — titre d'une page
*/
/** Échappement attributs HTML doc module. */
function fxDocEsc($str) {
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
/** Page PHP courante pour doc_prg (miroir fxBibInfoPage / info_prg). */
function fxDocPrg($mem_global = 0) {
global $vPage;
if ($mem_global == 1) {
return 'global';
}
return !empty($vPage) ? $vPage : 'compte.php';
}
/** Langue courante doc. */
function fxDocLangue() {
global $strLangue;
return !empty($strLangue) ? $strLangue : 'fr';
}
/** Tables doc_* présentes en BD (évite erreur si SQL module pas encore exécuté). */
function fxDocSchemaReady() {
global $objDatabase;
static $blnReady = null;
if ($blnReady !== null) {
return $blnReady;
}
if (!isset($objDatabase)) {
$blnReady = false;
return false;
}
$qry = $objDatabase->fxQuery("SHOW TABLES LIKE 'doc_page'");
$blnReady = ($qry && mysqli_num_rows($qry) > 0);
return $blnReady;
}
/**
* Charge en mémoire toutes les pages doc pour une page PHP (comme obtenirTextepage).
* Remplit $vdoc_page[doc_page_clef] = row.
*/
function obtenirDocPage($page = null, $langue = null) {
global $objDatabase, $vdoc_page;
if ($page === null || $page === '') {
$page = fxDocPrg(0);
}
if ($langue === null || $langue === '') {
$langue = fxDocLangue();
}
$vdoc_page = array();
if (!isset($objDatabase) || !fxDocSchemaReady()) {
return $vdoc_page;
}
$sql = "SELECT *
FROM doc_page
WHERE doc_actif = 1
AND doc_langue = '" . $objDatabase->fxEscape($langue) . "'
AND doc_prg IN ('" . $objDatabase->fxEscape($page) . "', 'global')
ORDER BY doc_tri, pk_doc_page";
$rows = $objDatabase->fxGetResults($sql);
if (!is_array($rows)) {
return $vdoc_page;
}
for ($i = 1; $i <= count($rows); $i++) {
if (!empty($rows[$i]['doc_page_clef'])) {
$vdoc_page[$rows[$i]['doc_page_clef']] = $rows[$i];
}
}
return $vdoc_page;
}
/** Ligne doc_anchor par clé d'appel. */
function fxDocGetAnchorRow($clef, $mem_global = 0) {
global $objDatabase;
static $anchorCache = array();
$strPrg = fxDocPrg($mem_global);
$cacheKey = $clef . '|' . $strPrg;
if (array_key_exists($cacheKey, $anchorCache)) {
return $anchorCache[$cacheKey];
}
if (!isset($objDatabase) || $clef === '' || !fxDocSchemaReady()) {
$anchorCache[$cacheKey] = array();
return $anchorCache[$cacheKey];
}
$sql = "SELECT *
FROM doc_anchor
WHERE doc_actif = 1
AND doc_anchor_clef = '" . $objDatabase->fxEscape($clef) . "'
AND doc_prg IN ('" . $objDatabase->fxEscape($strPrg) . "', 'global')
ORDER BY FIELD(doc_prg, '" . $objDatabase->fxEscape($strPrg) . "', 'global')
LIMIT 1";
$row = $objDatabase->fxGetRow($sql);
$anchorCache[$cacheKey] = is_array($row) ? $row : array();
return $anchorCache[$cacheKey];
}
/** Résout doc_mod_clef depuis une clé d'ancre ou un code module direct. */
function fxDocResolveModClef($clef, $mem_global = 0) {
$anchor = fxDocGetAnchorRow($clef, $mem_global);
if (!empty($anchor['doc_mod_clef'])) {
return $anchor['doc_mod_clef'];
}
return $clef;
}
/** Pages actives d'un module, triées (pour carrousel overlay). */
function fxDocGetModulePages($modClef, $langue = null, $mem_global = 0) {
global $objDatabase, $vdoc_page;
if ($langue === null || $langue === '') {
$langue = fxDocLangue();
}
$strPrg = fxDocPrg($mem_global);
$modClef = fxDocResolveModClef($modClef, $mem_global);
if (!isset($objDatabase) || $modClef === '' || !fxDocSchemaReady()) {
return array();
}
$sql = "SELECT *
FROM doc_page
WHERE doc_actif = 1
AND doc_mod_clef = '" . $objDatabase->fxEscape($modClef) . "'
AND doc_langue = '" . $objDatabase->fxEscape($langue) . "'
AND doc_prg IN ('" . $objDatabase->fxEscape($strPrg) . "', 'global')
ORDER BY doc_tri, pk_doc_page";
$rows = $objDatabase->fxGetResults($sql);
$pages = array();
if (!is_array($rows)) {
return $pages;
}
for ($i = 1; $i <= count($rows); $i++) {
$pages[] = $rows[$i];
if (!empty($rows[$i]['doc_page_clef'])) {
if (!isset($vdoc_page) || !is_array($vdoc_page)) {
$vdoc_page = array();
}
$vdoc_page[$rows[$i]['doc_page_clef']] = $rows[$i];
}
}
return $pages;
}
/** Une page par doc_page_clef (mémoire ou BD). */
function fxDocGetPageRow($clef, $mem_global = 0) {
global $vdoc_page, $objDatabase;
if (!empty($vdoc_page[$clef])) {
return $vdoc_page[$clef];
}
if (!isset($objDatabase) || $clef === '' || !fxDocSchemaReady()) {
return array();
}
$strPrg = fxDocPrg($mem_global);
$langue = fxDocLangue();
static $pageCache = array();
$cacheKey = $clef . '|' . $langue . '|' . $strPrg;
if (array_key_exists($cacheKey, $pageCache)) {
return $pageCache[$cacheKey];
}
$sql = "SELECT *
FROM doc_page
WHERE doc_actif = 1
AND doc_page_clef = '" . $objDatabase->fxEscape($clef) . "'
AND doc_langue = '" . $objDatabase->fxEscape($langue) . "'
AND doc_prg IN ('" . $objDatabase->fxEscape($strPrg) . "', 'global')
ORDER BY FIELD(doc_prg, '" . $objDatabase->fxEscape($strPrg) . "', 'global')
LIMIT 1";
$row = $objDatabase->fxGetRow($sql);
$pageCache[$cacheKey] = is_array($row) ? $row : array();
return $pageCache[$cacheKey];
}
/** Crée une page doc vide si absente (miroir afficheTexte). */
function fxDocEnsurePage($clef, $mem_global = 0, $modClef = '') {
global $objDatabase;
$row = fxDocGetPageRow($clef, $mem_global);
if (!empty($row)) {
return $row;
}
$strPrg = fxDocPrg($mem_global);
$langue = fxDocLangue();
if ($modClef === '') {
$modClef = $clef;
}
$tabData = array(
'doc_mod_clef' => $modClef,
'doc_page_clef' => $clef,
'doc_langue' => $langue,
'doc_titre' => $clef,
'doc_sous_titre' => '',
'doc_html' => '',
'doc_prg' => $strPrg,
'doc_tri' => 0,
'doc_actif' => 1,
);
fxSetDocPage('add', $tabData);
if ($langue === 'fr') {
$tabDataEn = $tabData;
$tabDataEn['doc_langue'] = 'en';
fxSetDocPage('add', $tabDataEn);
}
return fxDocGetPageRow($clef, $mem_global);
}
/**
* Titre doc_titre — équivalent afficheTexte pour doc_page.
*/
function afficheDocTitre($clef, $mem_echo = 1, $mem_global = 0, $modClef = '') {
$row = fxDocGetPageRow($clef, $mem_global);
if (empty($row)) {
fxDocEnsurePage($clef, $mem_global, $modClef);
$row = fxDocGetPageRow($clef, $mem_global);
}
$str = trim($row['doc_titre'] ?? '');
if ($str === '' || $str === $clef) {
$str = '*' . $clef . '*';
}
if ($mem_echo == 1) {
echo fxDocEsc($str);
}
return $str;
}
/**
* Corps HTML doc_html — équivalent afficheAide (contenu long, HTML admin).
*/
function afficheDocHtml($clef, $mem_echo = 1, $mem_global = 0, $modClef = '') {
$row = fxDocGetPageRow($clef, $mem_global);
if (empty($row)) {
fxDocEnsurePage($clef, $mem_global, $modClef);
$row = fxDocGetPageRow($clef, $mem_global);
}
$str = $row['doc_html'] ?? '';
if ($mem_echo == 1) {
echo $str;
}
return $str;
}
/** Sous-titre doc_sous_titre. */
function afficheDocSousTitre($clef, $mem_echo = 1, $mem_global = 0, $modClef = '') {
$row = fxDocGetPageRow($clef, $mem_global);
if (empty($row)) {
fxDocEnsurePage($clef, $mem_global, $modClef);
$row = fxDocGetPageRow($clef, $mem_global);
}
$str = trim($row['doc_sous_titre'] ?? '');
if ($mem_echo == 1) {
echo fxDocEsc($str);
}
return $str;
}
/** URL pop-up doc_edition.php — module documentation paginé. */
function fxAdminDocUrl($anchorClef, $mem_global = 0) {
global $vDomaine, $vPage;
$strPrg = fxDocPrg($mem_global);
$modClef = fxDocResolveModClef($anchorClef, $mem_global);
return $vDomaine . '/php/doc_edition.php?' . http_build_query(array(
'anchor' => $anchorClef,
'mod' => $modClef,
'prg' => $strPrg,
'bd' => 'client',
'page' => !empty($vPage) ? $vPage : 'compte.php',
));
}
/** Lien crayon doc_edition.php — superadm dev/préprod + switch Textes ON. */
function fxAdminDocButton($anchorClef, $mem_global = 0) {
if (!fxAdminTextEditModeActive() || trim($anchorClef) === '') {
return '';
}
return '<a href="' . fxDocEsc(fxAdminDocUrl($anchorClef, $mem_global)) . '"'
. ' class="' . fxAdminTradLinkClass() . ' ms1-doc-edit-link"'
. ' title="Modifier le guide documentation"'
. ' aria-label="Modifier le guide documentation">'
. '<i class="fa fa-book" aria-hidden="true"></i>'
. '</a>';
}
/** Bouton ? pour ouvrir le module doc (clé doc_anchor_clef) — même rond que btn-aide-bib. */
function fxDocRenderTrigger($anchorClef, $mem_global = 0, $strAriaLabel = 'Documentation') {
$modClef = fxDocResolveModClef($anchorClef, $mem_global);
return '<button type="button"'
. ' class="btn-aide-bib ms1-doc-trigger btn-doc-trigger"'
. ' data-doc-anchor="' . fxDocEsc($anchorClef) . '"'
. ' data-doc-mod="' . fxDocEsc($modClef) . '"'
. ' aria-label="' . fxDocEsc($strAriaLabel) . '"'
. ' aria-haspopup="dialog">?</button>';
}
/** Panneau overlay paginé pour un module (clé doc_anchor_clef ou doc_mod_clef). */
function fxDocRenderModule($anchorClef, $mem_global = 0) {
$modClef = fxDocResolveModClef($anchorClef, $mem_global);
$pages = fxDocGetModulePages($modClef, null, $mem_global);
if (empty($pages)) {
return '';
}
$overlayId = 'ms1-doc-overlay-' . preg_replace('/[^a-z0-9_-]/i', '-', $anchorClef);
$html = '<div id="' . fxDocEsc($overlayId) . '"'
. ' class="ms1-doc-overlay"'
. ' data-doc-anchor="' . fxDocEsc($anchorClef) . '"'
. ' data-doc-mod="' . fxDocEsc($modClef) . '"'
. ' hidden'
. ' role="dialog"'
. ' aria-modal="true"'
. ' aria-labelledby="' . fxDocEsc($overlayId) . '-title">';
$html .= '<div class="ms1-doc-backdrop" data-ms1-doc-close="1"></div>';
$html .= '<div class="ms1-doc-panel">';
$html .= '<header class="ms1-doc-header">';
$html .= '<div class="ms1-doc-header-text">';
$html .= '<h2 class="ms1-doc-title" id="' . fxDocEsc($overlayId) . '-title"></h2>';
$html .= '<p class="ms1-doc-subtitle"></p>';
$html .= '</div>';
$html .= '<div class="ms1-doc-header-actions">';
$html .= fxAdminDocButton($anchorClef, $mem_global);
$html .= '<button type="button" class="ms1-doc-close" data-ms1-doc-close="1" aria-label="Fermer">&times;</button>';
$html .= '</div>';
$html .= '</header>';
$html .= '<div class="ms1-doc-body">';
foreach ($pages as $idx => $page) {
$hidden = $idx > 0 ? ' hidden' : '';
$html .= '<div class="ms1-doc-page' . $hidden . '"'
. ' data-doc-page-index="' . (int)$idx . '"'
. ' data-doc-page-clef="' . fxDocEsc($page['doc_page_clef']) . '">';
$html .= '<div class="ms1-doc-content ms1-doc-format">' . ($page['doc_html'] ?? '') . '</div>';
$html .= '</div>';
}
$html .= '</div>';
$html .= '<footer class="ms1-doc-footer">';
$html .= '<div class="ms1-doc-nav-dots">';
foreach ($pages as $idx => $page) {
$active = $idx === 0 ? ' is-active' : '';
$label = trim($page['doc_titre'] ?? '') !== '' ? $page['doc_titre'] : (string)($idx + 1);
$html .= '<button type="button"'
. ' class="ms1-doc-dot' . $active . '"'
. ' data-doc-page-index="' . (int)$idx . '"'
. ' title="' . fxDocEsc($label) . '"'
. ' aria-label="' . fxDocEsc($label) . '">'
. (int)($idx + 1)
. '</button>';
}
$html .= '</div>';
$html .= '<div class="ms1-doc-nav-arrows">';
$html .= '<button type="button" class="ms1-doc-prev" data-ms1-doc-prev="1">Précédent</button>';
$html .= '<span class="ms1-doc-counter"><span class="ms1-doc-counter-current">1</span> / <span class="ms1-doc-counter-total">' . count($pages) . '</span></span>';
$html .= '<button type="button" class="ms1-doc-next" data-ms1-doc-next="1">Suivant</button>';
$html .= '</div>';
$html .= '</footer>';
$html .= '</div></div>';
// Métadonnées titres pour JS (init header sans parser le HTML)
$html .= '<script type="application/json" class="ms1-doc-meta" data-doc-anchor="' . fxDocEsc($anchorClef) . '">';
$meta = array();
foreach ($pages as $page) {
$meta[] = array(
'clef' => $page['doc_page_clef'],
'titre' => $page['doc_titre'] ?? '',
'sous_titre' => $page['doc_sous_titre'] ?? '',
);
}
$html .= json_encode($meta, JSON_UNESCAPED_UNICODE);
$html .= '</script>';
return $html;
}
/** Trigger + overlay (appel typique à côté d'un titre de bloc). */
function fxDocRenderBlock($anchorClef, $mem_global = 0, $strAriaLabel = 'Documentation') {
return fxDocRenderTrigger($anchorClef, $mem_global, $strAriaLabel) . fxDocRenderModule($anchorClef, $mem_global);
}
function fxSetDocMod($strAction, $tabData, $id = 0) {
global $objDatabase;
switch ($strAction) {
case 'add':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_mod', 'doc_mod', $GLOBALS['arrConDatabaseMain']['db']);
$sqlValide = "SELECT COUNT(doc_mod_clef) FROM doc_mod
WHERE doc_mod_clef = '" . $objDatabase->fxEscape($tabData['doc_mod_clef']) . "'
AND doc_mod_prg = '" . $objDatabase->fxEscape($tabData['doc_mod_prg']) . "'";
if ((int)$objDatabase->fxGetVar($sqlValide) > 0) {
break;
}
$sqlInsert = "INSERT INTO doc_mod " . $tabFieldsValues['set'] . ', doc_mod_creation = NOW()';
$objDatabase->fxQuery($sqlInsert);
break;
case 'mod':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_mod', 'doc_mod', $GLOBALS['arrConDatabaseMain']['db']);
$sqlUpdate = "UPDATE doc_mod " . $tabFieldsValues['set'] . ", doc_mod_maj = NOW() WHERE " . $tabFieldsValues['key'];
$objDatabase->fxQuery($sqlUpdate);
break;
}
}
function fxSetDocPage($strAction, $tabData, $id = 0) {
global $objDatabase;
switch ($strAction) {
case 'add':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_page', 'doc_page', $GLOBALS['arrConDatabaseMain']['db']);
$sqlValide = "SELECT COUNT(doc_page_clef) FROM doc_page
WHERE doc_page_clef = '" . $objDatabase->fxEscape($tabData['doc_page_clef']) . "'
AND doc_langue = '" . $objDatabase->fxEscape($tabData['doc_langue']) . "'
AND doc_prg = '" . $objDatabase->fxEscape($tabData['doc_prg']) . "'";
if ((int)$objDatabase->fxGetVar($sqlValide) > 0) {
break;
}
$sqlInsert = "INSERT INTO doc_page " . $tabFieldsValues['set'] . ', doc_creation = NOW()';
$objDatabase->fxQuery($sqlInsert);
break;
case 'mod':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_page', 'doc_page', $GLOBALS['arrConDatabaseMain']['db']);
$sqlUpdate = "UPDATE doc_page " . $tabFieldsValues['set'] . ", doc_maj = NOW() WHERE " . $tabFieldsValues['key'];
$objDatabase->fxQuery($sqlUpdate);
break;
}
}
function fxSetDocAnchor($strAction, $tabData, $id = 0) {
global $objDatabase;
switch ($strAction) {
case 'add':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_anchor', 'doc_anchor', $GLOBALS['arrConDatabaseMain']['db']);
$sqlValide = "SELECT COUNT(doc_anchor_clef) FROM doc_anchor
WHERE doc_anchor_clef = '" . $objDatabase->fxEscape($tabData['doc_anchor_clef']) . "'
AND doc_prg = '" . $objDatabase->fxEscape($tabData['doc_prg']) . "'";
if ((int)$objDatabase->fxGetVar($sqlValide) > 0) {
break;
}
$sqlInsert = "INSERT INTO doc_anchor " . $tabFieldsValues['set'] . ', doc_creation = NOW()';
$objDatabase->fxQuery($sqlInsert);
break;
case 'mod':
$tabFieldsValues = fxListFieldsValues($tabData, 'pk_doc_anchor', 'doc_anchor', $GLOBALS['arrConDatabaseMain']['db']);
$sqlUpdate = "UPDATE doc_anchor " . $tabFieldsValues['set'] . " WHERE " . $tabFieldsValues['key'];
$objDatabase->fxQuery($sqlUpdate);
break;
}
}