Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -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'] ?? '© '.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>
|
||||
Reference in new issue
Block a user