Files
ms1inscription-v5/php/inc_fx_eve_acces_promoteur.php

1134 lines
50 KiB
PHP

<?php
/**
* MSIN-4401 — Gestion des accès V2 côté promoteur (UI événement).
* Style aligné sur les modules v2 (hub / gestion inscriptions / dossards).
*/
require_once __DIR__ . '/inc_fx_eve_acces.php';
function fxEveAccesPromoteurT($strClef, $strLangue = 'fr')
{
$strTexte = afficheTexte($strClef, 0, 0, 1);
if ($strTexte === $strClef || trim((string)$strTexte) === '' || strpos((string)$strTexte, '*' . $strClef . '*') !== false) {
$arrFallback = array(
'tableau_promoteur_menu_eve_acces' => array('fr' => 'Gestion des accès V2', 'en' => 'Access management V2'),
'eve_acces_promoteur_title' => array('fr' => 'Gestion des accès V2', 'en' => 'Access management V2'),
'eve_acces_promoteur_intro' => array(
'fr' => 'Assignez des kits d\'accès à d\'autres personnes pour cet événement.',
'en' => 'Assign access kits to other people for this event.',
),
'eve_acces_promoteur_list_title' => array('fr' => 'Personnes assignées', 'en' => 'Assigned people'),
'eve_acces_promoteur_invite_title' => array('fr' => 'Inviter quelqu\'un', 'en' => 'Invite someone'),
'eve_acces_promoteur_invite_soon' => array(
'fr' => 'L\'invitation par courriel arrive à l\'étape suivante. Pour l\'instant, consultez les accès déjà en place.',
'en' => 'Email invitation comes in the next step. For now, review existing access.',
),
'eve_acces_promoteur_empty' => array(
'fr' => 'Aucun accès assigné pour cet événement.',
'en' => 'No access assigned for this event.',
),
'eve_acces_promoteur_col_compte' => array('fr' => 'Compte', 'en' => 'Account'),
'eve_acces_promoteur_col_kit' => array('fr' => 'Kit', 'en' => 'Kit'),
'eve_acces_promoteur_col_statut' => array('fr' => 'Statut', 'en' => 'Status'),
'eve_acces_promoteur_statut_actif' => array('fr' => 'Actif', 'en' => 'Active'),
'eve_acces_promoteur_statut_pending' => array(
'fr' => 'Compte non activé',
'en' => 'Account not activated',
),
'eve_acces_promoteur_col_depuis' => array('fr' => 'Depuis', 'en' => 'Since'),
'eve_acces_promoteur_col_expire' => array('fr' => 'Expire', 'en' => 'Expires'),
'eve_acces_promoteur_col_action' => array('fr' => 'Action', 'en' => 'Action'),
'eve_acces_promoteur_field_courriel' => array('fr' => 'Courriel', 'en' => 'Email'),
'eve_acces_promoteur_field_prenom' => array('fr' => 'Prénom', 'en' => 'First name'),
'eve_acces_promoteur_field_nom' => array('fr' => 'Nom', 'en' => 'Last name'),
'eve_acces_promoteur_field_kit' => array('fr' => 'Kit d\'accès', 'en' => 'Access kit'),
'eve_acces_promoteur_field_jours' => array('fr' => 'Durée (jours)', 'en' => 'Duration (days)'),
'eve_acces_promoteur_jours_help' => array(
'fr' => '0 = sans expiration. Utilisé surtout pour les nouveaux accès.',
'en' => '0 = no expiration. Mainly for new access grants.',
),
'eve_acces_promoteur_invite_courriel_help' => array(
'fr' => 'Saisissez d\'abord le courriel : on vérifie s\'il existe déjà un compte MS1 avant de demander le nom.',
'en' => 'Enter the email first: we check if an MS1 account already exists before asking for a name.',
),
'eve_acces_promoteur_btn_invite' => array('fr' => 'Assigner l\'accès', 'en' => 'Grant access'),
'eve_acces_promoteur_btn_resend' => array('fr' => 'Renvoyer le mail', 'en' => 'Resend email'),
'eve_acces_promoteur_btn_revoke' => array('fr' => 'Retirer', 'en' => 'Revoke'),
'eve_acces_promoteur_confirm_revoke' => array(
'fr' => 'Retirer cet accès ?',
'en' => 'Revoke this access?',
),
'eve_acces_promoteur_no_kits' => array(
'fr' => 'Aucun kit délégable disponible. Demandez à un super admin d\'activer « délégable » sur un kit.',
'en' => 'No delegable kit available. Ask a super admin to mark a kit as delegable.',
),
'eve_acces_promoteur_ok_existing' => array(
'fr' => 'Accès assigné au compte existant. Un courriel a été envoyé.',
'en' => 'Access granted to the existing account. An email was sent.',
),
'eve_acces_promoteur_ok_created' => array(
'fr' => 'Compte créé et accès assigné. Un courriel avec le lien de mot de passe a été envoyé.',
'en' => 'Account created and access granted. An email with the password link was sent.',
),
// MSIN-4478 — réinvite d'un stub jamais activé
'eve_acces_promoteur_ok_pending' => array(
'fr' => 'Invitation mise à jour. Un courriel avec le lien de mot de passe a été envoyé.',
'en' => 'Invitation updated. An email with the password link was sent.',
),
'eve_acces_promoteur_ok_revoked' => array('fr' => 'Accès retiré.', 'en' => 'Access revoked.'),
'eve_acces_promoteur_ok_resent' => array(
'fr' => 'Courriel d\'invitation renvoyé.',
'en' => 'Invitation email resent.',
),
'eve_acces_promoteur_err_courriel' => array('fr' => 'Courriel invalide.', 'en' => 'Invalid email.'),
'eve_acces_promoteur_err_kit' => array('fr' => 'Kit non autorisé.', 'en' => 'Kit not allowed.'),
'eve_acces_promoteur_err_nom' => array(
'fr' => 'Prénom et nom requis pour créer un nouveau compte.',
'en' => 'First and last name required to create a new account.',
),
'eve_acces_promoteur_err_inactive' => array(
'fr' => 'Un compte existe avec ce courriel mais il est inactif. Contactez le support.',
'en' => 'An account exists with this email but it is inactive. Contact support.',
),
'eve_acces_promoteur_err_revoke' => array('fr' => 'Retrait impossible.', 'en' => 'Unable to revoke.'),
'eve_acces_promoteur_err_resend' => array(
'fr' => 'Renvoi du courriel impossible.',
'en' => 'Unable to resend the email.',
),
'eve_acces_promoteur_err_generic' => array('fr' => 'Une erreur est survenue.', 'en' => 'An error occurred.'),
'eve_acces_promoteur_check_exists' => array(
'fr' => 'Compte déjà existant. Un courriel de confirmation sera envoyé.',
'en' => 'Existing account. A confirmation email will be sent.',
),
'eve_acces_promoteur_check_new' => array(
'fr' => 'Aucun compte avec ce courriel. Indiquez le prénom et le nom pour créer le compte.',
'en' => 'No account with this email. Enter first and last name to create the account.',
),
'eve_acces_promoteur_check_inactive' => array(
'fr' => 'Un compte existe avec ce courriel mais il est inactif. Contactez le support.',
'en' => 'An account exists with this email but it is inactive. Contact support.',
),
'eve_acces_promoteur_check_invalid' => array(
'fr' => 'Courriel invalide.',
'en' => 'Invalid email.',
),
'eve_acces_promoteur_access_denied' => array('fr' => 'Accès refusé.', 'en' => 'Access denied.'),
'eve_acces_promoteur_btn_back' => array('fr' => 'Retour', 'en' => 'Back'),
'eve_acces_promoteur_mail_new_subject' => array(
'fr' => 'Accès promoteur — créez votre mot de passe',
'en' => 'Promoter access — set your password',
),
'eve_acces_promoteur_mail_new_body' => array(
'fr' => 'Bonjour %prenom%,<br><br>Un accès vous a été accordé pour <strong>%eve_nom%</strong> (kit : %kit%).<br><br>Un compte a été créé avec le courriel <strong>%login%</strong>.<br>Cliquez ci-dessous pour choisir votre mot de passe.<br><br>Ensuite, connectez-vous ici pour travailler sur le terrain :<br><a href="%mobile_url%">%mobile_url%</a>',
'en' => 'Hello %prenom%,<br><br>You were granted access to <strong>%eve_nom%</strong> (kit: %kit%).<br><br>An account was created with <strong>%login%</strong>.<br>Click below to set your password.<br><br>Then sign in here for field use:<br><a href="%mobile_url%">%mobile_url%</a>',
),
'eve_acces_promoteur_mail_new_cta' => array('fr' => 'Choisir mon mot de passe', 'en' => 'Set my password'),
'eve_acces_promoteur_mail_existing_subject' => array(
'fr' => 'Nouvel accès — %eve_nom%',
'en' => 'New access — %eve_nom%',
),
'eve_acces_promoteur_mail_existing_body' => array(
'fr' => 'Bonjour %prenom%,<br><br>Un accès vous a été accordé pour <strong>%eve_nom%</strong> (kit : %kit%).<br><br>Ouvrez le lien ci-dessous et connectez-vous avec le compte <strong>%login%</strong> (si un autre compte est déjà ouvert, vous serez invité à vous déconnecter).',
'en' => 'Hello %prenom%,<br><br>You were granted access to <strong>%eve_nom%</strong> (kit: %kit%).<br><br>Open the link below and sign in as <strong>%login%</strong> (if another account is already open, you will be asked to sign out).',
),
'eve_acces_promoteur_mail_existing_cta' => array(
'fr' => 'Ouvrir l\'accès (mobile)',
'en' => 'Open access (mobile)',
),
);
$strLang = ($strLangue === 'en') ? 'en' : 'fr';
if (isset($arrFallback[$strClef][$strLang])) {
return $arrFallback[$strClef][$strLang];
}
}
return $strTexte;
}
function fxEveAccesPromoteurEsc($str)
{
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
function fxEveAccesPromoteurFormatDt($strDt)
{
$strDt = trim((string)$strDt);
if ($strDt === '' || strpos($strDt, '0000-00-00') === 0) {
return '—';
}
return substr($strDt, 0, 16);
}
function fxEveAccesPromoteurMenuLabel($strLangue = 'fr')
{
return fxEveAccesPromoteurT('tableau_promoteur_menu_eve_acces', $strLangue);
}
function fxEveAccesPromoteurHasInviteTeamColumn()
{
global $objDatabase;
static $blnHas = null;
if ($blnHas !== null) {
return $blnHas;
}
$tab = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_invite_team'");
$blnHas = ($tab != null && count($tab) > 0);
return $blnHas;
}
/**
* MSIN-4478 — compte stub d'invitation jamais vraiment utilisé (pas de 1re visite).
*/
function fxEveAccesPromoteurCompteIsPendingInvite($arrCompte)
{
if (!is_array($arrCompte) || empty($arrCompte['com_id'])) {
return false;
}
$strLastVisit = trim((string)($arrCompte['com_lastvisit'] ?? ''));
$blnNeverVisited = ($strLastVisit === '' || strpos($strLastVisit, '0000-00-00') === 0);
if (!$blnNeverVisited) {
return false;
}
if (!fxEveAccesPromoteurHasInviteTeamColumn()) {
return false;
}
return intval($arrCompte['com_invite_team'] ?? 0) === 1;
}
/**
* MSIN-4478 — nouveau jeton reset pour renvoyer le mail « créez votre mot de passe ».
*
* @return string token ('' si colonnes absentes)
*/
function fxEveAccesPromoteurRefreshResetToken($intComId)
{
global $objDatabase;
$intComId = intval($intComId);
if ($intComId <= 0) {
return '';
}
$strToken = uniqid('inv', true);
$strNow = function_exists('fxGetDateTime') ? fxGetDateTime() : date('Y-m-d H:i:s');
$arrSet = array();
$tabResetTok = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_reset_token'");
if ($tabResetTok != null && count($tabResetTok) > 0) {
$arrSet[] = "com_reset_token = '" . $objDatabase->fxEscape($strToken) . "'";
}
$tabResetDate = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_reset_date'");
if ($tabResetDate != null && count($tabResetDate) > 0) {
$arrSet[] = "com_reset_date = '" . $objDatabase->fxEscape($strNow) . "'";
}
if (count($arrSet) === 0) {
return '';
}
$objDatabase->fxQuery(
'UPDATE inscriptions_comptes SET ' . implode(', ', $arrSet)
. ' WHERE com_id = ' . $intComId
);
return $strToken;
}
/**
* MSIN-4478 — un seul kit actif/invite par personne+événement : révoque les autres rôles.
*/
function fxEveAccesPromoteurRevokeOtherKits($intComId, $intEveId, $intKeepRoleId, $intActorComId)
{
global $objDatabase;
$intComId = intval($intComId);
$intEveId = intval($intEveId);
$intKeepRoleId = intval($intKeepRoleId);
$intActorComId = intval($intActorComId);
if ($intComId <= 0 || $intEveId <= 0 || $intKeepRoleId <= 0 || !fxEveAccesIsEnabled()) {
return;
}
$tab = $objDatabase->fxGetResults(
"SELECT ea_id FROM inscriptions_eve_acces
WHERE com_id = $intComId
AND eve_id = $intEveId
AND role_id <> $intKeepRoleId
AND ea_statut IN ('actif', 'invite')"
);
if ($tab == null) {
return;
}
foreach ($tab as $row) {
fxEveAccesRevoke(intval($row['ea_id']), $intActorComId);
}
}
/**
* MSIN-4478 — après revoke : si stub jamais activé et plus aucun accès, désactive le compte (libère le courriel).
*/
function fxEveAccesPromoteurDeactivateOrphanInviteStub($intComId)
{
global $objDatabase;
$intComId = intval($intComId);
if ($intComId <= 0 || !fxEveAccesIsEnabled()) {
return false;
}
$arrCompte = $objDatabase->fxGetRow(
'SELECT com_id, com_actif, com_invite_team, com_lastvisit
FROM inscriptions_comptes WHERE com_id = ' . $intComId . ' LIMIT 1'
);
if (empty($arrCompte) || !fxEveAccesPromoteurCompteIsPendingInvite($arrCompte)) {
return false;
}
if (intval($arrCompte['com_actif'] ?? 0) !== 1) {
return false;
}
$intLeft = intval($objDatabase->fxGetVar(
"SELECT COUNT(*) FROM inscriptions_eve_acces
WHERE com_id = $intComId
AND ea_statut IN ('actif', 'invite')"
));
if ($intLeft > 0) {
return false;
}
$arrSet = array('com_actif = 0');
$tabResetTok = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_reset_token'");
if ($tabResetTok != null && count($tabResetTok) > 0) {
$arrSet[] = "com_reset_token = ''";
}
$objDatabase->fxQuery(
'UPDATE inscriptions_comptes SET ' . implode(', ', $arrSet)
. ' WHERE com_id = ' . $intComId
);
return true;
}
/**
* MSIN-4401 — crée un compte MS1 minimal pour invitation promoteur.
*
* @return array{state:string,message?:string,com_id?:int,reset_token?:string}
*/
function fxEveAccesPromoteurCreateCompteInvite($strCourriel, $strPrenom, $strNom, $strLangue, $intGrantedBy)
{
global $objDatabase;
$strCourriel = trim(strtolower((string)$strCourriel));
$strPrenom = trim((string)$strPrenom);
$strNom = trim((string)$strNom);
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
if ($strCourriel === '' || !filter_var($strCourriel, FILTER_VALIDATE_EMAIL)) {
return array('state' => 'error', 'message' => 'courriel');
}
if ($strPrenom === '' || $strNom === '') {
return array('state' => 'error', 'message' => 'nom');
}
$strToken = uniqid('inv', true);
$strPasswordHash = password_hash(bin2hex(random_bytes(16)), PASSWORD_DEFAULT);
$strNow = fxGetDateTime();
$strInviteFlag = fxEveAccesPromoteurHasInviteTeamColumn()
? ', com_invite_team = 1'
: '';
$blnHasConfirme = false;
$tabConfirme = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_confirme'");
if ($tabConfirme != null && count($tabConfirme) > 0) {
$blnHasConfirme = true;
}
$blnHasResetDate = false;
$tabResetDate = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_reset_date'");
if ($tabResetDate != null && count($tabResetDate) > 0) {
$blnHasResetDate = true;
}
$blnHasNoteInterne = false;
$tabNote = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_note_interne'");
if ($tabNote != null && count($tabNote) > 0) {
$blnHasNoteInterne = true;
}
$blnHasResetToken = false;
$tabResetTok = $objDatabase->fxGetResults("SHOW COLUMNS FROM inscriptions_comptes LIKE 'com_reset_token'");
if ($tabResetTok != null && count($tabResetTok) > 0) {
$blnHasResetToken = true;
}
$sql = "INSERT INTO inscriptions_comptes SET
com_prenom = '" . $objDatabase->fxEscape($strPrenom) . "',
com_nom = '" . $objDatabase->fxEscape($strNom) . "',
com_courriel = '" . $objDatabase->fxEscape($strCourriel) . "',
com_login = '" . $objDatabase->fxEscape($strCourriel) . "',
com_password = '" . $objDatabase->fxEscape($strPasswordHash) . "',
com_langue = '" . $objDatabase->fxEscape($strLangue) . "',
com_actif = 1,
com_creation = '" . $objDatabase->fxEscape($strNow) . "'
$strInviteFlag";
if ($blnHasConfirme) {
$sql .= ', com_confirme = 1';
}
if ($blnHasResetToken) {
$sql .= ", com_reset_token = '" . $objDatabase->fxEscape($strToken) . "'";
}
if ($blnHasResetDate) {
$sql .= ", com_reset_date = '" . $objDatabase->fxEscape($strNow) . "'";
}
if ($blnHasNoteInterne) {
$sql .= ", com_note_interne = 'MSIN-4401 invitation promoteur (par com_id "
. intval($intGrantedBy) . ")'";
}
if ($objDatabase->fxQuery($sql) === false) {
return array('state' => 'error', 'message' => 'insert');
}
$intComId = intval($objDatabase->fxGetVar(
"SELECT com_id FROM inscriptions_comptes
WHERE com_courriel = '" . $objDatabase->fxEscape($strCourriel) . "'
ORDER BY com_id DESC LIMIT 1"
));
if ($intComId <= 0) {
return array('state' => 'error', 'message' => 'insert');
}
return array(
'state' => 'success',
'com_id' => $intComId,
'reset_token' => $strToken,
);
}
/**
* MSIN-4401 — Signature du destinataire invite (évite spoof invite_com).
*/
function fxEveAccesPromoteurInviteSig($intComId, $intEveId)
{
$strSecret = defined('QR_SECRET_KEY') ? (string)QR_SECRET_KEY : 'ms1-invite';
return substr(hash_hmac('sha256', intval($intComId) . '|' . intval($intEveId), $strSecret), 0, 20);
}
function fxEveAccesPromoteurInviteSigValid($intComId, $intEveId, $strSig)
{
$strExpect = fxEveAccesPromoteurInviteSig($intComId, $intEveId);
return is_string($strSig) && $strSig !== '' && hash_equals($strExpect, $strSig);
}
/** Ajoute invite_com + invite_sig à une URL /mobile (ou absolue). */
function fxEveAccesPromoteurAppendInviteParams($strUrl, $intComId, $intEveId)
{
$strSep = (strpos($strUrl, '?') !== false) ? '&' : '?';
return $strUrl . $strSep
. 'invite_com=' . intval($intComId)
. '&invite_sig=' . rawurlencode(fxEveAccesPromoteurInviteSig($intComId, $intEveId));
}
function fxEveAccesPromoteurSendInviteMail($arrCompte, $arrEve, $arrRole, $strLangue, $blnNewAccount, $strResetToken = '')
{
global $vDomaine, $vEmail, $vClient;
if (!function_exists('fxSendMail')) {
require_once __DIR__ . '/inc_mail.php';
}
if (!function_exists('fxGetMailFormat')) {
require_once __DIR__ . '/inc_fonctions.php';
}
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
$strEveNom = fxUnescape($arrEve['eve_nom_' . $strLangue] ?? ($arrEve['eve_nom_fr'] ?? ''));
$strKit = ($strLangue === 'en')
? ($arrRole['role_label_en'] ?? $arrRole['role_label_fr'] ?? '')
: ($arrRole['role_label_fr'] ?? '');
$strPrenom = fxUnescape($arrCompte['com_prenom'] ?? '');
$strLogin = $arrCompte['com_login'] ?? $arrCompte['com_courriel'];
$intEveId = intval($arrEve['eve_id'] ?? 0);
$intComId = intval($arrCompte['com_id'] ?? 0);
$strEveParam = urlencode(base64_encode((string)$intEveId));
$strComptePath = ($strLangue === 'en') ? '/account' : '/compte';
// MSIN-4401 — Lien terrain : /mobile + identité destinataire (invite_com/sig).
$strMobileUrl = $vDomaine . '/mobile?promoteur_eve_id=' . $strEveParam;
if ($strLangue === 'en') {
$strMobileUrl = $vDomaine . '/mobile/en?promoteur_eve_id=' . $strEveParam;
}
$strMobileUrl = fxEveAccesPromoteurAppendInviteParams($strMobileUrl, $intComId, $intEveId);
// Placeholders Info (contenu HTML géré dans table info).
$arrMailVars = array(
'%prenom%' => htmlspecialchars($strPrenom, ENT_QUOTES, 'UTF-8'),
'%eve_nom%' => htmlspecialchars($strEveNom, ENT_QUOTES, 'UTF-8'),
'%kit%' => htmlspecialchars($strKit, ENT_QUOTES, 'UTF-8'),
'%login%' => htmlspecialchars((string)$strLogin, ENT_QUOTES, 'UTF-8'),
'%mobile_url%' => htmlspecialchars($strMobileUrl, ENT_QUOTES, 'UTF-8'),
);
if ($blnNewAccount && $strResetToken !== '') {
$subject = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_subject', $strLangue), $arrMailVars);
$message = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_body', $strLangue), $arrMailVars);
$attachment_label = fxEveAccesPromoteurT('eve_acces_promoteur_mail_new_cta', $strLangue);
$attachment_url = $vDomaine . $strComptePath . '/reset?t=' . urlencode($strResetToken)
. '&m=' . urlencode($arrCompte['com_courriel'])
. '&id=' . intval($arrCompte['com_id']);
} else {
$subject = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_subject', $strLangue), $arrMailVars);
$message = strtr(fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_body', $strLangue), $arrMailVars);
$attachment_label = fxEveAccesPromoteurT('eve_acces_promoteur_mail_existing_cta', $strLangue);
$attachment_url = $strMobileUrl;
}
$body = fxGetMailFormat(false, false, true, false, $strLangue);
$body = str_replace('%logo%', $vDomaine . '/images/logoms1inscription.png', $body);
$body = str_replace('%to_mail%', $arrCompte['com_courriel'], $body);
$body = str_replace('%subject%', $subject, $body);
$body = str_replace('%message%', $message, $body);
$body = str_replace('%attachment_url%', $attachment_url, $body);
$body = str_replace('%attachment_label%', $attachment_label, $body);
fxSendMail(
$subject,
$body,
$arrCompte['com_courriel'],
trim(($arrCompte['com_prenom'] ?? '') . ' ' . ($arrCompte['com_nom'] ?? '')),
$vEmail,
$vClient,
1,
0
);
return true;
}
/**
* MSIN-4401 / MSIN-4478 — invitation / assignation par promoteur.
*
* @return array{state:string,message:string,created?:bool}
*/
function fxEveAccesPromoteurInvite($intEveId, $intGrantedBy, $arrPost, $strLangue = 'fr')
{
global $objDatabase;
$intEveId = intval($intEveId);
$intGrantedBy = intval($intGrantedBy);
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
if (!fxEveAccesCanTeamInvite($intGrantedBy, $intEveId)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_generic', $strLangue));
}
$strCourriel = trim(strtolower((string)($arrPost['invite_courriel'] ?? '')));
$strPrenom = trim((string)($arrPost['invite_prenom'] ?? ''));
$strNom = trim((string)($arrPost['invite_nom'] ?? ''));
$intRoleId = intval($arrPost['invite_role_id'] ?? 0);
$intJours = intval($arrPost['invite_jours'] ?? 0);
if ($intJours < 0) {
$intJours = 0;
}
if ($intJours > 3650) {
$intJours = 3650;
}
if ($strCourriel === '' || !filter_var($strCourriel, FILTER_VALIDATE_EMAIL)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_courriel', $strLangue));
}
if (!fxEveAccesRoleIsDelegable($intRoleId)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_kit', $strLangue));
}
$arrEve = $objDatabase->fxGetRow(
'SELECT eve_id, eve_nom_fr, eve_nom_en FROM inscriptions_evenements WHERE eve_id = ' . $intEveId . ' LIMIT 1'
);
$arrRole = $objDatabase->fxGetRow(
'SELECT role_id, role_code, role_label_fr, role_label_en FROM inscriptions_eve_roles WHERE role_id = ' . $intRoleId . ' LIMIT 1'
);
// fxGetRow peut retourner false (pas null) selon l'environnement.
if (empty($arrEve) || empty($arrRole)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_generic', $strLangue));
}
$strCompteCols = 'com_id, com_prenom, com_nom, com_courriel, com_login, com_actif, com_lastvisit';
if (fxEveAccesPromoteurHasInviteTeamColumn()) {
$strCompteCols .= ', com_invite_team';
}
$rowExisting = $objDatabase->fxGetRow(
"SELECT $strCompteCols
FROM inscriptions_comptes
WHERE com_courriel = '" . $objDatabase->fxEscape($strCourriel) . "'
ORDER BY com_actif DESC, com_id DESC
LIMIT 1"
);
$blnCreated = false;
$blnPending = false;
$strResetToken = '';
$arrCompte = null;
if (!empty($rowExisting) && is_array($rowExisting)) {
$blnPending = fxEveAccesPromoteurCompteIsPendingInvite($rowExisting);
// MSIN-4478 — stub désactivé après revoke : on le réactive pour réinviter.
if (intval($rowExisting['com_actif']) !== 1) {
if (!$blnPending) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_inactive', $strLangue));
}
$objDatabase->fxQuery(
'UPDATE inscriptions_comptes SET com_actif = 1 WHERE com_id = ' . intval($rowExisting['com_id'])
);
$rowExisting['com_actif'] = 1;
}
$arrCompte = $rowExisting;
if ($strPrenom !== '' && trim((string)$arrCompte['com_prenom']) === '') {
$objDatabase->fxQuery(
"UPDATE inscriptions_comptes SET com_prenom = '" . $objDatabase->fxEscape($strPrenom) . "'
WHERE com_id = " . intval($arrCompte['com_id'])
);
$arrCompte['com_prenom'] = $strPrenom;
}
if ($strNom !== '' && trim((string)$arrCompte['com_nom']) === '') {
$objDatabase->fxQuery(
"UPDATE inscriptions_comptes SET com_nom = '" . $objDatabase->fxEscape($strNom) . "'
WHERE com_id = " . intval($arrCompte['com_id'])
);
$arrCompte['com_nom'] = $strNom;
}
if ($blnPending) {
$strResetToken = fxEveAccesPromoteurRefreshResetToken(intval($arrCompte['com_id']));
}
} else {
if ($strPrenom === '' || $strNom === '') {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_nom', $strLangue));
}
$arrCreate = fxEveAccesPromoteurCreateCompteInvite(
$strCourriel,
$strPrenom,
$strNom,
$strLangue,
$intGrantedBy
);
if (($arrCreate['state'] ?? '') !== 'success') {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_generic', $strLangue));
}
$blnCreated = true;
$blnPending = true;
$strResetToken = (string)($arrCreate['reset_token'] ?? '');
$arrCompte = array(
'com_id' => intval($arrCreate['com_id']),
'com_prenom' => $strPrenom,
'com_nom' => $strNom,
'com_courriel' => $strCourriel,
'com_login' => $strCourriel,
'com_invite_team' => 1,
'com_lastvisit' => '',
);
}
// MSIN-4478 — changer de kit = remplacer, pas empiler.
fxEveAccesPromoteurRevokeOtherKits(
intval($arrCompte['com_id']),
$intEveId,
$intRoleId,
$intGrantedBy
);
// Stub jamais activé → statut invite + mail mot de passe (même au 2e envoi).
$strGrantStatut = $blnPending ? 'invite' : 'actif';
$arrGrant = fxEveAccesGrant(
intval($arrCompte['com_id']),
$intEveId,
$intRoleId,
$intJours,
$intGrantedBy,
'MSIN-4478 invitation promoteur',
$strGrantStatut
);
if (($arrGrant['state'] ?? '') !== 'success') {
return array(
'state' => 'error',
'message' => $arrGrant['message'] ?? fxEveAccesPromoteurT('eve_acces_promoteur_err_generic', $strLangue),
);
}
// L'accès est déjà enregistré : un échec mail ne doit pas faire planter la page.
try {
fxEveAccesPromoteurSendInviteMail(
$arrCompte,
$arrEve,
$arrRole,
$strLangue,
$blnPending,
$strResetToken
);
} catch (Throwable $e) {
// ignore
}
if ($blnCreated) {
$strMsg = fxEveAccesPromoteurT('eve_acces_promoteur_ok_created', $strLangue);
} elseif ($blnPending) {
$strMsg = fxEveAccesPromoteurT('eve_acces_promoteur_ok_pending', $strLangue);
} else {
$strMsg = fxEveAccesPromoteurT('eve_acces_promoteur_ok_existing', $strLangue);
}
return array(
'state' => 'success',
'created' => $blnCreated,
'message' => $strMsg,
);
}
/**
* MSIN-4401 / MSIN-4478 — révocation par le promoteur (team.manage ou auteur du grant / super admin).
*/
function fxEveAccesPromoteurRevoke($intEaId, $intActorComId, $intEveId)
{
global $objDatabase;
$intEaId = intval($intEaId);
$intActorComId = intval($intActorComId);
$intEveId = intval($intEveId);
$tab = $objDatabase->fxGetRow(
'SELECT * FROM inscriptions_eve_acces WHERE ea_id = ' . $intEaId . ' LIMIT 1'
);
if ($tab === null || intval($tab['eve_id']) !== $intEveId) {
return array('state' => 'error');
}
$blnOk = fxEveAccesCanTeamManage($intActorComId, $intEveId)
|| (intval($tab['ea_granted_by'] ?? 0) === $intActorComId && fxEveAccesCanTeamInvite($intActorComId, $intEveId));
if (!$blnOk) {
return array('state' => 'error');
}
$arrResult = fxEveAccesRevoke($intEaId, $intActorComId);
if (($arrResult['state'] ?? '') === 'success') {
fxEveAccesPromoteurDeactivateOrphanInviteStub(intval($tab['com_id'] ?? 0));
}
return $arrResult;
}
/**
* MSIN-4401 — renvoi du courriel d'invitation (lien /mobile + invite_com/sig).
* Compte non activé : nouveau jeton reset + mail « créer mot de passe ».
*/
function fxEveAccesPromoteurResendMail($intEaId, $intActorComId, $intEveId, $strLangue = 'fr')
{
global $objDatabase;
$intEaId = intval($intEaId);
$intActorComId = intval($intActorComId);
$intEveId = intval($intEveId);
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
if ($intEaId <= 0 || $intEveId <= 0 || !fxEveAccesCanTeamInvite($intActorComId, $intEveId)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$tab = $objDatabase->fxGetRow(
'SELECT * FROM inscriptions_eve_acces WHERE ea_id = ' . $intEaId . ' LIMIT 1'
);
if ($tab === null || intval($tab['eve_id']) !== $intEveId) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$strStatut = (string)($tab['ea_statut'] ?? '');
if ($strStatut !== 'actif' && $strStatut !== 'invite') {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$blnOk = fxEveAccesCanTeamManage($intActorComId, $intEveId)
|| (intval($tab['ea_granted_by'] ?? 0) === $intActorComId);
if (!$blnOk) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$intComId = intval($tab['com_id'] ?? 0);
$arrCompte = $objDatabase->fxGetRow(
'SELECT * FROM inscriptions_comptes WHERE com_id = ' . $intComId . ' LIMIT 1'
);
if ($arrCompte === null || intval($arrCompte['com_actif'] ?? 0) !== 1) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$arrEve = function_exists('fxGetEvenementsId') ? fxGetEvenementsId($intEveId) : null;
if (!is_array($arrEve)) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$arrRole = $objDatabase->fxGetRow(
'SELECT * FROM inscriptions_eve_roles WHERE role_id = ' . intval($tab['role_id']) . ' LIMIT 1'
);
if ($arrRole === null) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
$strLastVisit = trim((string)($arrCompte['com_lastvisit'] ?? ''));
$blnInviteTeam = intval($arrCompte['com_invite_team'] ?? 0) === 1;
$blnPending = ($strStatut === 'invite')
|| fxEveAccesPromoteurCompteIsPendingInvite($arrCompte)
|| ($blnInviteTeam && ($strLastVisit === '' || strpos($strLastVisit, '0000-00-00') === 0));
$strResetToken = '';
if ($blnPending) {
$strResetToken = fxEveAccesPromoteurRefreshResetToken($intComId);
}
try {
fxEveAccesPromoteurSendInviteMail($arrCompte, $arrEve, $arrRole, $strLangue, $blnPending, $strResetToken);
} catch (Throwable $e) {
return array('state' => 'error', 'message' => fxEveAccesPromoteurT('eve_acces_promoteur_err_resend', $strLangue));
}
return array(
'state' => 'success',
'message' => fxEveAccesPromoteurT('eve_acces_promoteur_ok_resent', $strLangue),
);
}
/** MSIN-4401 — rendu page promoteur (invitation + liste). */
function fxEveAccesPromoteurRenderPage($intEveId, $strLangue = 'fr', $strFlash = '', $strError = '')
{
global $vDomaine;
$intEveId = intval($intEveId);
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
$strPage = ($strLangue === 'fr') ? '/compte' : '/account';
$strBackUrl = function_exists('fxGetPromoteurBackUrl')
? fxGetPromoteurBackUrl($intEveId, $strPage)
: ($vDomaine . $strPage);
$intActor = intval($_SESSION['com_id'] ?? 0);
if (!empty($_SESSION['usa_id'])) {
$intActor = intval($_SESSION['usa_id']);
}
$arrEve = function_exists('fxGetEvenementsId') ? fxGetEvenementsId($intEveId) : null;
$strEveNom = '';
if (is_array($arrEve) && !empty($arrEve['eve_nom_' . $strLangue])) {
$strEveNom = fxUnescape($arrEve['eve_nom_' . $strLangue]);
}
$arrRows = fxEveAccesListByEveId($intEveId, true);
$arrRoles = fxEveAccesGetDelegableRoles();
$strRoleLabelKey = ($strLangue === 'en') ? 'role_label_en' : 'role_label_fr';
$blnCanManage = fxEveAccesCanTeamManage($intActor, $intEveId);
$strFormAction = $vDomaine . $strPage . '/inc_tableau_eve_acces?promoteur_eve_id='
. urlencode(base64_encode($intEveId)) . '&lng=' . $strLangue;
echo '<div class="eve-acces-promoteur-page" id="module-eve-acces-promoteur">';
echo '<a class="btn btn-primary rounded-pill" id="link_back" href="' . fxEveAccesPromoteurEsc($strBackUrl) . '">';
echo '<i class="fa fa-chevron-left mr-2" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(afficheTexte('promoteur_back', 0));
echo '</a>';
echo '<div class="eve-acces-promoteur-head mt-4 mb-3">';
echo '<h1 class="h4 mb-1">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_title', $strLangue));
if (function_exists('fxEveAccesPermInspectBadge')) {
echo ' ' . fxEveAccesPermInspectBadge('team.invite', 'menu');
echo ' ' . fxEveAccesPermInspectBadge('team.manage', 'menu');
}
echo '</h1>';
if ($strEveNom !== '') {
echo '<p class="text-muted mb-1">' . fxEveAccesPromoteurEsc($strEveNom) . '</p>';
}
echo '<p class="text-muted small mb-0">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_intro', $strLangue)) . '</p>';
echo '</div>';
if ($strFlash !== '') {
echo '<div class="alert alert-success">' . fxEveAccesPromoteurEsc($strFlash) . '</div>';
}
if ($strError !== '') {
echo '<div class="alert alert-danger">' . fxEveAccesPromoteurEsc($strError) . '</div>';
}
echo '<div class="card mb-3 eve-acces-promoteur-card">';
echo '<div class="card-header d-flex align-items-center">';
echo '<i class="fa fa-user-plus mr-2" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_invite_title', $strLangue));
echo '</div>';
echo '<div class="card-body">';
if (count($arrRoles) === 0) {
echo '<p class="text-muted small mb-0">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_no_kits', $strLangue)) . '</p>';
} else {
$strAjaxUrl = $vDomaine . '/ajax_verif_courriel.php';
echo '<form method="post" action="' . fxEveAccesPromoteurEsc($strFormAction)
. '" class="eve-acces-promoteur-invite-form" id="eve_acces_invite_form"'
. ' data-ajax-url="' . fxEveAccesPromoteurEsc($strAjaxUrl) . '"'
. ' data-msg-exists="' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_check_exists', $strLangue)) . '"'
. ' data-msg-new="' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_check_new', $strLangue)) . '">';
echo '<input type="hidden" name="eve_acces_action" value="invite">';
echo '<div class="form-row">';
echo '<div class="form-group col-md-12">';
echo '<label for="invite_courriel">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_field_courriel', $strLangue)) . '</label>';
echo '<input class="form-control rounded-0" type="email" id="invite_courriel" name="invite_courriel" required maxlength="100" autocomplete="email">';
echo '<div id="invite_courriel_feedback" class="small mt-2 mb-0" style="display:none;" role="status" aria-live="polite"></div>';
echo '</div>';
echo '</div>';
echo '<div class="form-row" id="invite_name_row" style="display:none;">';
echo '<div class="form-group col-md-6">';
echo '<label for="invite_prenom">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_field_prenom', $strLangue))
. ' <span class="text-danger" aria-hidden="true">*</span></label>';
echo '<input class="form-control rounded-0" type="text" id="invite_prenom" name="invite_prenom" maxlength="50">';
echo '</div>';
echo '<div class="form-group col-md-6">';
echo '<label for="invite_nom">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_field_nom', $strLangue))
. ' <span class="text-danger" aria-hidden="true">*</span></label>';
echo '<input class="form-control rounded-0" type="text" id="invite_nom" name="invite_nom" maxlength="50">';
echo '</div>';
echo '</div>';
echo '<div class="form-row">';
echo '<div class="form-group col-md-6">';
echo '<label for="invite_role_id">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_field_kit', $strLangue)) . '</label>';
echo '<select class="form-control rounded-0" id="invite_role_id" name="invite_role_id" required>';
foreach ($arrRoles as $arrRole) {
$strLabel = trim((string)($arrRole[$strRoleLabelKey] ?? $arrRole['role_label_fr'] ?? ''));
echo '<option value="' . intval($arrRole['role_id']) . '">' . fxEveAccesPromoteurEsc($strLabel) . '</option>';
}
echo '</select>';
echo '</div>';
echo '<div class="form-group col-md-3">';
echo '<label for="invite_jours">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_field_jours', $strLangue)) . '</label>';
echo '<input class="form-control rounded-0" type="number" id="invite_jours" name="invite_jours" min="0" max="3650" value="0">';
echo '<small class="form-text text-muted">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_jours_help', $strLangue)) . '</small>';
echo '</div>';
echo '<div class="form-group col-md-3 d-flex align-items-end">';
echo '<button type="submit" class="btn btn-primary rounded-0 btn-block" id="invite_submit_btn">';
echo '<i class="fa fa-paper-plane mr-1" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_btn_invite', $strLangue));
echo '</button>';
echo '</div>';
echo '</div>';
echo '<p class="small text-muted mb-0">'
. fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_invite_courriel_help', $strLangue))
. '</p>';
echo '</form>';
fxEveAccesPromoteurRenderInviteScript();
}
echo '</div></div>';
echo '<div class="card mb-3 eve-acces-promoteur-card">';
echo '<div class="card-header d-flex justify-content-between align-items-center flex-wrap">';
echo '<span><i class="fa fa-users mr-2" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_list_title', $strLangue));
echo '</span>';
echo '<span class="small text-muted">' . count($arrRows) . '</span>';
echo '</div>';
echo '<div class="card-body p-0">';
echo '<div class="table-responsive mb-0">';
echo '<table class="table table-sm table-striped table-hover mb-0">';
echo '<thead class="thead-light"><tr>';
echo '<th>' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_col_compte', $strLangue)) . '</th>';
echo '<th>' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_col_kit', $strLangue)) . '</th>';
echo '<th class="text-center">' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_col_statut', $strLangue)) . '</th>';
echo '<th>' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_col_depuis', $strLangue)) . '</th>';
echo '<th>' . fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_col_expire', $strLangue)) . '</th>';
echo '<th></th>';
echo '</tr></thead><tbody>';
if (count($arrRows) === 0) {
echo '<tr><td colspan="6" class="text-muted px-3 py-3">'
. fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_empty', $strLangue))
. '</td></tr>';
} else {
foreach ($arrRows as $arrRow) {
$strCompte = trim(($arrRow['com_prenom'] ?? '') . ' ' . ($arrRow['com_nom'] ?? ''));
$strKit = trim((string)($arrRow[$strRoleLabelKey] ?? $arrRow['role_label_fr'] ?? ''));
if ($strKit === '') {
$strKit = (string)($arrRow['role_code'] ?? '');
}
$strStatut = (string)($arrRow['ea_statut'] ?? '');
$strLastVisit = trim((string)($arrRow['com_lastvisit'] ?? ''));
$blnInviteTeam = intval($arrRow['com_invite_team'] ?? 0) === 1;
// Nouveau compte invitation : en attente tant que pas activé (mdp) / jamais connecté.
$blnPending = ($strStatut === 'invite')
|| ($blnInviteTeam && ($strLastVisit === '' || strpos($strLastVisit, '0000-00-00') === 0));
$strBadge = 'secondary';
$strStatutLabel = $strStatut;
if ($blnPending) {
$strBadge = 'warning';
$strStatutLabel = fxEveAccesPromoteurT('eve_acces_promoteur_statut_pending', $strLangue);
} elseif ($strStatut === 'actif') {
$strBadge = 'success';
$strStatutLabel = fxEveAccesPromoteurT('eve_acces_promoteur_statut_actif', $strLangue);
}
$blnCanRevoke = $blnCanManage
|| (intval($arrRow['ea_granted_by'] ?? 0) === $intActor);
echo '<tr>';
echo '<td>';
echo '<div>' . fxEveAccesPromoteurEsc($strCompte) . '</div>';
echo '<div class="small text-muted">' . fxEveAccesPromoteurEsc($arrRow['com_courriel'] ?? '') . '</div>';
echo '</td>';
echo '<td>' . fxEveAccesPromoteurEsc($strKit) . '</td>';
echo '<td class="text-center"><span class="badge badge-' . $strBadge . '">'
. fxEveAccesPromoteurEsc($strStatutLabel) . '</span></td>';
echo '<td class="text-nowrap small">'
. fxEveAccesPromoteurEsc(fxEveAccesPromoteurFormatDt($arrRow['ea_created_at'] ?? '')) . '</td>';
echo '<td class="text-nowrap small">'
. fxEveAccesPromoteurEsc(fxEveAccesPromoteurFormatDt($arrRow['ea_expires_at'] ?? '')) . '</td>';
echo '<td class="text-right text-nowrap">';
if ($blnCanRevoke && intval($arrRow['com_id']) !== $intActor) {
echo '<form method="post" action="' . fxEveAccesPromoteurEsc($strFormAction) . '" class="d-inline mr-1">';
echo '<input type="hidden" name="eve_acces_action" value="resend">';
echo '<input type="hidden" name="ea_id" value="' . intval($arrRow['ea_id']) . '">';
echo '<button type="submit" class="btn btn-sm btn-outline-primary rounded-0" title="'
. fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_btn_resend', $strLangue)) . '">';
echo '<i class="fa fa-envelope mr-1" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_btn_resend', $strLangue));
echo '</button></form>';
echo '<form method="post" action="' . fxEveAccesPromoteurEsc($strFormAction) . '" class="d-inline"';
echo ' onsubmit="return confirm(\'' . fxEveAccesPromoteurEsc(
fxEveAccesPromoteurT('eve_acces_promoteur_confirm_revoke', $strLangue)
) . '\');">';
echo '<input type="hidden" name="eve_acces_action" value="revoke">';
echo '<input type="hidden" name="ea_id" value="' . intval($arrRow['ea_id']) . '">';
echo '<button type="submit" class="btn btn-sm btn-outline-danger rounded-0">';
echo fxEveAccesPromoteurEsc(fxEveAccesPromoteurT('eve_acces_promoteur_btn_revoke', $strLangue));
echo '</button></form>';
}
echo '</td>';
echo '</tr>';
}
}
echo '</tbody></table></div></div></div>';
echo '<a class="btn btn-primary rounded-pill mt-2" href="' . fxEveAccesPromoteurEsc($strBackUrl) . '">';
echo '<i class="fa fa-chevron-left mr-2" aria-hidden="true"></i>';
echo fxEveAccesPromoteurEsc(afficheTexte('promoteur_back', 0));
echo '</a>';
echo '</div>';
}
/** MSIN-4401 — JS vérif courriel via ajax_verif_courriel.php (existant). */
function fxEveAccesPromoteurRenderInviteScript()
{
static $blnDone = false;
if ($blnDone) {
return;
}
$blnDone = true;
?>
<script>
(function () {
var form = document.getElementById('eve_acces_invite_form');
if (!form) return;
var input = document.getElementById('invite_courriel');
var feedback = document.getElementById('invite_courriel_feedback');
var nameRow = document.getElementById('invite_name_row');
var prenom = document.getElementById('invite_prenom');
var nom = document.getElementById('invite_nom');
var submitBtn = document.getElementById('invite_submit_btn');
var ajaxUrl = form.getAttribute('data-ajax-url') || '';
var msgExists = form.getAttribute('data-msg-exists') || '';
var msgNew = form.getAttribute('data-msg-new') || '';
var timer = null;
var lastChecked = '';
function setFeedback(msg, kind) {
if (!feedback) return;
if (!msg) {
feedback.style.display = 'none';
feedback.textContent = '';
feedback.className = 'small mt-2 mb-0';
return;
}
feedback.style.display = 'block';
feedback.textContent = msg;
feedback.className = 'small mt-2 mb-0 text-' + (kind || 'muted');
}
function setNameMode(mode) {
if (!nameRow || !prenom || !nom) return;
if (mode === 'required') {
nameRow.style.display = '';
prenom.required = true;
nom.required = true;
if (submitBtn) submitBtn.disabled = false;
} else {
nameRow.style.display = 'none';
prenom.required = false;
nom.required = false;
if (submitBtn) submitBtn.disabled = false;
}
}
function isValidEmail(v) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function checkCourriel() {
var email = (input.value || '').trim().toLowerCase();
if (email === '' || !isValidEmail(email)) {
lastChecked = '';
setFeedback('', '');
setNameMode('hidden');
return;
}
if (email === lastChecked) return;
var requestEmail = email;
// Même API que la fiche compte : true = libre, false = déjà pris (actif).
var qs = 'com_courriel=' + encodeURIComponent(email);
fetch(ajaxUrl + '?' + qs, { credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(function (libre) {
if ((input.value || '').trim().toLowerCase() !== requestEmail) return;
lastChecked = requestEmail;
if (libre === false || libre === 'false' || libre === 0) {
setFeedback(msgExists, 'success');
setNameMode('hidden');
} else {
setFeedback(msgNew, 'info');
setNameMode('required');
}
})
.catch(function () {
if ((input.value || '').trim().toLowerCase() !== requestEmail) return;
lastChecked = '';
setFeedback('', '');
setNameMode('hidden');
});
}
input.addEventListener('blur', checkCourriel);
input.addEventListener('input', function () {
if (timer) clearTimeout(timer);
timer = setTimeout(checkCourriel, 450);
});
})();
</script>
<?php
}