519 lines
17 KiB
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">×</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;
|
|
}
|
|
}
|