Files
ms1inscription-v5/php/inc_fx_inscriptions_gestion.php
stephan fdecff66d5 Refactor registration management styles and enhance AJAX handling, update version code to 4.72.773
This commit refines the CSS styles for the registration management form, adjusting margins and padding for better layout. It also updates the JavaScript to include the epr_id in the delete action, improving the handling of AJAX requests. Additionally, the version code is incremented to 4.72.773 to reflect these changes.
2026-07-09 11:16:12 -04:00

1778 lines
76 KiB
PHP

<?php
/**
* Gestion des inscriptions — recherche, filtres, cartes, fiche.
*
* Deux entrees distinctes (meme module, navigation differente) :
* - Promoteur : /compte/inc_tableau_inscriptions_gestion → retour inc_tableau_promoteur
* - Acces v2 seul : /mobile → retour /mobile ou Mon compte
*
* MSIN-4436 — Parametre inscr_return (URL interne encodee) : retour contextuel depuis la fiche
* (ex. panneau anomalies dossards) au lieu de la liste des inscriptions.
*
* Droits : v2 uniquement (fxInscrGestionCanAccess). i18n : toujours info_prg compte.php.
*/
function fxInscrGestionIsEntry() {
return defined('MS1_INSCR_GESTION_ENTRY') && MS1_INSCR_GESTION_ENTRY;
}
/** Menu promoteur legacy (tachometre) visible sur Mon compte ? */
function fxInscrGestionUserHasLegacyPromoteurMenu($intComId)
{
if (!function_exists('fxPromoteurLegacySiteEnabled')) {
require_once __DIR__ . '/inc_fonctions.php';
}
if (!fxPromoteurLegacySiteEnabled()) {
return false;
}
global $objDatabase;
$row = $objDatabase->fxGetRow(
'SELECT com_eve_promoteur FROM inscriptions_comptes WHERE com_id = ' . intval($intComId)
);
return $row !== null && trim((string)$row['com_eve_promoteur']) !== '';
}
/** Utilisateur avec menu promoteur legacy (section Info promoteur). */
function fxInscrGestionUserHasPromoteurMenu($intComId) {
if (fxInscrGestionUserHasLegacyPromoteurMenu($intComId)) {
return true;
}
global $objDatabase;
$row = $objDatabase->fxGetRow(
'SELECT com_rapports FROM inscriptions_comptes WHERE com_id = ' . intval($intComId)
);
return $row !== null && trim((string)$row['com_rapports']) !== '';
}
/** Permissions v2 requises pour la fiche de gestion des inscriptions (web). */
function fxInscrGestionPermKeysView() {
return array('registrations.view', 'inscriptions_gestion.view');
}
function fxInscrGestionPermKeysScan() {
return array('registrations.scan');
}
/** Permission action (niveau fonction dans la fiche de gestion). */
function fxInscrGestionCanDo($intComId, $intEveId, $strActionPerm) {
fxInscrGestionLoadEveAcces();
if (!fxInscrGestionCanAccess($intComId, $intEveId)) {
return false;
}
if (fxEveAccesHasPermission(intval($intComId), intval($intEveId), $strActionPerm)) {
return true;
}
/* Kit inscriptions complet (desktop) inclut les actions de gestion. */
if (fxEveAccesHasPermission(intval($intComId), intval($intEveId), 'inscriptions.edit')) {
return true;
}
/* dossards.manage autorise aussi la modification du no dossard sur la fiche de gestion. */
if ($strActionPerm === 'inscriptions_gestion.bib_edit'
&& fxEveAccesHasPermission(intval($intComId), intval($intEveId), 'dossards.manage')) {
return true;
}
return false;
}
/** Badge cadenas (mode inspecteur super admin) sur la fiche de gestion. */
function fxInscrGestionPermInspectEcho($strPermKey) {
if (!function_exists('fxAdminPermInspectModeActive') || !fxAdminPermInspectModeActive()) {
return;
}
fxInscrGestionLoadEveAcces();
echo ' ' . fxEveAccesPermInspectBadge($strPermKey, 'inscr_gestion');
}
/** Badge + libelle pour un controle masque par les droits (mode inspecteur). */
function fxInscrGestionPermInspectEchoHidden($strPermKey, $strLabel) {
if (!function_exists('fxAdminPermInspectModeActive') || !fxAdminPermInspectModeActive()) {
return;
}
fxInscrGestionLoadEveAcces();
echo '<span class="ms1-perm-inspect-slot">';
echo fxEveAccesPermInspectBadge($strPermKey, 'inscr_gestion');
echo ' <span class="text-muted small">' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</span> ';
}
function fxInscrGestionLoadEveAcces() {
require_once __DIR__ . '/inc_fx_eve_acces.php';
}
/**
* Evenements accessibles via acces v2 uniquement (pas com_eve_promoteur, pas super admin).
*/
function fxInscrGestionGetPromoteurEveIds($intComId) {
fxInscrGestionLoadEveAcces();
return fxEveAccesGetEventIdsWithInscrGestionAccess(intval($intComId));
}
function fxInscrGestionCanAccess($intComId, $intEveId) {
if (!empty($_SESSION['usa_id'])) {
return true;
}
fxInscrGestionLoadEveAcces();
return fxEveAccesHasInscrGestionWebAccess(intval($intComId), intval($intEveId));
}
function fxInscrGestionCanScan($intComId, $intEveId) {
if (!empty($_SESSION['usa_id'])) {
return true;
}
fxInscrGestionLoadEveAcces();
return fxEveAccesHasAnyPermission(intval($intComId), intval($intEveId), fxInscrGestionPermKeysScan());
}
function fxInscrGestionBaseUrl($strLangue, $blnMobileEntry = null) {
global $vDomaine;
if ($blnMobileEntry === null) {
$blnMobileEntry = fxInscrGestionIsEntry();
}
if ($blnMobileEntry) {
return $vDomaine . '/mobile';
}
return $vDomaine . ($strLangue === 'fr' ? '/compte/inc_tableau_inscriptions_gestion' : '/account/inc_tableau_inscriptions_gestion');
}
/**
* MSIN-4436 — Encode une URL interne relative (/compte/... ou /account/...) pour inscr_return.
*/
function fxInscrGestionEncodeReturnUrl($strRelativePath) {
$strRelativePath = trim((string)$strRelativePath);
if ($strRelativePath === '' || $strRelativePath[0] !== '/') {
return '';
}
return rtrim(strtr(base64_encode($strRelativePath), '+/', '-_'), '=');
}
/**
* MSIN-4436 — Décode inscr_return ; retourne le chemin relatif validé ou ''.
*/
function fxInscrGestionDecodeReturnPath($strToken) {
$strToken = trim((string)$strToken);
if ($strToken === '') {
return '';
}
$strB64 = strtr($strToken, '-_', '+/');
$intPad = strlen($strB64) % 4;
if ($intPad > 0) {
$strB64 .= str_repeat('=', 4 - $intPad);
}
$strPath = base64_decode($strB64, true);
if ($strPath === false || $strPath === '' || $strPath[0] !== '/') {
return '';
}
if (preg_match('#^https?://#i', $strPath)) {
return '';
}
if (!preg_match('#^/(compte|account)/#', $strPath)) {
return '';
}
return $strPath;
}
/** MSIN-4436 — URL absolue de retour depuis inscr_return, ou '' si invalide. */
function fxInscrGestionDecodeReturnUrl($strToken) {
global $vDomaine;
$strPath = fxInscrGestionDecodeReturnPath($strToken);
if ($strPath === '') {
return '';
}
return $vDomaine . $strPath;
}
/** MSIN-4436 — Bouton retour fiche : URL externe (inscr_return) ou liste filtrée. */
function fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq) {
if (!empty($arrReq['inscr_return'])) {
$strReturn = fxInscrGestionDecodeReturnUrl($arrReq['inscr_return']);
if ($strReturn !== '') {
return $strReturn;
}
}
return fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array('pec_id' => null, 'pg' => $arrReq['pg']));
}
/** MSIN-4436 — Libellé du bouton retour selon le contexte d'origine. */
function fxInscrGestionResolveBackLabel($arrReq, $strLangue = 'fr') {
if (!empty($arrReq['inscr_return'])) {
$strPath = fxInscrGestionDecodeReturnPath($arrReq['inscr_return']);
if ($strPath !== '' && strpos($strPath, 'inc_tableau_gestion_epreuves') !== false) {
return fxInscrGestionT('inscr_gestion_back_bib');
}
return fxInscrGestionT('inscr_gestion_back_prev');
}
return fxInscrGestionT('inscr_gestion_back_list');
}
function fxInscrGestionEsc($str) {
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
/** Page info / i18n des libelles de gestion des inscriptions (toujours compte.php). */
function fxInscrGestionTextesPage() {
return 'compte.php';
}
function fxInscrGestionLoadTextes($strLangue) {
static $tabCache = array();
static $strCacheLang = '';
if ($strCacheLang === $strLangue && !empty($tabCache)) {
return $tabCache;
}
$tabCache = obtenirTextepage(fxInscrGestionTextesPage(), $strLangue, 2);
if (!is_array($tabCache)) {
$tabCache = array();
}
$strCacheLang = $strLangue;
return $tabCache;
}
/**
* Recherche un libelle info par clef (toutes pages), priorite compte.php > global > mobile.php.
*/
function fxInscrGestionLookupTexteDb($strClef, $strLangue) {
global $objDatabase;
static $tabMemo = array();
$strMemoKey = $strClef . "\0" . $strLangue;
if (array_key_exists($strMemoKey, $tabMemo)) {
return $tabMemo[$strMemoKey];
}
$sql = "SELECT info_texte FROM info
WHERE info_clef = '" . $objDatabase->fxEscape($strClef) . "'
AND info_langue = '" . $objDatabase->fxEscape($strLangue) . "'
AND info_actif = 1
ORDER BY FIELD(info_prg, 'compte.php', 'global', 'mobile.php', 'mobile')
LIMIT 1";
$strTexte = $objDatabase->fxGetVar($sql);
if ($strTexte === null || trim((string)$strTexte) === '' || trim((string)$strTexte) === $strClef) {
$tabMemo[$strMemoKey] = null;
return null;
}
$tabMemo[$strMemoKey] = (string)$strTexte;
return $tabMemo[$strMemoKey];
}
function fxInscrGestionResolveTexte($strClef, $strLangue) {
global $vtexte_page;
$tab = fxInscrGestionLoadTextes($strLangue);
if (isset($tab[$strClef]['info_texte'])) {
$str = trim((string)$tab[$strClef]['info_texte']);
if ($str !== '' && $str !== $strClef) {
return $str;
}
}
if (is_array($vtexte_page) && isset($vtexte_page[$strClef]['info_texte'])) {
$str = trim((string)$vtexte_page[$strClef]['info_texte']);
if ($str !== '' && $str !== $strClef) {
return $str;
}
}
$strDb = fxInscrGestionLookupTexteDb($strClef, $strLangue);
if ($strDb !== null) {
return $strDb;
}
if (function_exists('fxAdminCanUseTextEditTools') && fxAdminCanUseTextEditTools()) {
global $vPage;
$memPage = $vPage;
$vPage = fxInscrGestionTextesPage();
$str = afficheTexte($strClef, 0);
$vPage = $memPage;
if (strpos($str, '*') === 0 && substr($str, -1) === '*') {
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
if (trim($str) === '' || trim($str) === $strClef) {
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
return $str;
}
return fxInscrGestionFallbackTexte($strClef, $strLangue);
}
/** Libelles integres (secours si table info non a jour). */
function fxInscrGestionFallbackTexte($strClef, $strLangue) {
static $tabMemo = array();
if (!isset($tabMemo[$strLangue])) {
$blnFr = ($strLangue !== 'en');
$tabMemo[$strLangue] = array(
'inscr_gestion_qr_title' => $blnFr ? 'Code QR — Lecteur' : 'QR code reader',
'inscr_gestion_qr_hint' => $blnFr ? 'Pointez la caméra vers le code QR du participant.' : 'Point the camera at the participant\'s QR code.',
'inscr_gestion_qr_invalid' => $blnFr ? 'Code QR invalide ou introuvable.' : 'Invalid or unknown QR code.',
'inscr_gestion_qr_not_found' => $blnFr ? 'Participant introuvable pour cet événement.' : 'Participant not found for this event.',
'inscr_gestion_qr_wrong_event' => $blnFr ? 'Ce code QR appartient à un autre événement : %s' : 'This QR code belongs to another event: %s',
'inscr_gestion_qr_camera_error' => $blnFr ? 'Impossible d\'accéder à la caméra.' : 'Unable to access the camera.',
'inscr_gestion_bib_scan_btn' => $blnFr ? 'Scanner le numéro' : 'Scan number',
'inscr_gestion_bib_scan_hint' => $blnFr ? 'Ouvrez la caméra, cadrez le numéro, puis touchez l\'image pour lire.' : 'Open the camera, frame the number, then tap the image to read.',
'inscr_gestion_bib_scan_ready' => $blnFr ? 'Cadrez le numéro, puis touchez l\'image' : 'Frame the number, then tap the image',
'inscr_gestion_bib_scan_tap' => $blnFr
? 'Centrez les numéros que vous voulez considérer comme numéro de dossard avant de cliquer.'
: 'Center the numbers you want to use as the bib number before tapping.',
'inscr_gestion_bib_scan_scanning' => $blnFr ? 'Lecture en cours...' : 'Reading...',
'inscr_gestion_bib_scan_loading' => $blnFr ? 'Initialisation (1re fois)...' : 'Initializing (first time)...',
'inscr_gestion_bib_scan_found' => $blnFr ? 'Numéro %s détecté — validez avec OK.' : 'Number %s detected — press OK.',
'inscr_gestion_bib_scan_invalid' => $blnFr ? 'Numéro illisible. Cadrez un seul numéro.' : 'Unreadable number. Frame a single number.',
// Libelles de la barre de boutons (fiche de gestion) — editables via la table info (page compte.php).
'inscr_gestion_show_invoice' => $blnFr ? 'Afficher la facture' : 'Show invoice',
'inscr_gestion_show_transaction' => $blnFr ? 'Afficher la transaction' : 'Show transaction',
'inscr_gestion_hide_transaction' => $blnFr ? 'Masquer la transaction' : 'Hide transaction',
'inscr_gestion_renvoi' => $blnFr ? 'Retourner confirmation' : 'Resend confirmation',
'inscr_gestion_modifier_participant' => $blnFr ? 'Modifier l\'inscription' : 'Edit registration',
// Invite affichee quand la page arrive vide (aucune recherche lancee).
'inscr_gestion_search_prompt' => $blnFr
? 'Utilisez la recherche ci-dessus pour afficher les inscriptions. Laissez les champs vides et lancez la recherche pour toutes les afficher.'
: 'Use the search above to display registrations. Leave the fields empty and search to show them all.',
// MSIN-4405 — Detail du statut annule (transferee / annulee), derive en lecture.
'inscr_gestion_annulation_titre' => $blnFr ? 'Statut de l\'inscription' : 'Registration status',
'inscr_gestion_statut_annule' => $blnFr ? 'Annulée' : 'Cancelled',
'inscr_gestion_statut_transfert' => $blnFr ? 'Transférée' : 'Transferred',
'inscr_gestion_back_bib' => $blnFr ? 'Retour aux dossards' : 'Back to bibs',
'inscr_gestion_back_prev' => $blnFr ? 'Retour' : 'Back',
'inscr_gestion_chk_cancelled' => $blnFr
? 'Montrer les inscriptions annulées ou transférées'
: 'Show cancelled or transferred registrations',
);
}
if (isset($tabMemo[$strLangue][$strClef])) {
return $tabMemo[$strLangue][$strClef];
}
return $strClef;
}
/** Charge $vtexte_page pour le module gestion (entry /mobile ou compte). */
function fxInscrGestionInitTextes($strLangue) {
global $vtexte_page, $vPage;
$vPage = fxInscrGestionTextesPage();
$vtexte_page = fxInscrGestionLoadTextes($strLangue);
}
/** Libelle i18n sans echo parasite — independant de la page PHP courante. */
function fxInscrGestionT($strClef) {
global $strLangue;
if (!isset($strLangue) || $strLangue === '') {
$strLangue = 'fr';
}
$strTexte = fxInscrGestionResolveTexte($strClef, $strLangue);
if ($strTexte === $strClef || trim((string)$strTexte) === '') {
$strFallback = fxInscrGestionFallbackTexte($strClef, $strLangue);
if ($strFallback !== $strClef && trim((string)$strFallback) !== '') {
$strTexte = $strFallback;
}
}
if (function_exists('fxAdminTradMarker')) {
$strMarker = fxAdminTradMarker($strClef, 0);
if ($strMarker !== '' && strpos($strTexte, $strMarker) === false) {
$strTexte .= $strMarker;
}
}
return $strTexte;
}
/**
* Libelle "robuste" pour les pastilles du scanner : si la table info renvoie une
* clef i18n brute (donnee corrompue, ex. ancien 'inscr_mobile_bib_scan_tap' jamais
* traduit), on l'ignore et on prend le libelle integre. Evite d'afficher une clef
* technique a l'usager. Pas de marqueur d'edition ici (texte affiche dans la video).
*/
function fxInscrGestionScanLabel($strClef) {
global $strLangue;
$strLang = (isset($strLangue) && $strLangue !== '') ? $strLangue : 'fr';
$strTexte = trim((string)fxInscrGestionResolveTexte($strClef, $strLang));
if ($strTexte === '' || preg_match('/^[a-z][a-z0-9]*(_[a-z0-9]+)+$/', $strTexte)) {
$strTexte = fxInscrGestionFallbackTexte($strClef, $strLang);
}
return $strTexte;
}
function fxInscrGestionLoginActionUrl($strLangue) {
global $vDomaine;
if ($strLangue === 'en') {
return $vDomaine . '/mobile/en';
}
return $vDomaine . '/mobile';
}
function fxInscrGestionRenderLoginPage($strLangue) {
$strActionUrl = fxInscrGestionLoginActionUrl($strLangue);
$strLblUser = ($strLangue === 'fr') ? 'Nom d\'utilisateur' : 'Username';
$strLblPass = ($strLangue === 'fr') ? 'Mot de passe' : 'Password';
$strBtn = ($strLangue === 'fr') ? 'Connexion' : 'Sign in';
$strIntro = ($strLangue === 'fr')
? 'Connectez-vous pour accéder aux inscriptions mobile.'
: 'Sign in to access mobile registrations.';
echo '<div class="inscr-gestion-page inscr-gestion-login-wrap">';
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell inscr-gestion-shell--login">';
fxInscrGestionRenderPageHead(
($strLangue === 'fr') ? 'Inscriptions mobile' : 'Mobile registrations'
);
echo '<p class="inscr-gestion-login-intro text-muted">' . fxInscrGestionEsc($strIntro) . '</p>';
echo '<form action="' . fxInscrGestionEsc($strActionUrl) . '" id="frm_inscr_gestion_login" method="post" class="inscr-gestion-login-form">';
echo '<input type="hidden" name="form_action" value="' . fxInscrGestionEsc(urlencode(base64_encode('login'))) . '">';
echo '<label class="inscr-gestion-label" for="txt_login_mobile">' . fxInscrGestionEsc($strLblUser) . '</label>';
echo '<input class="form-control form-control-sm rounded-0" type="text" id="txt_login_mobile" name="txt_login" required autofocus>';
echo '<label class="inscr-gestion-label" for="txt_password_mobile">' . fxInscrGestionEsc($strLblPass) . '</label>';
echo '<input class="form-control form-control-sm rounded-0" type="password" id="txt_password_mobile" name="txt_password" required>';
echo '<button class="btn btn-primary btn-sm rounded-0 btn-block mt-3" type="submit" id="btn_login_mobile" name="btn_login">' . fxInscrGestionEsc($strBtn) . '</button>';
echo '</form></div></div>';
}
/** Libellé menu promoteur V2 (même texte que l'ancien + suffixe V2). */
function fxInscrGestionPromoteurMenuLabel() {
return afficheTexte('tableau_promoteur_menueinscription', 0) . ' V2';
}
function fxInscrGestionListBackLabel($strLangue) {
if (fxInscrGestionIsEntry()) {
return ($strLangue === 'fr') ? 'Événements' : 'Events';
}
return fxInscrGestionT('promoteur_back');
}
function fxInscrGestionShouldShowListBack($intComId) {
if (!fxInscrGestionIsEntry()) {
return true;
}
return count(fxInscrGestionGetPromoteurEveIds($intComId)) > 1;
}
function fxInscrGestionRenderNavBack($strUrl, $strLabel) {
echo '<a class="btn btn-primary rounded-pill inscr-gestion-nav-back" href="' . fxInscrGestionEsc($strUrl) . '">';
echo '<i class="fa fa-chevron-left mr-2" aria-hidden="true"></i>' . fxInscrGestionEsc($strLabel);
echo '</a>';
}
function fxInscrGestionRenderPageHead($strTitle, $strSub = '') {
echo '<div class="inscr-gestion-page-head">';
echo '<h1 class="inscr-gestion-page-title">' . fxInscrGestionEsc($strTitle) . '</h1>';
if (trim((string)$strSub) !== '') {
echo '<p class="inscr-gestion-page-sub text-muted mb-0">' . fxInscrGestionEsc($strSub) . '</p>';
}
echo '</div>';
}
function fxInscrGestionRenderSectionTitle($strLabel, $strIconClass) {
echo '<h2 class="inscr-gestion-fiche-section__title">';
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-fiche-section__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</h2>';
}
function fxInscrGestionRenderFicheSectionStart($strModifier, $strLabel, $strIconClass, $blnOpen = true) {
echo '<details class="inscr-gestion-fiche-section inscr-gestion-fiche-section--' . fxInscrGestionEsc($strModifier) . '"';
if ($blnOpen) {
echo ' open';
}
echo '>';
echo '<summary class="inscr-gestion-fiche-section__title">';
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-fiche-section__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</summary>';
echo '<div class="inscr-gestion-fiche-section__body">';
}
function fxInscrGestionRenderFicheSectionEnd() {
echo '</div></details>';
}
function fxInscrGestionRenderPanelSummary($strLabel, $strIconClass) {
echo '<i class="fa ' . fxInscrGestionEsc($strIconClass) . ' inscr-gestion-panel__icon" aria-hidden="true"></i>';
echo '<span>' . fxInscrGestionEsc($strLabel) . '</span>';
}
function fxInscrGestionParseEveId($strRaw) {
if ($strRaw === null || $strRaw === '') {
return 0;
}
$strDecoded = base64_decode(urldecode($strRaw), true);
if ($strDecoded === false || $strDecoded === '') {
return 0;
}
return (int)$strDecoded;
}
function fxInscrGestionParseRequest() {
$arr = array(
'rech_prenom' => isset($_GET['rech_prenom']) ? trim($_GET['rech_prenom']) : '',
'rech_nom' => isset($_GET['rech_nom']) ? trim($_GET['rech_nom']) : '',
'rech_no_commande' => isset($_GET['rech_no_commande']) ? trim($_GET['rech_no_commande']) : '',
'rech_nom_equipe' => isset($_GET['rech_nom_equipe']) ? trim($_GET['rech_nom_equipe']) : '',
'chk_bib' => !empty($_GET['chk_bib']) ? 1 : 0,
'chk_cancelled' => !empty($_GET['chk_cancelled']) ? 1 : 0,
'sel_rech_epreuve' => 0,
'pg' => isset($_GET['pg']) ? max(1, intval($_GET['pg'])) : 1,
'pec_id' => isset($_GET['pec_id']) ? intval($_GET['pec_id']) : 0,
// MSIN-4436 — Page d'origine (ex. gestion dossards) pour le bouton retour de la fiche.
'inscr_return' => isset($_GET['inscr_return']) ? trim((string)$_GET['inscr_return']) : '',
// q = 1 lorsque l'utilisateur a lance une recherche/filtre. La liste reste vide tant que q = 0.
'q' => 0,
);
if (!empty($_GET['sel_rech_epreuve'])) {
$decoded = base64_decode(urldecode($_GET['sel_rech_epreuve']), true);
if ($decoded !== false && $decoded !== '') {
$arr['sel_rech_epreuve'] = intval($decoded);
} else {
$arr['sel_rech_epreuve'] = intval($_GET['sel_rech_epreuve']);
}
}
// Recherche "active" si : bouton Rechercher/Filtres soumis, marqueur q transporte par les liens,
// ou au moins un critere present (utile pour le retour depuis une fiche / la pagination).
$blnSubmit = !empty($_GET['btn_recherche']) || !empty($_GET['apply_filters']) || !empty($_GET['q']);
$blnCriteria = ($arr['rech_prenom'] !== '' || $arr['rech_nom'] !== '' || $arr['rech_no_commande'] !== ''
|| $arr['rech_nom_equipe'] !== '' || $arr['sel_rech_epreuve'] > 0 || $arr['chk_bib'] || $arr['chk_cancelled']);
$arr['q'] = ($blnSubmit || $blnCriteria) ? 1 : 0;
return $arr;
}
function fxInscrGestionFilterActiveCount($arrReq) {
$int = 0;
if ($arrReq['sel_rech_epreuve'] > 0) {
$int++;
}
if ($arrReq['chk_bib']) {
$int++;
}
if ($arrReq['chk_cancelled']) {
$int++;
}
return $int;
}
function fxInscrGestionEmptyListRequest() {
return array(
'rech_prenom' => '',
'rech_nom' => '',
'rech_no_commande' => '',
'rech_nom_equipe' => '',
'chk_bib' => 0,
'chk_cancelled' => 0,
'sel_rech_epreuve' => 0,
'pg' => 1,
'pec_id' => 0,
'inscr_return' => '',
'q' => 0,
);
}
function fxInscrGestionSearchResetUrl($strBaseUrl, $intEveId) {
return fxInscrGestionBuildUrl($strBaseUrl, $intEveId, fxInscrGestionEmptyListRequest(), array('pg' => 1));
}
function fxInscrGestionBuildWhere($intEveId, $arrReq) {
global $objDatabase;
$strWhere = ' e.eve_id = ' . intval($intEveId);
if ($arrReq['rech_nom'] !== '') {
$strWhere .= ' AND p.par_nom LIKE "%' . $objDatabase->fxEscape($arrReq['rech_nom']) . '%"';
}
if ($arrReq['rech_prenom'] !== '') {
$strWhere .= ' AND p.par_prenom LIKE "%' . $objDatabase->fxEscape($arrReq['rech_prenom']) . '%"';
}
if ($arrReq['rech_no_commande'] !== '') {
$strEscCmd = $objDatabase->fxEscape($arrReq['rech_no_commande']);
$strWhere .= ' AND (e.no_commande LIKE "%' . $strEscCmd . '%" OR p.no_commande LIKE "%' . $strEscCmd . '%")';
}
if ($arrReq['rech_nom_equipe'] !== '') {
$strEscBibEquipe = $objDatabase->fxEscape($arrReq['rech_nom_equipe']);
$strWhere .= ' AND ('
. ' TRIM(p.no_bib) LIKE "%' . $strEscBibEquipe . '%"'
. ' OR TRIM(e.no_equipe) LIKE "%' . $strEscBibEquipe . '%"'
. ' OR e.pec_nom_equipe LIKE "%' . $strEscBibEquipe . '%"'
. ' OR p.par_nom_equipe LIKE "%' . $strEscBibEquipe . '%"'
. ')';
}
if ($arrReq['sel_rech_epreuve'] > 0) {
$strWhere .= ' AND p.epr_id = ' . intval($arrReq['sel_rech_epreuve']) . ' AND p.epr_id = e.epr_id';
}
if ($arrReq['chk_bib']) {
$strWhere .= " AND TRIM(p.no_bib) = ''";
}
if ($arrReq['chk_cancelled']) {
$strWhere .= ' AND e.is_cancelled = 1';
} else {
$strWhere .= ' AND e.is_cancelled = 0 AND e.pec_actif = 1';
}
return $strWhere;
}
function fxInscrGestionFetchRows($intEveId, $arrReq) {
global $objDatabase;
$strWhere = fxInscrGestionBuildWhere($intEveId, $arrReq);
$sql = 'SELECT p.no_bib, p.no_bib_remis, p.no_bib_remis_date, p.no_bib_remis_par, p.par_statut_course, p.par_id, p.par_equipe,'
. ' p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, e.pec_actif, p.rol_id,'
. ' e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe'
. ' FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE ' . $strWhere . ' AND p.pec_id = e.pec_id_original'
. ' GROUP BY e.pec_id_original'
. ' ORDER BY p.par_nom, p.par_prenom';
return $objDatabase->fxGetResults($sql);
}
function fxInscrGestionFetchRowByPec($intPecId, $intEveId) {
global $objDatabase;
$sql = 'SELECT p.no_bib, p.no_bib_remis, p.no_bib_remis_date, p.no_bib_remis_par, p.par_statut_course, p.par_id, p.par_equipe,'
. ' p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, e.pec_actif, p.rol_id,'
. ' e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe'
. ' FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE e.eve_id = ' . intval($intEveId)
. ' AND e.pec_id_original = ' . intval($intPecId)
. ' AND p.pec_id = e.pec_id_original'
. ' LIMIT 1';
return $objDatabase->fxGetRow($sql);
}
/**
* Politique de gestion des dossards en double pour un evenement.
* Pour l'instant : toujours 'warn' (avertissement non bloquant).
*
* Hook prevu pour le futur : lire un reglage par evenement afin de renvoyer
* 'block' (interdiction totale) ou 'allow' (doublons silencieux). Exemple :
* $rec = fxGetEvenementsId($intEveId);
* if (!empty($rec['eve_bib_doublon_interdit'])) return 'block';
* if (!empty($rec['eve_bib_doublon_autorise'])) return 'allow';
*
* @return string 'warn' | 'block' | 'allow'
*/
function fxInscrGestionBibDuplicatePolicy($intEveId) {
return 'warn';
}
/**
* Participants d'un evenement deja porteurs du dossard donne (hors la commande
* courante). Alimente le garde-fou "dossard en double".
*
* @param bool $blnTeam true = dossard d'equipe (e.no_equipe), false = individuel (p.no_bib)
* @return array liste de lignes ['par_nom','par_prenom','epr_id']
*/
function fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intExceptPec, $blnTeam, $intCurrentEprId = 0) {
global $objDatabase;
$strNoBib = trim((string)$strNoBib);
$intCurrentEprId = (int)$intCurrentEprId;
if (intval($intEveId) <= 0 || $strNoBib === '') {
return array();
}
$strBibCol = $blnTeam ? 'e.no_equipe' : 'p.no_bib';
$sql = 'SELECT p.par_nom, p.par_prenom, e.epr_id'
. ' FROM resultats_epreuves_commandees e, resultats_participants p'
. ' WHERE e.eve_id = ' . intval($intEveId)
. ' AND p.pec_id = e.pec_id_original'
. ' AND e.is_cancelled = 0 AND e.pec_actif = 1'
. " AND TRIM(" . $strBibCol . ") = '" . $objDatabase->fxEscape($strNoBib) . "'"
. ' AND e.pec_id_original <> ' . intval($intExceptPec)
. ' GROUP BY e.pec_id_original'
. ' ORDER BY p.par_nom, p.par_prenom';
$tab = $objDatabase->fxGetResults($sql);
if (!is_array($tab) || empty($tab)) {
return array();
}
$blnAllowInter = function_exists('fxBibEventAllowsInterEprDuplicates')
&& fxBibEventAllowsInterEprDuplicates($intEveId);
$arrOut = array();
foreach ($tab as $rowDup) {
$intDupEprId = (int)($rowDup['epr_id'] ?? 0);
if ($intCurrentEprId > 0 && $intDupEprId !== $intCurrentEprId && $blnAllowInter) {
continue;
}
$arrOut[] = $rowDup;
}
return $arrOut;
}
function fxInscrGestionEpreuveLabel($intEprId, $strLangue) {
$tab = fxGetEpreuve($intEprId);
if ($tab === null) {
return '';
}
$str = '';
if (trim($tab['epr_categorie_' . $strLangue]) !== '') {
$str .= $tab['epr_categorie_' . $strLangue] . ' — ';
}
$str .= $tab['epr_type_' . $strLangue];
if (trim($tab['epr_nom_' . $strLangue]) !== '') {
$str .= ' — ' . $tab['epr_nom_' . $strLangue];
}
return fxUnescape($str);
}
function fxInscrGestionBibDisplay($arrRow) {
if (intval($arrRow['pec_equipe']) === 1) {
return fxShowBibNumber($arrRow['no_equipe'], $arrRow['epr_id'], 'equipe');
}
return fxShowBibNumber($arrRow['no_bib'], $arrRow['epr_id'], 'participant');
}
/** Mode rendu fiche condensee (grille 2 colonnes dans l'onglet Details). */
function fxInscrGestionKvFicheCardMode($blnSet = null) {
static $blnMode = false;
if ($blnSet !== null) {
$blnMode = (bool)$blnSet;
}
return $blnMode;
}
/** Valeur longue ou courriel : occupe toute la largeur de la fiche. */
function fxInscrGestionKvValueWantsFullWidth($strValue, $blnHtml = false) {
if ($blnHtml) {
$strPlain = trim(strip_tags((string)$strValue));
} else {
$strPlain = trim((string)$strValue);
}
if ($strPlain === '') {
return false;
}
$intLen = function_exists('mb_strlen') ? mb_strlen($strPlain) : strlen($strPlain);
return $intLen > 48;
}
function fxInscrGestionRenderKvListOpen($blnFicheCard = false) {
fxInscrGestionKvFicheCardMode($blnFicheCard);
$strClass = 'inscr-gestion-kv-list';
if ($blnFicheCard) {
$strClass .= ' inscr-gestion-fiche-card';
}
echo '<div class="' . fxInscrGestionEsc($strClass) . '">';
}
function fxInscrGestionRenderKvListClose() {
fxInscrGestionKvFicheCardMode(false);
echo '</div>';
}
function fxInscrGestionRenderKvRow($strLabel, $strValue, $blnHtml = false, $strExtraClass = '') {
if (!$blnHtml && trim((string)$strValue) === '') {
return;
}
$strClass = 'inscr-gestion-kv-row';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
if (fxInscrGestionKvFicheCardMode() && fxInscrGestionKvValueWantsFullWidth($strValue, $blnHtml)) {
$strClass .= ' inscr-gestion-kv-row--full';
}
echo '<div class="' . fxInscrGestionEsc($strClass) . '">';
echo '<div class="inscr-gestion-kv-row__label">' . fxInscrGestionEsc($strLabel) . '</div>';
echo '<div class="inscr-gestion-kv-row__value">';
if ($blnHtml) {
echo $strValue;
} else {
echo fxInscrGestionEsc($strValue);
}
echo '</div></div>';
}
function fxInscrGestionFetchFicheProfile($arrRow, $strLangue) {
global $objDatabase;
$intParId = (int)$arrRow['par_id'];
$intPecId = (int)$arrRow['pec_id_original'];
$intEveId = (int)$arrRow['eve_id'];
$strLangCol = ($strLangue === 'en') ? 'en' : 'fr';
// Le titre "Capitaine" n'a de sens qu'en epreuve d'equipe : meme condition que le champ "Type"
// de la fiche (cf. fxInscrGestionRenderFiche). En individuel, on ne l'affiche pas, meme si rol_id=1.
$blnEquipe = (intval($arrRow['pec_equipe']) === 1)
&& fxIsParEquipe((int)$arrRow['pec_id_original'], (int)$arrRow['epr_id']);
$blnCapitaine = ((int)$arrRow['rol_id'] === 1) && $blnEquipe;
$tabProfile = $objDatabase->fxGetRow(
'SELECT p.par_courriel, p.par_naissance, p.par_sexe, p.par_id_original'
. ' FROM resultats_participants p'
. ' WHERE p.par_id = ' . $intParId
. ' LIMIT 1'
);
if ($tabProfile === null) {
return array(
'bln_capitaine' => $blnCapitaine,
'email' => '',
'birth_gender' => '',
'questions' => array(),
);
}
$strEmail = trim((string)$tabProfile['par_courriel']);
$strBirthGender = '';
$strNaissance = trim((string)$tabProfile['par_naissance']);
if ($strNaissance !== '' && $strNaissance !== '0000-00-00') {
$strBirthGender = fxShowDate($strNaissance, $strLangue)
. ' - '
. fxGetGender($tabProfile['par_sexe'], $strLangue);
}
$intParIdOriginal = (int)$tabProfile['par_id_original'];
$sqlQuestions = 'SELECT rq.que_choix_' . $strLangCol . ' AS que_choix,'
. ' iq.que_cart_label_' . $strLangCol . ' AS que_label,'
. ' rq.que_question_' . $strLangCol . ' AS que_question,'
. ' iq.que_tri, iq.que_id'
. ' FROM resultats_questions rq'
. ' INNER JOIN inscriptions_questions iq ON iq.que_id = rq.que_id'
. ' WHERE iq.que_cart_show = 1'
. ' AND iq.eve_id = ' . $intEveId
. ' AND rq.pec_id = ' . $intPecId
. ' AND (rq.par_id = ' . $intParId . ' OR rq.par_id = ' . $intParIdOriginal . ')'
. ' AND rq.que_actif = 1'
. " AND TRIM(COALESCE(rq.que_choix_" . $strLangCol . ", '')) <> ''"
. ' ORDER BY iq.que_tri, iq.que_id';
$tabQuestionsRaw = $objDatabase->fxGetResults($sqlQuestions);
$tabQuestions = array();
if ($tabQuestionsRaw !== null) {
for ($i = 1; $i <= count($tabQuestionsRaw); $i++) {
$tabQ = $tabQuestionsRaw[$i];
$strLabel = trim(fxUnescape($tabQ['que_label']));
if ($strLabel === '') {
$strLabel = trim(fxUnescape($tabQ['que_question']));
}
$strValue = trim(fxUnescape($tabQ['que_choix']));
if ($strLabel === '' || $strValue === '') {
continue;
}
$tabQuestions[] = array(
'label' => $strLabel,
'value' => $strValue,
);
}
}
return array(
'bln_capitaine' => $blnCapitaine,
'email' => $strEmail,
'birth_gender' => $strBirthGender,
'questions' => $tabQuestions,
);
}
function fxInscrGestionRenderKvRowFlag($strText, $strExtraClass = '') {
$strClass = 'inscr-gestion-kv-row inscr-gestion-kv-row--flag';
if ($strExtraClass !== '') {
$strClass .= ' ' . $strExtraClass;
}
if (fxInscrGestionKvFicheCardMode()) {
$strClass .= ' inscr-gestion-kv-row--full';
}
echo '<div class="' . fxInscrGestionEsc($strClass) . '">';
echo '<span class="inscr-gestion-kv-flag">' . fxInscrGestionEsc($strText) . '</span>';
echo '</div>';
}
function fxInscrGestionRenderFicheProfileKvRows($arrRow, $strLangue) {
$tabProfile = fxInscrGestionFetchFicheProfile($arrRow, $strLangue);
$blnFirstProfile = true;
$strProfileSep = 'inscr-gestion-kv-row--profile';
if ($tabProfile['bln_capitaine']) {
$strCaptainLabel = trim(fxInscrGestionT('participant-titre-capitaine'));
if ($strCaptainLabel === '') {
$strCaptainLabel = ($strLangue === 'fr') ? 'Capitaine' : 'Captain';
}
fxInscrGestionRenderKvRowFlag(
$strCaptainLabel,
$blnFirstProfile ? $strProfileSep : ''
);
$blnFirstProfile = false;
}
if ($tabProfile['email'] !== '') {
fxInscrGestionRenderKvRow(
fxInscrGestionT('inscr_gestion_email'),
$tabProfile['email'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
if ($tabProfile['birth_gender'] !== '') {
fxInscrGestionRenderKvRow(
fxInscrGestionT('inscr_gestion_birthdate'),
$tabProfile['birth_gender'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
foreach ($tabProfile['questions'] as $tabQuestion) {
fxInscrGestionRenderKvRow(
$tabQuestion['label'],
$tabQuestion['value'],
false,
$blnFirstProfile ? 'inscr-gestion-kv-row--profile' : ''
);
$blnFirstProfile = false;
}
}
/** Code statut course par défaut (Confirmé). */
function fxInscrGestionParStatutCourseDefault() {
return 'CONF';
}
function fxInscrGestionResolveParStatutCourse($strCurrent) {
$strCurrent = trim((string)$strCurrent);
return $strCurrent !== '' ? $strCurrent : fxInscrGestionParStatutCourseDefault();
}
/** Options statut course depuis info (info_description = opt). */
function fxInscrGestionGetStatutCourseOptions($strLangue) {
global $objDatabase;
static $tabCache = array();
if (isset($tabCache[$strLangue])) {
return $tabCache[$strLangue];
}
$sql = "SELECT info_texte, info_option2 FROM info"
. " WHERE info_actif = 1 AND info_clef = 'inscr_statut_course'"
. " AND info_langue = '" . $objDatabase->fxEscape($strLangue) . "'"
. " AND info_description = 'opt'"
. " ORDER BY info_trie";
$tabRows = $objDatabase->fxGetResults($sql);
$tabCache[$strLangue] = ($tabRows !== null) ? $tabRows : array();
return $tabCache[$strLangue];
}
function fxInscrGestionRenderStatutField($intParId, $strCurrent, $intEveId, $strLangue) {
$tabOptions = fxInscrGestionGetStatutCourseOptions($strLangue);
if (count($tabOptions) === 0) {
echo '<div class="inscr-gestion-muted">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_label')) . '</div>';
return;
}
$strCurrent = fxInscrGestionResolveParStatutCourse($strCurrent);
$strSelectId = 'inscr_statut_' . (int)$intParId;
echo '<select class="form-control rounded-0 inscr-gestion-statut-select" id="' . fxInscrGestionEsc($strSelectId) . '"'
. ' data-par_id="' . (int)$intParId . '"'
. ' data-eve_id="' . rawurlencode(base64_encode((string)$intEveId)) . '">';
foreach ($tabOptions as $arrOpt) {
$strCode = trim((string)$arrOpt['info_option2']);
$strLabel = trim((string)$arrOpt['info_texte']);
if ($strCode === '') {
continue;
}
echo '<option value="' . fxInscrGestionEsc($strCode) . '"'
. ($strCurrent === $strCode ? ' selected' : '') . '>'
. fxInscrGestionEsc($strLabel) . '</option>';
}
echo '</select>';
}
/** Paramètres GET pour liens (sans double-encodage). */
function fxInscrGestionQueryParams($intEveId, $arrReq, $arrExtra = array()) {
$tab = array(
'promoteur_eve_id' => base64_encode((string)$intEveId),
);
if ($arrReq['rech_prenom'] !== '') {
$tab['rech_prenom'] = $arrReq['rech_prenom'];
}
if ($arrReq['rech_nom'] !== '') {
$tab['rech_nom'] = $arrReq['rech_nom'];
}
if ($arrReq['rech_no_commande'] !== '') {
$tab['rech_no_commande'] = $arrReq['rech_no_commande'];
}
if ($arrReq['rech_nom_equipe'] !== '') {
$tab['rech_nom_equipe'] = $arrReq['rech_nom_equipe'];
}
if ($arrReq['chk_bib']) {
$tab['chk_bib'] = 1;
}
if ($arrReq['chk_cancelled']) {
$tab['chk_cancelled'] = 1;
}
if ($arrReq['sel_rech_epreuve'] > 0) {
$tab['sel_rech_epreuve'] = base64_encode((string)$arrReq['sel_rech_epreuve']);
}
if (!empty($_GET['lng'])) {
$tab['lng'] = $_GET['lng'];
}
if (!empty($arrReq['q'])) {
// Transporte l'etat "recherche lancee" pour que la pagination et le retour de fiche gardent la liste.
$tab['q'] = 1;
}
if (!empty($arrReq['pg']) && (int)$arrReq['pg'] > 1) {
$tab['pg'] = (int)$arrReq['pg'];
}
if (!empty($arrReq['inscr_return'])) {
$tab['inscr_return'] = $arrReq['inscr_return'];
}
foreach ($arrExtra as $strKey => $mixVal) {
if ($mixVal === null || $mixVal === '' || ($strKey === 'pec_id' && (int)$mixVal === 0)) {
unset($tab[$strKey]);
} else {
$tab[$strKey] = $mixVal;
}
}
return $tab;
}
function fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, $arrExtra = array()) {
$tab = fxInscrGestionQueryParams($intEveId, $arrReq, $arrExtra);
$strQs = http_build_query($tab, '', '&');
return $strBaseUrl . ($strQs !== '' ? '?' . $strQs : '');
}
function fxInscrGestionPaginationUrl($strBaseUrl, $intEveId, $arrReq) {
$tab = fxInscrGestionQueryParams($intEveId, $arrReq, array('pec_id' => null));
unset($tab['pg'], $tab['pec_id']);
$strQs = http_build_query($tab, '', '&amp;');
return $strBaseUrl . ($strQs !== '' ? '?' . $strQs : '?promoteur_eve_id=' . rawurlencode(base64_encode((string)$intEveId)));
}
function fxInscrGestionRenderPagination($strBaseUrl, $intEveId, $arrReq, $intTotal, $intPerPage, $intPage) {
if ($intTotal <= $intPerPage) {
return '';
}
return '<div class="inscr-gestion-pager">'
. fxGetPagination(
fxInscrGestionPaginationUrl($strBaseUrl, $intEveId, $arrReq),
$intTotal,
$intPerPage,
$intPage
)
. '</div>';
}
function fxInscrGestionExtractQrToken($strRaw) {
$strRaw = trim((string)$strRaw);
if ($strRaw === '') {
return '';
}
if (preg_match('/[?&]t=([^&\s#]+)/', $strRaw, $tabMatch)) {
return urldecode($tabMatch[1]);
}
if (preg_match('/^https?:\/\//i', $strRaw)) {
$tabParts = parse_url($strRaw);
if (!empty($tabParts['query'])) {
parse_str($tabParts['query'], $tabQuery);
if (!empty($tabQuery['t'])) {
return trim((string)$tabQuery['t']);
}
}
}
if (preg_match('/^[a-zA-Z0-9_-]{6,64}$/', $strRaw)) {
return $strRaw;
}
return '';
}
function fxInscrGestionResolveQrScan($strRaw, $intEveId, $strLangue) {
global $objDatabase;
$strToken = fxInscrGestionExtractQrToken($strRaw);
if ($strToken === '') {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$tabToken = $objDatabase->fxGetRow(
'SELECT qr_public_id, pec_id_original, eve_id'
. ' FROM qr_tokens'
. " WHERE qr_public_id = '" . $objDatabase->fxEscape($strToken) . "'"
. ' LIMIT 1'
);
if ($tabToken === null) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$intPecId = (int)$tabToken['pec_id_original'];
if ($intPecId <= 0) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_invalid'));
}
$tabPec = $objDatabase->fxGetRow(
'SELECT ec.pec_id_original'
. ' FROM resultats_epreuves_commandees ec'
. ' WHERE ec.pec_id_original = ' . $intPecId
. ' AND ec.eve_id = ' . intval($intEveId)
. ' AND ec.pec_actif = 1'
. ' LIMIT 1'
);
if ($tabPec !== null) {
$arrRow = fxInscrGestionFetchRowByPec($intPecId, $intEveId);
if ($arrRow === null) {
return array('state' => 'error', 'message' => fxInscrGestionT('inscr_gestion_qr_not_found'));
}
return array(
'state' => 'success',
'pec_id' => $intPecId,
);
}
$intQrEveId = (int)$tabToken['eve_id'];
if ($intQrEveId <= 0) {
$tabOther = $objDatabase->fxGetRow(
'SELECT ec.eve_id FROM resultats_epreuves_commandees ec'
. ' WHERE ec.pec_id_original = ' . $intPecId
. ' AND ec.pec_actif = 1'
. ' ORDER BY ec.pec_id_original DESC LIMIT 1'
);
if ($tabOther !== null) {
$intQrEveId = (int)$tabOther['eve_id'];
}
}
$strEveNom = '';
if ($intQrEveId > 0) {
$arrEv = fxGetEvenementsId($intQrEveId);
if (!empty($arrEv)) {
$strEveNom = fxUnescape($arrEv['eve_nom_' . $strLangue]);
}
}
if ($strEveNom === '') {
$strEveNom = '#' . $intQrEveId;
}
return array(
'state' => 'wrong_event',
'message' => str_replace('%s', $strEveNom, fxInscrGestionT('inscr_gestion_qr_wrong_event')),
'eve_nom' => $strEveNom,
);
}
function fxInscrGestionRenderEventPicker($tabEveIds, $strLangue, $strBaseUrl) {
global $objDatabase;
$strTitle = ($strLangue === 'fr') ? 'Choisir un événement' : 'Choose an event';
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell">';
fxInscrGestionRenderPageHead($strTitle);
echo '<div class="inscr-gestion-event-list">';
foreach ($tabEveIds as $intEveId) {
$qry = 'SELECT e.* FROM inscriptions_evenements e WHERE e.eve_actif = 1 AND e.eve_id = ' . intval($intEveId);
$tabEv = $objDatabase->fxGetRow($qry);
if ($tabEv === null) {
continue;
}
$strUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, fxInscrGestionParseRequest(), array());
echo '<a class="inscr-gestion-event-item" href="' . fxInscrGestionEsc($strUrl) . '">';
echo '<span class="inscr-gestion-event-item__name">' . fxInscrGestionEsc(fxUnescape($tabEv['eve_nom_' . $strLangue])) . '</span>';
echo '<i class="fa fa-chevron-right" aria-hidden="true"></i>';
echo '</a>';
}
echo '</div></div>';
}
function fxInscrGestionRenderQrPanel($intEveId, $strLangue) {
global $vDomaine;
$intComId = intval($_SESSION['com_id'] ?? 0);
if (!fxInscrGestionCanScan($intComId, $intEveId)) {
return;
}
$strQrAjaxUrl = $vDomaine . '/ajax_inscr_gestion_qr.php';
echo '<details class="inscr-gestion-panel inscr-gestion-panel--qr" id="inscr-gestion-qr-panel"';
echo ' data-qr-resolve-url="' . fxInscrGestionEsc($strQrAjaxUrl) . '"';
echo ' data-qr-eve-id="' . fxInscrGestionEsc(base64_encode((string)$intEveId)) . '"';
echo ' data-qr-lang="' . fxInscrGestionEsc($strLangue) . '"';
echo ' data-qr-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_invalid')) . '"';
echo ' data-qr-msg-wrong="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_wrong_event')) . '"';
echo ' data-qr-msg-camera="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_camera_error')) . '">';
echo '<summary class="inscr-gestion-panel__head">';
fxInscrGestionRenderPanelSummary(fxInscrGestionT('inscr_gestion_qr_title'), 'fa-qrcode');
echo '</summary>';
echo '<div class="inscr-gestion-panel__body">';
echo '<p class="inscr-gestion-qr-hint text-muted small mb-2">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_hint')) . '</p>';
echo '<div id="inscr-gestion-qr-feedback" class="alert d-none mb-2" role="alert"></div>';
echo '<div id="inscr-gestion-qr-reader-wrap" class="inscr-gestion-qr-reader-wrap">';
echo '<div id="inscr-gestion-qr-reader"></div>';
echo '</div></div></details>';
}
function fxInscrGestionRenderBibScanBlock($strBibInputId, $strLangue) {
echo '<div class="inscr-gestion-bib-scan-panel" hidden';
echo ' data-bib-input-id="' . fxInscrGestionEsc($strBibInputId) . '"';
echo ' data-msg-scanning="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_scanning')) . '"';
echo ' data-msg-loading="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_loading')) . '"';
echo ' data-msg-ready="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_ready')) . '"';
echo ' data-msg-found="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_found')) . '"';
echo ' data-msg-invalid="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_invalid')) . '"';
echo ' data-msg-camera="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_qr_camera_error')) . '">';
echo '<p class="inscr-gestion-bib-scan-hint text-muted small mb-1">' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_hint')) . '</p>';
echo '<div class="inscr-gestion-bib-scan-status small mb-1" aria-live="polite"></div>';
echo '<div class="inscr-gestion-bib-scan-capture" role="button" tabindex="0">';
echo '<div class="inscr-gestion-bib-scan-video"></div>';
// Zone-cible centrale (eclaircie) : aligne le repere visuel sur la region
// reellement lue par l'OCR (captureCenterFrame : ~55% largeur x 30% hauteur).
echo '<div class="inscr-gestion-bib-scan-target" aria-hidden="true"></div>';
echo '<span class="inscr-gestion-bib-scan-capture-label">' . fxInscrGestionEsc(fxInscrGestionScanLabel('inscr_gestion_bib_scan_tap')) . '</span>';
echo '</div>';
echo '</div>';
}
function fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq) {
global $vDomaine;
$arrEvenement = fxGetEvenementsId($intEveId);
$strEveNom = fxUnescape($arrEvenement['eve_nom_' . $strLangue]);
$intFilterCount = fxInscrGestionFilterActiveCount($arrReq);
// La page arrive vide : on n'affiche les resultats qu'apres une recherche (meme vide = tout afficher).
$blnSearchActive = !empty($arrReq['q']);
$blnFreshSearch = !empty($_GET['btn_recherche']) || !empty($_GET['apply_filters']);
$tabRows = null;
// Un seul resultat apres soumission : redirection JS (header() impossible ici, la page est deja entamee).
if ($blnSearchActive && $blnFreshSearch) {
$tabRows = fxInscrGestionFetchRows($intEveId, $arrReq);
if ($tabRows !== null && count($tabRows) === 1) {
$strFicheUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array(
'pec_id' => (int)$tabRows[1]['pec_id_original'],
'pg' => 1,
));
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell" data-inscr-gestion="1">';
echo '<script>window.location.replace(' . json_encode($strFicheUrl, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE) . ');</script>';
echo '<noscript><meta http-equiv="refresh" content="0;url=' . fxInscrGestionEsc($strFicheUrl) . '"></noscript>';
echo '</div>';
return;
}
}
$strBackUrl = fxInscrGestionIsEntry()
? $strBaseUrl
: ($vDomaine . ($strLangue === 'fr' ? '/compte/inc_tableau_promoteur' : '/account/inc_tableau_promoteur')
. '?promoteur_eve_id=' . rawurlencode(base64_encode((string)$intEveId)));
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell" data-inscr-gestion="1">';
if (fxInscrGestionShouldShowListBack((int)$_SESSION['com_id'])) {
fxInscrGestionRenderNavBack($strBackUrl, fxInscrGestionListBackLabel($strLangue));
}
fxInscrGestionRenderPageHead(fxInscrGestionT('inscr_gestion_title'), $strEveNom);
fxInscrGestionRenderQrPanel($intEveId, $strLangue);
// Recherche + filtres dans un seul panneau (repliable — ouvert par défaut tant qu'aucune recherche n'a été lancée).
global $objDatabase;
$tabEpreuves = $objDatabase->fxGetResults(
'SELECT * FROM inscriptions_epreuves e WHERE e.eve_id = ' . intval($intEveId) . ' AND epr_actif = 1 ORDER BY epr_id'
);
echo '<details class="inscr-gestion-panel"' . ($blnSearchActive ? '' : ' open') . '>';
echo '<summary class="inscr-gestion-panel__head">';
fxInscrGestionRenderPanelSummary(fxInscrGestionT('txt_recherche'), 'fa-search');
echo '</summary>';
echo '<div class="inscr-gestion-panel__body">';
echo '<form action="' . fxInscrGestionEsc($strBaseUrl) . '" method="get" class="inscr-gestion-form">';
echo '<input type="hidden" name="promoteur_eve_id" value="' . fxInscrGestionEsc(base64_encode((string)$intEveId)) . '">';
$arrFields = array(
'rech_prenom' => 'rech_prenom',
'rech_nom' => 'rech_nom',
'rech_no_commande' => 'rech_no_commande',
'rech_nom_equipe' => 'inscr_gestion_rech_bib_equipe',
);
foreach ($arrFields as $strKey => $strLbl) {
echo '<label class="inscr-gestion-label" for="' . $strKey . '">' . fxInscrGestionEsc(fxInscrGestionT($strLbl)) . '</label>';
if ($strKey === 'rech_nom_equipe') {
echo '<div class="inscr-gestion-field-block inscr-gestion-field-block--search-bib" data-inscr-field="bib">';
fxInscrGestionRenderBibScanBlock($strKey, $strLangue);
echo '<div class="inscr-gestion-bib-row">';
echo '<input class="form-control form-control-sm rounded-0" type="text" id="' . $strKey . '" name="' . $strKey . '" value="' . fxInscrGestionEsc($arrReq[$strKey]) . '">';
echo '<button class="btn btn-secondary btn-sm rounded-0 inscr-gestion-bib-scan-toggle" type="button" aria-expanded="false"'
. ' title="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '"'
. ' aria-label="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '">';
echo '<i class="fa fa-camera" aria-hidden="true"></i>';
echo '</button>';
echo '</div></div>';
continue;
}
echo '<input class="form-control form-control-sm rounded-0" type="text" id="' . $strKey . '" name="' . $strKey . '" value="' . fxInscrGestionEsc($arrReq[$strKey]) . '">';
}
$strFilterLabel = fxInscrGestionT('inscr_gestion_filters');
if ($intFilterCount > 0) {
$strFilterLabel .= ' (' . $intFilterCount . ')';
}
echo '<div class="inscr-gestion-form-subsection">';
echo '<div class="inscr-gestion-label inscr-gestion-label--subsection">' . fxInscrGestionEsc($strFilterLabel) . '</div>';
echo '<label class="inscr-gestion-label" for="sel_rech_epreuve">' . fxInscrGestionEsc(fxInscrGestionT('sel_rech_epreuve')) . '</label>';
echo '<select class="form-control form-control-sm rounded-0" id="sel_rech_epreuve" name="sel_rech_epreuve">';
echo '<option value="">—</option>';
for ($i = 1; $i <= count($tabEpreuves); $i++) {
$strOpt = fxInscrGestionEpreuveLabel($tabEpreuves[$i]['epr_id'], $strLangue);
$strSel = ($arrReq['sel_rech_epreuve'] == $tabEpreuves[$i]['epr_id']) ? ' selected' : '';
$strVal = base64_encode((string)$tabEpreuves[$i]['epr_id']);
echo '<option value="' . fxInscrGestionEsc($strVal) . '"' . $strSel . '>' . fxInscrGestionEsc($strOpt) . '</option>';
}
echo '</select>';
echo '<label class="inscr-gestion-check mt-2"><input type="checkbox" name="chk_bib" value="1"' . ($arrReq['chk_bib'] ? ' checked' : '') . '> ' . fxInscrGestionEsc(fxInscrGestionT('chk_bib')) . '</label>';
echo '<label class="inscr-gestion-check"><input type="checkbox" name="chk_cancelled" value="1"' . ($arrReq['chk_cancelled'] ? ' checked' : '') . '> ' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_chk_cancelled')) . '</label>';
echo '</div>';
echo '<div class="inscr-gestion-form-actions mt-2">';
echo '<a class="btn btn-secondary btn-sm rounded-0" href="' . fxInscrGestionEsc(fxInscrGestionSearchResetUrl($strBaseUrl, $intEveId)) . '">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_reset_search')) . '</a>';
echo '<button class="btn btn-primary btn-sm rounded-0" type="submit" name="btn_recherche" value="1">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche')) . '</button>';
echo '</div></form></div></details>';
echo '<div id="inscr-gestion-list" class="inscr-gestion-list-anchor">';
// Tant qu'aucune recherche n'a été lancée, on n'affiche aucune inscription : seulement une invite.
if (!$blnSearchActive) {
echo '<div class="alert alert-light border inscr-gestion-empty">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_search_prompt')) . '</div>';
echo '</div>';
return;
}
// Résultats (la recherche a été lancée — une recherche vide affiche toutes les inscriptions).
if ($tabRows === null) {
$tabRows = fxInscrGestionFetchRows($intEveId, $arrReq);
}
$intNbItemsParPage = 50;
$intNbTotal = ($tabRows !== null) ? count($tabRows) : 0;
$intPage = 1;
$intStart = 1;
$intEnd = $intNbItemsParPage;
if (!empty($_GET['pg'])) {
$intPage = max(1, intval($_GET['pg']));
}
if (intval($intPage) > 1) {
$intStart = ($intPage - 1) * $intNbItemsParPage + 1;
$intEnd = $intStart + $intNbItemsParPage - 1;
}
if ($intEnd > $intNbTotal) {
$intEnd = $intNbTotal;
}
echo '<div class="inscr-gestion-list-head">';
if ($strLangue === 'fr') {
echo '<span>' . (int)$intNbTotal . ' inscription' . ($intNbTotal > 1 ? 's' : '') . '</span>';
} else {
echo '<span>' . (int)$intNbTotal . ' registration' . ($intNbTotal !== 1 ? 's' : '') . '</span>';
}
if ($intNbTotal > $intNbItemsParPage) {
$intNbPages = (int)ceil($intNbTotal / $intNbItemsParPage);
echo '<span class="inscr-gestion-list-head__pg">' . ($strLangue === 'fr' ? 'Page ' : 'Page ') . $intPage . ' / ' . $intNbPages . '</span>';
}
echo '</div>';
if ($intNbTotal > 0) {
echo '<div class="inscr-gestion-list">';
// En-tete de colonnes (memes colonnes que les lignes ; masque en vue cellulaire,
// ou la liste passe en cartes). aria-hidden : purement visuel, redondant pour les
// lecteurs d'ecran car chaque valeur de ligne porte deja son sens.
echo '<div class="inscr-gestion-list-cols" aria-hidden="true">';
if ($strLangue === 'fr') {
echo '<span>Participant</span>';
echo '<span>Épreuve</span>';
echo '<span>Commande</span>';
echo '<span class="inscr-gestion-list-cols__bib">Dossard</span>';
echo '<span class="inscr-gestion-list-cols__check">Remis</span>';
} else {
echo '<span>Participant</span>';
echo '<span>Race</span>';
echo '<span>Order</span>';
echo '<span class="inscr-gestion-list-cols__bib">Bib</span>';
echo '<span class="inscr-gestion-list-cols__check">Issued</span>';
}
echo '<span></span>';
echo '</div>';
for ($j = $intStart; $j <= $intEnd; $j++) {
$row = $tabRows[$j];
$strFicheUrl = fxInscrGestionBuildUrl($strBaseUrl, $intEveId, $arrReq, array(
'pec_id' => (int)$row['pec_id_original'],
'pg' => $intPage,
));
$strBib = fxInscrGestionBibDisplay($row);
$strName = fxUnescape($row['par_nom']) . ', ' . fxUnescape($row['par_prenom']);
$strRace = fxInscrGestionEpreuveLabel($row['epr_id'], $strLangue);
$strCardClass = 'inscr-gestion-list-item';
$strCancelBadge = '';
if ($row['is_cancelled'] == '1') {
// MSIN-4405 — Cause derivee en lecture seule (aucune ecriture en base).
$strCause = fxGetAnnulationCause($row['pec_id_original'], $row['pec_actif']);
$strCardClass .= ' inscr-gestion-list-item--cancelled inscr-gestion-list-item--' . $strCause;
$strCancelBadge = ' <span class="inscr-gestion-cancel-badge inscr-gestion-cancel-badge--' . $strCause . '">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_' . $strCause)) . '</span>';
}
echo '<a class="' . $strCardClass . '" href="' . fxInscrGestionEsc($strFicheUrl) . '">';
echo '<span class="inscr-gestion-list-item__name">' . fxInscrGestionEsc($strName) . $strCancelBadge . '</span>';
echo '<span class="inscr-gestion-list-item__race">' . fxInscrGestionEsc($strRace) . '</span>';
echo '<span class="inscr-gestion-list-item__order">' . fxInscrGestionEsc($row['no_commande']) . '</span>';
echo '<span class="inscr-gestion-list-item__bib">';
echo trim($strBib) !== '' ? fxInscrGestionEsc($strBib) : '—';
echo '</span>';
echo '<span class="inscr-gestion-list-item__check">';
if ($row['no_bib_remis'] == 1) {
echo '<span class="inscr-gestion-badge inscr-gestion-badge--ok">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_enregistre')) . '</span>';
}
echo '</span>';
echo '<i class="fa fa-chevron-right inscr-gestion-list-item__arrow" aria-hidden="true"></i>';
echo '</a>';
}
echo '</div>';
echo fxInscrGestionRenderPagination($strBaseUrl, $intEveId, $arrReq, $intNbTotal, $intNbItemsParPage, $intPage);
} else {
echo '<div class="alert alert-light border inscr-gestion-empty">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche_no_result')) . '</div>';
}
echo '</div>';
}
function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $arrRow) {
global $vDomaine;
$intComId = intval($_SESSION['com_id'] ?? 0);
$intRowEveId = intval($arrRow['eve_id']);
$blnCanStatut = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.statut_edit');
$blnCanBib = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.bib_edit');
$blnCanRemis = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.bib_remis');
$blnCanEdit = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.edit');
$blnCanCancel = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.cancel');
$blnCanRestore = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.restore');
// Droit de remboursement PayPal (etape remboursement) : controle l'affichage du bouton + panneau.
$blnCanRefund = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.refund');
$blnHasEditable = ($blnCanStatut || $blnCanBib || $blnCanRemis);
$blnHasGestion = ($blnCanEdit || $blnCanCancel || $blnCanRestore || $blnCanRefund);
$blnInspect = function_exists('fxAdminPermInspectModeActive') && fxAdminPermInspectModeActive();
$strBackUrl = fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq);
$strEveCode = fxGetEvenementsUrl($arrRow['eve_id']);
$strBib = fxInscrGestionBibDisplay($arrRow);
$intParId = $arrRow['par_id'];
$blnCancelled = ($arrRow['is_cancelled'] == '1');
$blnEquipe = (intval($arrRow['pec_equipe']) === 1);
$strBibAction = $blnEquipe ? 'no_equipe' : 'no_bib';
$strBibInputId = $blnEquipe ? 'txt_no_bib_e' : 'txt_no_bib_p';
if ($arrRow['no_bib_remis'] == 1) {
$strRemisClass = 'btn-success';
$strRemisIcon = '<i class="fa fa-check" aria-hidden="true"></i>';
$intRemisVal = 0;
} else {
$strRemisClass = 'btn-primary';
$strRemisIcon = '<i class="fa fa-sharp fa-regular fa-square fa-2xs" aria-hidden="true"></i>';
$intRemisVal = 1;
}
echo '<div id="module-inscr-gestion" class="inscr-gestion-shell inscr-gestion-fiche" data-inscr-gestion="1"';
$blnFromQr = !empty($_GET['from_qr']);
if ($blnFromQr) {
echo ' data-from-qr="1"';
}
echo '>';
fxInscrGestionRenderNavBack($strBackUrl, fxInscrGestionResolveBackLabel($arrReq, $strLangue));
echo '<div class="inscr-gestion-fiche-head">';
echo '<h1 class="inscr-gestion-page-title">' . fxInscrGestionEsc(fxUnescape($arrRow['par_nom']) . ', ' . fxUnescape($arrRow['par_prenom'])) . '</h1>';
echo '</div>';
$strDetailsType = $blnEquipe && fxIsParEquipe($arrRow['pec_id_original'], $arrRow['epr_id'])
? fxInscrGestionT('promoteur_bib_show_teamates')
: fxInscrGestionT('epreuve_individuelle');
fxInscrGestionRenderFicheSectionStart('details', fxInscrGestionT('inscr_gestion_details'), 'fa-info-circle', !$blnFromQr);
fxInscrGestionRenderKvListOpen(true);
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_epreuve'), fxInscrGestionEpreuveLabel($arrRow['epr_id'], $strLangue));
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_type'), $strDetailsType);
if ($blnCancelled) {
// MSIN-4405 — Detail de l'annulation (transferee / annulee) derive en lecture seule.
$strCause = fxGetAnnulationCause($arrRow['pec_id_original'], $arrRow['pec_actif']);
fxInscrGestionRenderKvRow(
fxInscrGestionT('inscr_gestion_annulation_titre'),
'<span class="inscr-gestion-cancel-badge inscr-gestion-cancel-badge--' . $strCause . '">'
. fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_' . $strCause)) . '</span>',
true
);
}
fxInscrGestionRenderKvRow(fxInscrGestionT('inscr_gestion_modified'), fxCheckMAJ($arrRow['pec_id_original'], $intParId, $strLangue), true);
fxInscrGestionRenderFicheProfileKvRows($arrRow, $strLangue);
fxInscrGestionRenderKvListClose();
fxInscrGestionRenderFicheSectionEnd();
$strStyleEdit = $strStyleCancel = '';
$strStyleRetablir = ' d-none';
if ($blnCancelled) {
$strStyleEdit = $strStyleCancel = ' d-none';
$strStyleRetablir = '';
}
// Libelle du bouton de renvoi (facture/invitation) : passe par la table info pour etre editable.
$strRenvoi = fxInscrGestionT('inscr_gestion_renvoi');
if ($blnHasGestion || $blnInspect) {
// MSIN — a l'ouverture de la fiche, seul l'onglet Détails reste deploye.
fxInscrGestionRenderFicheSectionStart('gestion', fxInscrGestionT('inscr_gestion_section_management'), 'fa-cog', false);
echo '<div class="inscr-gestion-gestion-actions">';
// Libelle devant le bouton facture (numero de commande) — invite a afficher la facture.
echo '<span class="inscr-gestion-facture-label">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_show_invoice')) . '</span>';
echo '<a class="btn btn-primary btn-sm rounded-pill" href="' . $vDomaine . '/facture.php?no_panier=' . rawurlencode($arrRow['no_panier']) . '&amp;lang=' . $strLangue . '" target="_blank">' . fxInscrGestionEsc($arrRow['no_commande']) . '</a>';
// Bascule "Afficher la transaction" (MSIN) : place entre le numero de transaction et Modifier.
// Charge le resume de la commande en AJAX (action transaction_resume) dans le panneau ci-dessous.
echo '<button class="btn btn-info btn-sm rounded-0 btn_transaction_toggle" type="button"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-transaction-target="#inscr-gestion-transaction-panel"'
. ' data-label-show="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_show_transaction')) . '"'
. ' data-label-hide="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_hide_transaction')) . '"'
. ' aria-expanded="false">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_show_transaction')) . '</button>';
if ($blnCanEdit) {
echo '<button class="btn btn-primary btn-sm rounded-0 btn_edit_event_promo' . $strStyleEdit . '" type="button"'
. ' id="modifier_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_modifier_participant')) . '</button>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.edit');
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.edit', fxInscrGestionT('inscr_gestion_modifier_participant'));
}
if ($blnCanRefund) {
// Bouton bascule : ouvre/ferme le panneau de remboursement et declenche le chargement AJAX du formulaire.
echo '<button class="btn btn-warning btn-sm rounded-0 btn_refund_toggle" type="button"'
. ' data-inscr-field="remboursement"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-eve_id="' . (int)$intRowEveId . '"'
. ' data-refund-target="#inscr-gestion-refund-panel"'
. ' aria-expanded="false">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_remboursement')) . '</button>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.refund');
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.refund', fxInscrGestionT('inscr_gestion_remboursement'));
}
if ($blnCanCancel) {
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_cancel_event_promo' . $strStyleCancel . '" type="button"'
. ' id="annuler_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('btn_annulerinscriptions')) . '</button>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.cancel');
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.cancel', fxInscrGestionT('btn_annulerinscriptions'));
}
if ($blnCanRestore) {
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_retablir_event_promo' . $strStyleRetablir . '" type="button"'
. ' id="retablir_fiche" data-eve_code="' . fxInscrGestionEsc($strEveCode) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '" data-eve_id="' . (int)$arrRow['eve_id'] . '"'
. ' value="' . (int)$arrRow['pec_id_original'] . '">' . fxInscrGestionEsc(fxInscrGestionT('btn_retablirinscriptions')) . '</button>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.restore');
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.restore', fxInscrGestionT('btn_retablirinscriptions'));
}
// RENVOI place en dernier (MSIN-4403) : retourne la confirmation au participant.
echo '<button class="btn btn-primary btn-sm rounded-0 link_confirmation" type="button"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-no_commande="' . fxInscrGestionEsc($arrRow['no_commande']) . '"'
. ' data-lng="' . $strLangue . '">' . fxInscrGestionEsc($strRenvoi) . '</button>';
echo '</div>';
if ($blnCanRefund) {
// Panneau de remboursement : vide au depart, rempli en AJAX a la 1ere ouverture (action=refund_form).
// data-loaded evite de recharger inutilement ; data-no_panier identifie la commande cote serveur.
// Place AVANT le panneau transaction pour qu'a l'ouverture le formulaire apparaisse au-dessus du detail.
echo '<div class="inscr-gestion-refund-panel" id="inscr-gestion-refund-panel" style="margin-top:10px;"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-loaded="0" hidden></div>';
}
// Panneau de transaction : vide au depart, rempli en AJAX a la 1ere ouverture (action=transaction_resume).
// Sous les boutons, repliable, accessible a tout moment (independant du remboursement).
echo '<div class="inscr-gestion-transaction-panel" id="inscr-gestion-transaction-panel" style="margin-top:10px;"'
. ' data-no_panier="' . fxInscrGestionEsc($arrRow['no_panier']) . '"'
. ' data-loaded="0" hidden></div>';
fxInscrGestionRenderFicheSectionEnd();
}
if ($blnHasEditable || $blnInspect) {
// MSIN — champs modifiables fermes par defaut ; ouverts si arrivee par scan QR (dossard).
fxInscrGestionRenderFicheSectionStart('editable', fxInscrGestionT('inscr_gestion_section_editable'), 'fa-pencil-square-o', $blnFromQr);
if ($blnCanBib) {
$strFullBibInputId = $strBibInputId . $intParId;
// Scanner OCR toujours offert sur la fiche de gestion : la saisie se fait
// souvent au cellulaire / a une table d'inscription. La lecture est 100%
// cote client (Tesseract.js) et ne touche aucun endpoint serveur protege.
$blnCanBibScan = true;
echo '<div class="inscr-gestion-field-block" data-inscr-field="bib">';
echo '<div class="inscr-gestion-field-block__label">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_number')) . '</div>';
if ($blnCanBibScan) {
fxInscrGestionRenderBibScanBlock($strFullBibInputId, $strLangue);
}
echo '<div class="inscr-gestion-bib-row">';
echo '<input class="form-control rounded-0" type="text" id="' . $strFullBibInputId . '" value="' . fxInscrGestionEsc($strBib) . '">';
if ($blnCanBibScan) {
echo '<button class="btn btn-secondary rounded-0 inscr-gestion-bib-scan-toggle" type="button" aria-expanded="false"'
. ' title="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '"'
. ' aria-label="' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_bib_scan_btn')) . '">';
echo '<i class="fa fa-camera" aria-hidden="true"></i>';
echo '</button>';
}
echo '<button class="btn btn-primary rounded-0 btn_bib" type="button" data-action="' . $strBibAction . '"'
. ' data-eve_id="' . rawurlencode(base64_encode((string)$arrRow['eve_id'])) . '"'
. ' data-epr_id="' . (int)$arrRow['epr_id'] . '"'
. ' data-par_id="' . (int)$intParId . '"'
. ' data-pec_id="' . (int)$arrRow['pec_id_original'] . '">OK</button>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.bib_edit');
echo '</div>';
echo '</div>';
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.bib_edit', fxInscrGestionT('promoteur_bib_number'));
}
if ($blnCanStatut) {
echo '<div class="inscr-gestion-field-block inscr-gestion-field-block--statut" data-inscr-field="statut">';
echo '<div class="inscr-gestion-field-block__label">' . fxInscrGestionEsc(fxInscrGestionT('inscr_gestion_statut_label')) . '</div>';
fxInscrGestionRenderStatutField($intParId, $arrRow['par_statut_course'] ?? '', $arrRow['eve_id'], $strLangue);
fxInscrGestionPermInspectEcho('inscriptions_gestion.statut_edit');
echo '</div>';
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.statut_edit', fxInscrGestionT('inscr_gestion_statut_label'));
}
$strRemisDate = trim((string)$arrRow['no_bib_remis_date']);
if ($strRemisDate === '0000-00-00 00:00:00') {
$strRemisDate = '';
}
$strRemisPar = trim((string)$arrRow['no_bib_remis_par']);
if ($blnCanRemis) {
echo '<div class="inscr-gestion-field-block" data-inscr-field="remis">';
echo '<div class="inscr-gestion-remis-row">';
echo '<div class="inscr-gestion-remis-row__left">';
echo '<div class="inscr-gestion-field-block__label inscr-gestion-field-block__label--inline">' . fxInscrGestionEsc(fxInscrGestionT('promoteur_bib_enregistre')) . '</div>';
echo '<span id="' . (int)$intParId . '">';
echo '<a class="btn btn-sm rounded-0 btn_remis ' . $strRemisClass . '" href="#" data-action="remis"'
. ' data-table="' . rawurlencode(base64_encode('resultats_participants')) . '"'
. ' data-field="no_bib_remis" data-key="par_id" data-valeur="' . $intRemisVal . '"'
. ' data-id="' . (int)$intParId . '">' . $strRemisIcon . '</a>';
echo '</span>';
echo '</div>';
echo '<div class="inscr-gestion-remis-row__meta inscr-gestion-muted">';
echo '<span id="date_' . (int)$intParId . '">' . fxInscrGestionEsc($strRemisDate) . '</span>';
if ($strRemisDate !== '' && $strRemisPar !== '') {
echo '<span class="inscr-gestion-remis-row__sep"> — </span>';
}
echo '<span id="par_' . (int)$intParId . '">' . fxInscrGestionEsc($strRemisPar) . '</span>';
echo '</div></div></div>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.bib_remis');
} elseif ($blnInspect) {
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.bib_remis', fxInscrGestionT('promoteur_bib_enregistre'));
}
fxInscrGestionRenderFicheSectionEnd();
}
echo '</div>';
}
function fxInscrGestionRun($intEveId, $strLangue) {
$strBaseUrl = fxInscrGestionBaseUrl($strLangue);
$arrReq = fxInscrGestionParseRequest();
if ($arrReq['pec_id'] > 0) {
$arrRow = fxInscrGestionFetchRowByPec($arrReq['pec_id'], $intEveId);
if ($arrRow === null) {
echo '<div class="alert alert-danger">' . fxInscrGestionEsc(fxInscrGestionT('txt_recherche_no_result')) . '</div>';
echo '<a class="btn btn-primary rounded-0" href="' . fxInscrGestionEsc(fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq)) . '">';
echo fxInscrGestionEsc(fxInscrGestionResolveBackLabel($arrReq, $strLangue)) . '</a>';
return;
}
fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq, $arrRow);
return;
}
fxInscrGestionRenderList($intEveId, $strLangue, $strBaseUrl, $arrReq);
}