Refactor inscription management access control to implement granular permissions for transaction actions. Update CSS for improved button layout and alignment, ensuring consistent styling across action items. Increment version code to 4.72.816 to reflect these changes.

This commit is contained in:
2026-07-13 17:53:55 -04:00
parent 6c56fb7af7
commit e52c3b7b45
5 changed files with 183 additions and 47 deletions

View File

@ -243,10 +243,9 @@ switch ($strAction) {
break;
}
// L'eve_id fait foi cote serveur (jamais le client). Acces = acces a la fiche :
// qui peut voir la transaction peut voir ses remboursements (decision produit).
// L'eve_id fait foi cote serveur (jamais le client). Droit granulaire transaction.
$intEveId = intval($recCommandes['eve_id']);
if ($intEveId <= 0 || !fxInscrGestionCanAccess($intComId, $intEveId)) {
if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.transaction')) {
$tabRetour = array('state' => 'error', 'message' => 'unauthorized');
break;
}

View File

@ -4137,12 +4137,31 @@ a.ms1-trad-link.btn-aide-trad{
flex-wrap:wrap;
gap:8px;
padding:0;
align-items:center;
}
.inscr-gestion-gestion-actions .btn{
margin:0;
}
/* MSIN-4401 — Bouton + cadenas colle a droite (jamais orphelin a gauche). */
.inscr-gestion-action-item{
display:inline-flex;
flex-wrap:wrap;
align-items:center;
gap:4px 6px;
max-width:100%;
}
.inscr-gestion-action-item .ms1-perm-inspect-badge,
.inscr-gestion-action-item .ms1-perm-inspect-slot{
margin:0;
}
.inscr-gestion-action-item--inspect-only{
opacity:.92;
}
/* Remboursement PayPal — fiche gestion promoteur (formulaire + panneau doc) */
.ms1-refund-wrap{
width:100%;

View File

@ -100,7 +100,7 @@ function fxInscrGestionPermInspectEcho($strPermKey) {
return;
}
fxInscrGestionLoadEveAcces();
echo ' ' . fxEveAccesPermInspectBadge($strPermKey, 'inscr_gestion');
echo fxEveAccesPermInspectBadge($strPermKey, 'inscr_gestion');
}
/** Badge + libelle pour un controle masque par les droits (mode inspecteur). */
@ -112,7 +112,19 @@ function fxInscrGestionPermInspectEchoHidden($strPermKey, $strLabel) {
echo '<span class="ms1-perm-inspect-slot">';
echo fxEveAccesPermInspectBadge($strPermKey, 'inscr_gestion');
echo ' <span class="text-muted small">' . fxInscrGestionEsc($strLabel) . '</span>';
echo '</span> ';
echo '</span>';
}
/** MSIN-4401 — Enveloppe action + cadenas (toujours a droite du controle). */
function fxInscrGestionActionItemOpen($strExtraClass = '') {
$strExtra = trim((string)$strExtraClass);
echo '<span class="inscr-gestion-action-item'
. ($strExtra !== '' ? ' ' . $strExtra : '')
. '">';
}
function fxInscrGestionActionItemClose() {
echo '</span>';
}
function fxInscrGestionLoadEveAcces() {
@ -2099,8 +2111,13 @@ function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq,
$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');
// MSIN-4401 — granularite facture / transaction / renvoi (plus d'acces implicite sans droit).
$blnCanInvoice = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.invoice');
$blnCanTransaction = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.transaction');
$blnCanRenvoi = fxInscrGestionCanDo($intComId, $intRowEveId, 'inscriptions_gestion.renvoi');
$blnHasEditable = ($blnCanStatut || $blnCanBib || $blnCanRemis);
$blnHasGestion = ($blnCanEdit || $blnCanCancel || $blnCanRestore || $blnCanRefund);
$blnHasGestion = ($blnCanEdit || $blnCanCancel || $blnCanRestore || $blnCanRefund
|| $blnCanInvoice || $blnCanTransaction || $blnCanRenvoi);
$blnInspect = function_exists('fxAdminPermInspectModeActive') && fxAdminPermInspectModeActive();
$strBackUrl = fxInscrGestionResolveBackUrl($strBaseUrl, $intEveId, $arrReq);
@ -2183,61 +2200,101 @@ function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq,
$blnForceSectionsOpen
);
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');
// MSIN-4401 — Chaque action = wrap [controle][cadenas a droite] (evite badge orphelin a gauche).
if ($blnCanInvoice) {
fxInscrGestionActionItemOpen();
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>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.invoice');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.invoice', fxInscrGestionT('inscr_gestion_show_invoice'));
fxInscrGestionActionItemClose();
}
if ($blnCanTransaction) {
fxInscrGestionActionItemOpen();
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>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.transaction');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.transaction', fxInscrGestionT('inscr_gestion_show_transaction'));
fxInscrGestionActionItemClose();
}
if ($blnCanEdit) {
// d-none sur le wrap entier (pas seulement le bouton) pour ne pas laisser le cadenas orphelin.
fxInscrGestionActionItemOpen(trim($strStyleEdit));
echo '<button class="btn btn-primary btn-sm rounded-0 btn_edit_event_promo" 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');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.edit', fxInscrGestionT('inscr_gestion_modifier_participant'));
fxInscrGestionActionItemClose();
}
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');
fxInscrGestionActionItemOpen();
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');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.refund', fxInscrGestionT('inscr_gestion_remboursement'));
fxInscrGestionActionItemClose();
}
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');
fxInscrGestionActionItemOpen(trim($strStyleCancel));
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_cancel_event_promo" 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');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.cancel', fxInscrGestionT('btn_annulerinscriptions'));
fxInscrGestionActionItemClose();
}
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');
fxInscrGestionActionItemOpen(trim($strStyleRetablir));
echo '<button class="btn btn-secondary btn-sm rounded-0 btn_retablir_event_promo" 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');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.restore', fxInscrGestionT('btn_retablirinscriptions'));
fxInscrGestionActionItemClose();
}
if ($blnCanRenvoi) {
fxInscrGestionActionItemOpen();
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>';
fxInscrGestionPermInspectEcho('inscriptions_gestion.renvoi');
fxInscrGestionActionItemClose();
} elseif ($blnInspect) {
fxInscrGestionActionItemOpen('inscr-gestion-action-item--inspect-only');
fxInscrGestionPermInspectEchoHidden('inscriptions_gestion.renvoi', $strRenvoi);
fxInscrGestionActionItemClose();
}
// 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).
@ -2249,9 +2306,11 @@ function fxInscrGestionRenderFiche($intEveId, $strLangue, $strBaseUrl, $arrReq,
}
// 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).
if ($blnCanTransaction) {
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();
}

