Initial commit (code), runtime ignoré, secrets exclus

This commit is contained in:
nicoboy
2026-01-02 15:05:25 +01:00
commit 2fdcf50bc1
87 changed files with 11525 additions and 0 deletions

View File

@ -0,0 +1,411 @@
<?php
/**
* Export Mots de Passe - Enseignant
* Génération et téléchargement des listes par classe
*/
define('APP_ROOT', dirname(__DIR__));
require_once APP_ROOT . '/config/config.php';
require_once APP_ROOT . '/config/database.php';
session_start();
// Vérifier authentification enseignant
if (!isLoggedIn() || !isEnseignant()) {
redirect('index.php');
}
// $auth = new Auth(); // MIGRÉ vers SessionManager
$user = currentUser();
$db = Database::getInstance();
$exporter = new PasswordExport();
// Actions
$action = $_GET['action'] ?? '';
$message = '';
$error = '';
if ($action === 'export_classe' && isset($_GET['id_classe'])) {
$result = $exporter->exportClassePasswords((int)$_GET['id_classe']);
if ($result['success']) {
$message = 'Export réussi : ' . $result['filename'];
} else {
$error = $result['message'];
}
}
if ($action === 'export_all') {
$results = $exporter->exportAllClasses();
$success_count = count(array_filter($results, fn($r) => $r['success']));
$message = "Export terminé : $success_count classe(s) exportée(s)";
}
if ($action === 'download' && isset($_GET['file'])) {
$exporter->downloadFile($_GET['file']);
}
if ($action === 'delete' && isset($_GET['file'])) {
if ($exporter->deleteFile($_GET['file'])) {
$message = 'Fichier supprimé';
} else {
$error = 'Erreur suppression';
}
}
// Récupérer classes
$classes = $db->fetchAll("
SELECT
c.id_classe,
c.nom_classe,
c.niveau,
c.type_classe,
COUNT(u.id_utilisateur) as nb_eleves
FROM classes c
LEFT JOIN utilisateurs u ON c.id_classe = u.id_classe AND u.actif = TRUE
WHERE c.actif = TRUE
GROUP BY c.id_classe
ORDER BY c.nom_classe
");
// Fichiers exportés
$files = $exporter->listExportedFiles();
// Déconnexion
if (isset($_GET['logout'])) {
$auth->logout();
redirect('index.php');
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Export Mots de Passe - Enseignant</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #667eea;
--secondary: #764ba2;
--success: #10b981;
--danger: #ef4444;
--warning: #f59e0b;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
--gray-800: #1f2937;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--gray-100);
color: var(--gray-800);
}
.header {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
padding: 20px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header .container {
max-width: 1400px;
margin: 0 auto;
padding: 0 30px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 1.8em;
}
.header .nav {
display: flex;
gap: 15px;
}
.header a {
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 8px;
background: rgba(255,255,255,0.2);
transition: all 0.3s;
}
.header a:hover {
background: rgba(255,255,255,0.3);
}
.main-content {
max-width: 1400px;
margin: 40px auto;
padding: 0 30px;
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.alert-success {
background: #d1fae5;
color: #065f46;
border-left: 4px solid var(--success);
}
.alert-error {
background: #fee2e2;
color: #991b1b;
border-left: 4px solid var(--danger);
}
.alert-warning {
background: #fef3c7;
color: #92400e;
border-left: 4px solid var(--warning);
}
.section {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
margin-bottom: 30px;
}
.section h2 {
margin-bottom: 20px;
color: var(--primary);
}
.btn {
padding: 10px 20px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s;
border: none;
cursor: pointer;
display: inline-block;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-success {
background: var(--success);
color: white;
}
.btn-danger {
background: var(--danger);
color: white;
}
.btn-warning {
background: var(--warning);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th {
background: var(--gray-100);
padding: 15px;
text-align: left;
font-weight: 600;
border-bottom: 2px solid var(--gray-200);
}
td {
padding: 15px;
border-bottom: 1px solid var(--gray-200);
}
tr:hover {
background: var(--gray-100);
}
.badge {
display: inline-block;
padding: 5px 12px;
border-radius: 20px;
font-size: 0.85em;
font-weight: 600;
}
.badge-regular { background: #dbeafe; color: #1e40af; }
.badge-soutien { background: #fef3c7; color: #92400e; }
.actions {
display: flex;
gap: 10px;
}
</style>
</head>
<body>
<header class="header">
<div class="container">
<h1>🔐 Export Mots de Passe</h1>
<div class="nav">
<a href="dashboard.php">← Retour Dashboard</a>
<a href="?logout=1">Déconnexion</a>
</div>
</div>
</header>
<main class="main-content">
<?php if ($message): ?>
<div class="alert alert-success"><?= h($message) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-error"><?= h($error) ?></div>
<?php endif; ?>
<div class="alert alert-warning">
⚠️ <strong>Sécurité :</strong> Les fichiers contiennent les mots de passe en clair.
Ils sont stockés hors du dossier web dans <code>/var/backups/mathematiques/classes_passwords/</code>
avec permissions 600 (lecture seule propriétaire).
</div>
<!-- Export par classe -->
<section class="section">
<h2>📚 Export par Classe</h2>
<p style="margin-bottom: 20px;">
Générez un fichier JSON contenant tous les logins et mots de passe pour chaque classe.
</p>
<div style="margin-bottom: 20px;">
<a href="?action=export_all" class="btn btn-success"
onclick="return confirm('Exporter toutes les classes ?')">
📤 Exporter Toutes les Classes
</a>
</div>
<table>
<thead>
<tr>
<th>Classe</th>
<th>Niveau</th>
<th>Type</th>
<th>Nb Élèves</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($classes as $classe): ?>
<tr>
<td><strong><?= h($classe['nom_classe']) ?></strong></td>
<td><?= h($classe['niveau']) ?></td>
<td>
<span class="badge badge-<?= $classe['type_classe'] ?>">
<?= $classe['type_classe'] === 'reguliere' ? 'Régulière' : 'Soutien' ?>
</span>
</td>
<td><?= $classe['nb_eleves'] ?></td>
<td class="actions">
<a href="?action=export_classe&id_classe=<?= $classe['id_classe'] ?>"
class="btn btn-primary">
Exporter
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<!-- Fichiers exportés -->
<section class="section">
<h2>📁 Fichiers Exportés</h2>
<?php if (empty($files)): ?>
<p style="color: #6b7280;">Aucun fichier exporté pour le moment.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>Fichier</th>
<th>Taille</th>
<th>Date Modification</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($files as $file): ?>
<tr>
<td><code><?= h($file['filename']) ?></code></td>
<td><?= number_format($file['size'] / 1024, 2) ?> KB</td>
<td><?= date('d/m/Y H:i', $file['date_modification']) ?></td>
<td class="actions">
<a href="?action=download&file=<?= urlencode($file['filename']) ?>"
class="btn btn-success">
⬇️ Télécharger
</a>
<a href="?action=delete&file=<?= urlencode($file['filename']) ?>"
class="btn btn-danger"
onclick="return confirm('Supprimer ce fichier ?')">
🗑️ Supprimer
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</section>
<!-- Informations -->
<section class="section">
<h2> Informations</h2>
<h3 style="margin-top: 20px;">Format du fichier JSON :</h3>
<pre style="background: var(--gray-100); padding: 15px; border-radius: 8px; overflow-x: auto;">
{
"classe": "2nde_A",
"niveau": "2nde",
"annee_scolaire": "2024-2025",
"type": "reguliere",
"date_export": "2025-10-23 10:30:00",
"nombre_eleves": 25,
"eleves": [
{
"login": "j.dupont",
"nom": "DUPONT",
"prenom": "Jean",
"date_naissance": "15/03/2009",
"mot_de_passe": "1503"
}
]
}
</pre>
<h3 style="margin-top: 20px;">Règles de génération :</h3>
<ul style="margin-left: 20px; line-height: 1.8;">
<li><strong>Login :</strong> p.nom (première lettre prénom + point + nom, minuscules)</li>
<li><strong>Doublons :</strong> ajout chiffre (j.dupont2, j.dupont3...)</li>
<li><strong>Mot de passe élève :</strong> JJMM (jour+mois naissance en 4 chiffres)</li>
<li><strong>Mot de passe enseignant :</strong> Personnalisé (n.boyer → math2025!)</li>
</ul>
</section>
</main>
</body>
</html>