Init Git Prod Mars 2026

This commit is contained in:
Oleg Barykin committed 2026-03-05 10:25:39 +01:00
commit d81efe93b1
362 files changed
+86627

No files matched your search

+297
View File
@@ -0,0 +1,297 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../includes/header_cabinet.php';
if (!function_exists('h')) { function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } }
$token = trim((string)($_GET['token'] ?? ''));
$inv = null;
if ($token) {
$q = $pdo->prepare("SELECT * FROM invitations WHERE token=:t AND status='envoyee' LIMIT 1");
$q->execute(['t'=>$token]);
$inv = $q->fetch(PDO::FETCH_ASSOC);
}
if (!$inv) {
echo '<div class="container my-4"><div class="alert alert-danger">'.h($translations['inv_token_invalide'] ?? 'Lien invalide.').'</div></div>';
exit;
}
if (!empty($inv['expires_at']) && new DateTime($inv['expires_at']) < new DateTime()) {
echo '<div class="container my-4"><div class="alert alert-warning">'.h($translations['inv_token_expire'] ?? 'Lien expiré.').'</div></div>';
exit;
}
$email = strtolower((string)$inv['email']);
$praticien_id = (int)$inv['praticien_id'];
$connectedRole = strtolower((string)($_SESSION['role'] ?? ''));
$connectedId = (int)($_SESSION['compte_id'] ?? 0);
$connectedEmail= strtolower((string)($_SESSION['email'] ?? ''));
$done = false;
$msg = '';
// =====================================================
// CAS 1 : Déjà connecté comme secrétaire
// =====================================================
if ($connectedRole === 'secretaire' && $connectedId > 0) {
if ($connectedEmail === $email) {
// rattachement au praticien si pas encore lié
$chk = $pdo->prepare("SELECT id FROM secretaire_praticien WHERE secretaire_user_id=:u AND praticien_id=:p");
$chk->execute(['u'=>$connectedId,'p'=>$praticien_id]);
if (!$chk->fetch()) {
$ins = $pdo->prepare("INSERT INTO secretaire_praticien (secretaire_user_id, praticien_id, can_manage_rdv, can_edit_slots, is_active)
VALUES (:u,:p,1,1,1)");
$ins->execute(['u'=>$connectedId,'p'=>$praticien_id]);
}
$pdo->prepare("UPDATE invitations SET status='acceptee' WHERE id=:id")->execute(['id'=>$inv['id']]);
$done = true;
} else {
// autre email → doit se connecter avec le bon compte
$msg = sprintf(
$translations['inv_email_diff'] ?? "Vous êtes connecté avec %s, mais l’invitation est pour %s. Veuillez vous connecter avec le bon compte.",
h($connectedEmail), h($email)
);
}
}
// =====================================================
// CAS 2 : POST (création compte si inexistant)
// =====================================================
elseif (($_SERVER['REQUEST_METHOD']??'')==='POST') {
$pwd = (string)($_POST['pwd'] ?? '');
$pwd2 = (string)($_POST['pwd2'] ?? '');
if (strlen($pwd) < 8 || $pwd !== $pwd2) {
$msg = $translations['inv_pwd_invalid'] ?? 'Mot de passe invalide ou non identique.';
} else {
// Vérifier si un compte existe déjà avec ce mail
$chk = $pdo->prepare("SELECT id, role FROM compte WHERE email=:e LIMIT 1");
$chk->execute(['e'=>$email]);
$acc = $chk->fetch(PDO::FETCH_ASSOC);
if ($acc) {
if ($acc['role'] !== 'secretaire') {
$msg = $translations['inv_email_occupe'] ?? "Un compte avec cet email existe déjà avec un autre rôle.";
} else {
// compte secrétaire déjà existant → rediriger vers login AVEC token
header("Location: /connexion.php?next=" . urlencode("/public/cabinet/accepter_invitation.php?token=".$token));
exit;
}
} else {
// création nouveau compte secrétaire
$ins = $pdo->prepare("INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide)
VALUES (:e, :pwd, 'secretaire', 0, NULL, 1)");
$ins->execute([
'e' => $email,
'pwd' => password_hash($pwd, PASSWORD_BCRYPT)
]);
$newId = (int)$pdo->lastInsertId();
$_SESSION['compte_id'] = $newId;
$_SESSION['role'] = 'secretaire';
$_SESSION['reference_id'] = 0;
$_SESSION['email'] = $email;
$pdo->prepare("INSERT INTO secretaire_praticien (secretaire_user_id, praticien_id, can_manage_rdv, can_edit_slots, is_active)
VALUES (:u,:p,1,1,1)")
->execute(['u'=>$newId,'p'=>$praticien_id]);
$pdo->prepare("UPDATE invitations SET status='acceptee' WHERE id=:id")->execute(['id'=>$inv['id']]);
$done = true;
}
}
}
// =====================================================
// CAS 3 : GET initial (ni connecté ni posté)
// Vérifier si compte existe déjà
// =====================================================
else {
$chk = $pdo->prepare("SELECT id, role FROM compte WHERE email=:e LIMIT 1");
$chk->execute(['e'=>$email]);
$acc = $chk->fetch(PDO::FETCH_ASSOC);
if ($acc && $acc['role']==='secretaire') {
echo '<div class="container my-4"><div class="alert alert-info">'.
h($translations['inv_compte_existe'] ?? 'Un compte secrétaire existe déjà, veuillez vous connecter.').
'</div><a href="/connexion.php?next='.urlencode("/public/cabinet/accepter_invitation.php?token=".$token).'" class="btn btn-primary">Connexion</a></div>';
exit;
}
}
?>
<div class="main1">
<div class="container py-5">
<div class="shadow p-4 rounded bg-light mx-auto" style="max-width:600px;">
<h1 class="h4 mb-3"><?= h($translations['inv_accepter_titre'] ?? 'Accepter l’invitation') ?></h1>
<?php if($done): ?>
<div class="alert alert-success mb-4"><?= h($translations['inv_ok'] ?? 'Invitation acceptée. Vous avez désormais accès au secrétariat.') ?></div>
<a class="btn btn-primary" href="/public/cabinet/dashboard.php"><?= h($translations['btn_aller_dashboard_sec'] ?? 'Aller au tableau de bord secrétaire') ?></a>
<?php else: ?>
<div class="card mb-3"><div class="card-body">
<p class="mb-1"><strong><?= h($translations['inv_email'] ?? 'Email') ?>:</strong> <?= h($email) ?></p>
<p class="mb-0"><strong><?= h($translations['inv_praticien'] ?? 'Praticien') ?>:</strong> #<?= (int)$praticien_id ?></p>
</div></div>
<?php if(!empty($msg)): ?>
<div class="alert alert-warning"><?= $msg ?></div>
<?php endif; ?>
<?php if($connectedRole!=='secretaire' || $connectedEmail!==$email): ?>
<form method="post" class="card p-3" id="create-secretary-form">
<label class="form-label mb-2">
<?= h($translations['inv_choisir_pwd'] ?? 'Choisissez un mot de passe (min. 8 caractères)') ?>
</label>
<!-- Mot de passe -->
<!-- MDP -->
<div class="mb-2 position-relative">
<input type="password" name="pwd" id="pwd" class="form-control pe-5"
minlength="8" required autocomplete="new-password"
placeholder="<?= h($translations['motdepasse_placeholder'] ?? 'Mot de passe') ?>">
<span class="toggle-password" onclick="togglePassword('pwd')"
style="position:absolute;right:12px;top:50%;transform:translateY(-50%);cursor:pointer;user-select:none;">👁️</span>
</div>
<div id="niveau-securite" class="mb-2 text-muted">
<?= h($translations['mdp_regle_min'] ?? 'min 8: (A–z)+(1–9)+(&–!)') ?>
</div>
<div class="strength-bar d-flex gap-1 mb-3" aria-hidden="true" style="height:8px;">
<div class="strength-fill flex-fill" style="background:#d9d9d9;border-radius:6px;"></div>
<div class="strength-fill flex-fill" style="background:#d9d9d9;border-radius:6px;"></div>
<div class="strength-fill flex-fill" style="background:#d9d9d9;border-radius:6px;"></div>
</div>
<!-- Confirmation -->
<div class="mb-2 position-relative">
<input type="password" name="pwd2" id="pwd2" class="form-control pe-5"
minlength="8" required autocomplete="new-password"
placeholder="<?= h($translations['confirm_mdp_placeholder'] ?? 'Confirmer le mot de passe') ?>">
<span class="toggle-password" onclick="togglePassword('pwd2')"
style="position:absolute;right:12px;top:50%;transform:translateY(-50%);cursor:pointer;user-select:none;">👁️</span>
</div>
<small id="confirm-feedback" class="d-block mb-3 text-muted" style="display:none;">—</small>
<button id="btn-submit" class="btn btn-primary" disabled>
<?= h($translations['inv_btn_creer_compte'] ?? 'Créer mon compte & accepter') ?>
</button>
</form>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
<footer class="site-footer">
<div class="container">
<p><?= $translations['copyr'] ?? '&copy; '.date('Y') ?></p>
</div>
</footer>
<script>
function togglePassword(id){
const i = document.getElementById(id);
if (i) i.type = (i.type === 'password') ? 'text' : 'password';
}
(function(){
const form = document.getElementById('create-secretary-form') || document.getElementById('form-inscription') || document.forms[0];
const pwd = document.getElementById('pwd');
const cpwd = document.getElementById('pwd2');
const fills = Array.from(document.querySelectorAll('.strength-fill'));
const hint = document.getElementById('niveau-securite');
const fb = document.getElementById('confirm-feedback');
const submit = document.getElementById('btn-submit');
const T = {
rule : '<?= addslashes($translations['mdp_regle_min'] ?? 'min 8: (A–z)+(1–9)+(&–!)') ?>',
weak : '<?= addslashes($translations['securite_faible'] ?? 'Sécurité : faible') ?>',
medium: '<?= addslashes($translations['securite_moyen'] ?? 'Sécurité : moyen') ?>',
strong: '<?= addslashes($translations['securite_fort'] ?? 'Sécurité : fort') ?>',
ok : '<?= addslashes($translations['mdp_identiques_ok'] ?? 'Les mots de passe correspondent.') ?>',
ko : '<?= addslashes($translations['mdp_non_identiques'] ?? 'Les mots de passe ne correspondent pas.') ?>'
};
function score(val){
if(!val) return 0;
let s = 0;
if(/[a-z]/.test(val)) s++;
if(/[A-Z]/.test(val)) s++;
if(/\d/.test(val)) s++;
if(/[\W_]/.test(val)) s++;
if(val.length>=8) s++;
return Math.min(s,5); // 0..5
}
function paintStrength(s){
// reset
fills.forEach(el => el.style.background = '#d9d9d9');
// niveaux: 0-1 faible(1 segment), 2-3 moyen(2), 4-5 fort(3)
let n = 0, color = '#dc3545'; // rouge
if (s <= 1) { n = 1; color = '#dc3545'; }
else if (s <= 3) { n = 2; color = '#ffc107'; } // jaune
else { n = 3; color = '#28a745'; } // vert
for (let i=0;i<n;i++) fills[i].style.background = color;
if (hint){
hint.classList.remove('text-danger','text-warning','text-success');
if (n===1){ hint.textContent = T.weak; hint.classList.add('text-danger'); }
else if (n===2){ hint.textContent = T.medium; hint.classList.add('text-warning'); }
else { hint.textContent = T.strong; hint.classList.add('text-success'); }
}
}
function checkMatch(){
if (!fb) return false;
if (!cpwd.value){ fb.style.display='none'; return false; }
const same = (pwd.value === cpwd.value) && cpwd.value.length>=8;
fb.textContent = same ? T.ok : T.ko;
fb.style.color = same ? '#198754' : '#dc3545';
fb.style.display = 'block';
return same;
}
function update(){
const v = pwd.value;
// ——— ÉTAT NEUTRE TANT QUE RIEN N'EST SAISI ———
if (!v) {
// barre toute grise
fills.forEach(el => el.style.background = '#d9d9d9');
// texte d'aide (règle) en gris
if (hint){
hint.textContent = T.rule;
hint.classList.remove('text-danger','text-warning','text-success');
hint.classList.add('text-muted');
}
// reset confirmation
if (fb){ fb.style.display = 'none'; }
// bouton désactivé
if (submit){ submit.disabled = true; }
return; // <<< on sort ici, pas de rouge par défaut
}
// ——— LOGIQUE NORMALE SI QUELQUE CHOSE EST SAISI ———
const sc = score(v);
paintStrength(sc);
const same = checkMatch();
if (submit){
// OK si force >= moyen (>=2 segments), longueur >=8 et confirmation OK
const ok = (sc >= 2) && (v.length >= 8) && same;
submit.disabled = !ok;
}
}
if (hint) hint.classList.remove('text-muted');
if (pwd) pwd.addEventListener('input', update);
if (cpwd) cpwd.addEventListener('input', update);
if (form) form.addEventListener('submit', function(e){
if (submit && submit.disabled){ e.preventDefault(); }
});
update(); // init
})();
</script>