This commit updates the CSS for the documentation overlay, consolidating styles for the help button and ensuring it aligns with the existing button styles. The PHP files have been modified to enhance the integration of the documentation trigger within the block headers, allowing for optional documentation links. Additionally, the SQL file has been updated to improve the content of the documentation pages, providing clearer instructions and enhancing user experience.
466 lines
16 KiB
PHP
466 lines
16 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';
|
|
}
|
|
|
|
/**
|
|
* 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)) {
|
|
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 === '') {
|
|
$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 === '') {
|
|
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 === '') {
|
|
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;
|
|
}
|
|
|
|
/** 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 .= '<button type="button" class="ms1-doc-close" data-ms1-doc-close="1" aria-label="Fermer">×</button>';
|
|
$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;
|
|
}
|
|
}
|