View File

@ -7,7 +7,7 @@
* Constantes *
*
**************/
define('_VERSION_CODE', '4.72.815');
define('_VERSION_CODE', '4.72.816');
define('_DATE_CODE', '2026-07-13');
//MSIN-4290
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');

View File

@ -0,0 +1,59 @@
-- MSIN-4401 — Droits granulaires fiche Gestion : facture, transaction, renvoi
-- Prérequis : MSIN-eve-acces-v2-phase2-multi-granular.sql (perms base)
-- Idempotent. Safe pilote.
--
-- Note : kit propriétaire (role_grants_all) n'a PAS besoin de ces lignes.
SET NAMES utf8mb4;
INSERT INTO `inscriptions_eve_permissions`
(`perm_key`, `perm_group`, `perm_scope`, `perm_label_fr`, `perm_label_en`,
`perm_description_fr`, `perm_description_en`, `perm_phase`, `perm_actif`, `perm_tri`)
VALUES
('inscriptions_gestion.invoice', 'inscriptions', 'action',
'Gestion inscription : facture', 'Registration mgmt: invoice',
'Afficher la facture (numero de commande) depuis la fiche',
'Show invoice (order number) from the sheet',
2, 1, 138),
('inscriptions_gestion.transaction', 'inscriptions', 'action',
'Gestion inscription : transaction', 'Registration mgmt: transaction',
'Afficher le resume de transaction depuis la fiche',
'Show transaction summary from the sheet',
2, 1, 139),
('inscriptions_gestion.renvoi', 'inscriptions', 'action',
'Gestion inscription : renvoyer confirmation', 'Registration mgmt: resend confirmation',
'Renvoyer la confirmation au participant depuis la fiche',
'Resend confirmation to the participant from the sheet',
2, 1, 140)
ON DUPLICATE KEY UPDATE
`perm_group` = VALUES(`perm_group`),
`perm_scope` = VALUES(`perm_scope`),
`perm_label_fr` = VALUES(`perm_label_fr`),
`perm_label_en` = VALUES(`perm_label_en`),
`perm_description_fr` = VALUES(`perm_description_fr`),
`perm_description_en` = VALUES(`perm_description_en`),
`perm_phase` = VALUES(`perm_phase`),
`perm_actif` = 1,
`perm_tri` = VALUES(`perm_tri`);
/* Kits terrain : ops = les 3 ; readonly = lecture facture+transaction */
INSERT INTO inscriptions_eve_role_permissions (role_id, perm_key)
SELECT r.role_id, p.perm_key
FROM inscriptions_eve_roles r
CROSS JOIN (
SELECT 'inscriptions_gestion.invoice' AS perm_key
UNION ALL SELECT 'inscriptions_gestion.transaction'
UNION ALL SELECT 'inscriptions_gestion.renvoi'
) p
WHERE r.role_code = 'gestion_ops'
ON DUPLICATE KEY UPDATE perm_key = VALUES(perm_key);
INSERT INTO inscriptions_eve_role_permissions (role_id, perm_key)
SELECT r.role_id, p.perm_key
FROM inscriptions_eve_roles r
CROSS JOIN (
SELECT 'inscriptions_gestion.invoice' AS perm_key
UNION ALL SELECT 'inscriptions_gestion.transaction'
) p
WHERE r.role_code IN ('gestion_readonly', 'gestion_statut')
ON DUPLICATE KEY UPDATE perm_key = VALUES(perm_key);