Files
Vivier/vues/utilisateurs.php

243 lines
15 KiB
PHP

<?php
/** Champ « votre mot de passe » des actions sensibles. */
$confirmer = static function (string $aide = ''): void { ?>
<label><?= e(t('Votre mot de passe, pour confirmer')) ?>
<input type="password" name="mon_mot_de_passe" required autocomplete="current-password">
<?php if ($aide !== ''): ?><small class="aide-champ"><?= e($aide) ?></small><?php endif; ?>
</label>
<?php };
?>
<div class="barre-titre">
<h1><?= e(t('Comptes')) ?> <small><?= count($liste) ?></small></h1>
<a class="bouton secondaire" href="<?= e(url('connexions')) ?>"><?= e(t('Journal des connexions')) ?></a>
</div>
<p class="aide"><?= e(t('Chaque compte lit et modifie tous les profils. Une personne rejoint Vivier par un lien d\'invitation, ou vous créez son compte ici. À la première connexion, chacun active la double authentification.')) ?></p>
<section class="carte mise-en-avant" id="invitations">
<h2><?= e(t('Inviter une personne')) ?></h2>
<?php if ($lien): ?>
<div class="lien-invitation" role="status">
<p><strong><?= e($lien['nom'] !== '' ? t('Lien d\'invitation pour %s', $lien['nom']) : t('Lien d\'invitation')) ?></strong></p>
<p class="aide"><?= e(t('Copiez-le et envoyez-le à la personne, par email ou messagerie interne. Il ne sera plus affiché. Il sert une fois et expire dans %d heures.', INVITATION_HEURES)) ?></p>
<div class="copie">
<input type="text" readonly value="<?= e($lien['lien']) ?>" aria-label="<?= e(t('Lien d\'invitation')) ?>">
<button type="button" data-copier="<?= e($lien['lien']) ?>" data-copie="<?= e(t('Copié')) ?>"><?= e(t('Copier')) ?></button>
</div>
</div>
<?php endif; ?>
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="ajout-ligne">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="inviter">
<label><?= e(t('Nom de la personne, facultatif')) ?> <input type="text" name="nom" maxlength="120" autocomplete="off"></label>
<button type="submit"><?= e(t('Créer un lien d\'invitation')) ?></button>
</form>
<p class="aide"><?= e(t('La personne choisit son identifiant et son mot de passe, puis active la double authentification. Son compte est actif tout de suite.')) ?></p>
<?php if ($invitations): ?>
<h3 class="intertitre"><?= e(t('Liens en cours')) ?></h3>
<ul class="simple">
<?php foreach ($invitations as $i): ?>
<li class="ligne-action">
<span><?= e($i['nom_affiche'] ?: t('Sans nom')) ?> <small><?= e(t('créé le %s par %s, expire le %s', date_fr($i['cree_le'], true), $i['par'], date_fr($i['expire_le'], true))) ?></small></span>
<form method="post" action="<?= e(url('utilisateurs')) ?>" data-confirmer="<?= e(t('Annuler ce lien ? Il ne fonctionnera plus.')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="annuler_invitation">
<input type="hidden" name="invitation" value="<?= (int) $i['id'] ?>">
<button type="submit" class="lien"><?= e(t('Annuler le lien')) ?></button>
</form>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</section>
<?php if ($demandes): ?>
<section class="carte demandes" id="demandes">
<h2><?= e(t('Demandes à valider')) ?> <small><?= count($demandes) ?></small></h2>
<p class="aide"><?= e(t('Demandes faites avant la version 1.8, par l\'ancienne page publique. Elles ne donnent aucun accès avant votre accord. Vivier ne prévient pas la personne : faites-le vous-même.')) ?></p>
<ul class="liste-demandes">
<?php foreach ($demandes as $d): ?>
<li>
<div>
<p class="compte-nom"><?= e($d['nom_affiche']) ?> <span class="badge badge-attente"><?= e(t('En attente')) ?></span></p>
<p class="compte-meta">
<span><?= e(t('Identifiant')) ?> : <strong><?= e($d['identifiant']) ?></strong></span>
<span><?= e(t('Demande du %s', date_fr($d['cree_le'], true))) ?></span>
</p>
</div>
<div class="actions-ligne">
<form method="post" action="<?= e(url('utilisateurs')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="id" value="<?= (int) $d['id'] ?>">
<button type="submit" name="action" value="accepter"><?= e(t('Accepter')) ?></button>
</form>
<form method="post" action="<?= e(url('utilisateurs')) ?>" data-confirmer="<?= e(t('Refuser cette demande ? Elle est effacée.')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="id" value="<?= (int) $d['id'] ?>">
<button type="submit" name="action" value="refuser" class="dangereux"><?= e(t('Refuser')) ?></button>
</form>
</div>
</li>
<?php endforeach; ?>
</ul>
</section>
<?php endif; ?>
<?php if ($blocages): ?>
<section class="carte attention" id="blocages">
<h2><?= e(t('Connexions bloquées')) ?> <small><?= count($blocages) ?></small></h2>
<p class="aide"><?= e(t('Après 5 échecs, un identifiant est bloqué 15 minutes depuis l\'adresse en cause, puis 30 et 60 minutes. Une adresse qui accumule 20 échecs en une heure est bloquée pour tous les identifiants. Débloquez seulement si vous savez qui essaie.')) ?></p>
<div class="defile">
<table>
<thead><tr><th><?= e(t('Bloqué')) ?></th><th><?= e(t('Adresse IP')) ?></th><th><?= e(t('Échecs sur 24 h')) ?></th><th><?= e(t('Jusqu\'à')) ?></th><th></th></tr></thead>
<tbody>
<?php foreach ($blocages as $b): ?>
<tr>
<td><?= $b['type'] === 'compte' ? e(t('Identifiant %s', $b['identifiant'])) : e(t('Toute l\'adresse')) ?></td>
<td><?= e($b['ip']) ?></td>
<td><?= (int) $b['echecs'] ?></td>
<td><?= e(date('H:i', $b['fin'])) ?></td>
<td class="actions-ligne">
<form method="post" action="<?= e(url('utilisateurs')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="debloquer">
<input type="hidden" name="cle" value="<?= e($b['cle']) ?>">
<button type="submit" class="lien"><?= e(t('Débloquer')) ?></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
<?php endif; ?>
<div class="deux-colonnes inverse">
<section class="carte">
<h2><?= e(t('Créer un compte')) ?></h2>
<p class="aide"><?= e(t('Préférez le lien d\'invitation : personne d\'autre que la personne ne connaît alors son mot de passe. Un compte créé ici change son mot de passe à la première connexion.')) ?></p>
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="creer">
<label><?= e(t('Nom de la personne')) ?> <input type="text" name="nom" required autocomplete="off"></label>
<label><?= e(t('Identifiant')) ?> <input type="text" name="identifiant" required autocomplete="off" autocapitalize="none" spellcheck="false" placeholder="<?= e(t('prenom.nom')) ?>"></label>
<label><?= e(t('Mot de passe provisoire, %d caractères au moins', LONGUEUR_MOT_DE_PASSE)) ?>
<input type="text" name="mot_de_passe" required minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="off" spellcheck="false">
</label>
<button type="submit" class="secondaire"><?= e(t('Créer le compte')) ?></button>
</form>
</section>
<ul class="comptes">
<?php foreach ($liste as $l): $moiMeme = (int) $l['id'] === (int) $moi['id']; ?>
<li class="carte<?= $l['actif'] ? '' : ' inactif' ?>">
<div class="compte-tete">
<div>
<p class="compte-nom"><?= e($l['nom_affiche']) ?><?= $moiMeme ? ' <span class="badge">' . e(t('vous')) . '</span>' : '' ?></p>
<p class="compte-meta">
<span><?= e(t('Identifiant')) ?> : <strong><?= e($l['identifiant']) ?></strong></span>
<?php if ($l['role'] === 'admin'): ?><span><?= e(t('Gère les comptes')) ?></span><?php endif; ?>
<?php if (!$l['actif']): ?><span class="retard"><?= e(t('Désactivé')) ?></span><?php endif; ?>
<span><?= $l['totp_active_le'] ? e(t('Double authentification active')) : '<span class="badge badge-attente">' . e(t('Double authentification à activer')) . '</span>' ?></span>
<?php if ($l['changer_mot_de_passe']): ?><span class="badge badge-attente"><?= e(t('Mot de passe à changer')) ?></span><?php endif; ?>
<span><?= e(t('Dernière connexion')) ?> : <?= e(date_fr($l['derniere_connexion'], true) ?: t('jamais')) ?></span>
</p>
</div>
</div>
<details>
<summary><?= e(t('Modifier le nom, l\'identifiant ou le mot de passe')) ?></summary>
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="modifier">
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
<div class="champs">
<label><?= e(t('Nom')) ?> <input type="text" name="nom" required value="<?= e($l['nom_affiche']) ?>" autocomplete="off"></label>
<label><?= e(t('Identifiant')) ?> <input type="text" name="identifiant" required value="<?= e($l['identifiant']) ?>" autocomplete="off" autocapitalize="none" spellcheck="false"></label>
<label class="large"><?= e(t('Nouveau mot de passe provisoire, à laisser vide pour le garder')) ?>
<input type="text" name="mot_de_passe" minlength="<?= LONGUEUR_MOT_DE_PASSE ?>" maxlength="<?= LONGUEUR_MAX_MOT_DE_PASSE ?>" autocomplete="off" spellcheck="false">
<small class="aide-champ"><?= e($moiMeme ? t('Pour votre propre mot de passe, passez plutôt par Mon compte.') : t('La personne le changera à sa prochaine connexion. Ses sessions ouvertes se ferment.')) ?></small>
</label>
<label class="large"><?= e(t('Votre mot de passe, seulement pour un nouveau mot de passe')) ?>
<input type="password" name="mon_mot_de_passe" autocomplete="current-password">
</label>
</div>
<div class="actions gauche"><button type="submit" class="secondaire"><?= e(t('Enregistrer')) ?></button></div>
</form>
</details>
<?php if (!$moiMeme): ?>
<?php if ($l['totp_active_le']): ?>
<details>
<summary><?= e(t('Remettre à zéro la double authentification')) ?></summary>
<p class="aide"><?= e(t('Pour une personne qui a perdu son téléphone et ses codes de secours. Elle activera une nouvelle application à sa prochaine connexion. Vérifiez d\'abord son identité, par téléphone ou en personne.')) ?></p>
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="double_auth">
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
<?php $confirmer(); ?>
<div class="actions gauche"><button type="submit" class="dangereux"><?= e(t('Remettre à zéro')) ?></button></div>
</form>
</details>
<?php endif; ?>
<details>
<summary><?= e($l['role'] === 'admin' ? t('Retirer la gestion des comptes') : t('Confier la gestion des comptes')) ?></summary>
<p class="aide"><?= e(t('Un gestionnaire crée et modifie les comptes, supprime des profils, voit le journal, les connexions et les sauvegardes.')) ?></p>
<form method="post" action="<?= e(url('utilisateurs')) ?>" class="pile">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="gestionnaire">
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
<?php $confirmer(); ?>
<div class="actions gauche"><button type="submit" class="secondaire"><?= e($l['role'] === 'admin' ? t('Retirer') : t('Confier')) ?></button></div>
</form>
</details>
<div class="actions-ligne">
<form method="post" action="<?= e(url('utilisateurs')) ?>"<?= $l['actif'] ? ' data-confirmer="' . e(t('Désactiver ce compte ? Ses sessions se ferment tout de suite.')) . '"' : '' ?>>
<?= champ_csrf() ?>
<input type="hidden" name="action" value="basculer">
<input type="hidden" name="id" value="<?= (int) $l['id'] ?>">
<button type="submit" class="lien"><?= e($l['actif'] ? t('Désactiver le compte') : t('Réactiver le compte')) ?></button>
</form>
<a href="<?= e(url('connexions', ['utilisateur' => $l['id']])) ?>"><?= e(t('Ses connexions')) ?></a>
</div>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<section class="carte" id="maintenance">
<h2><?= e(t('Maintenance')) ?></h2>
<div class="deux-egales">
<div>
<h3 class="intertitre"><?= e(t('Traduction des profils')) ?></h3>
<p class="aide"><?= e(Claude::configure()
? t('Chaque profil est traduit dans les langues de l\'interface, en arrière-plan, tant qu\'une page de Vivier reste ouverte. À lancer une fois après la mise à jour 1.8.')
: t('L\'analyse automatique n\'est pas configurée : les profils se traduisent un par un, par fichier, depuis l\'aperçu du CV IT Club.')) ?></p>
<p><?= e(tn($aTraduire, '%d traduction à faire.', '%d traductions à faire.')) ?></p>
<?php if ($echecsTraduction): ?>
<p class="retard"><?= e(tn(count($echecsTraduction), '%d traduction abandonnée après 3 essais :', '%d traductions abandonnées après 3 essais :')) ?></p>
<ul class="simple">
<?php foreach (array_slice($echecsTraduction, 0, 10) as $f): ?>
<li><a href="<?= e(url('profil', ['id' => $f['candidat_id']])) ?>"><?= e($f['prenom'] . ' ' . $f['nom']) ?></a> <small><?= e(strtoupper($f['langue'])) ?> · <?= e((string) $f['erreur']) ?></small></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if (Claude::configure()): ?>
<form method="post" action="<?= e(url('utilisateurs')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="traduire_tout">
<button type="submit" class="secondaire"><?= e(t('Traduire tous les profils')) ?></button>
</form>
<?php endif; ?>
</div>
<div>
<h3 class="intertitre"><?= e(t('Index de recherche')) ?></h3>
<p class="aide"><?= e(t('À relancer après une mise à jour de Vivier.')) ?></p>
<form method="post" action="<?= e(url('utilisateurs')) ?>">
<?= champ_csrf() ?>
<input type="hidden" name="action" value="reindexer">
<button type="submit" class="secondaire"><?= e(t('Réindexer tous les profils')) ?></button>
</form>
</div>
</div>
</section>