diff --git a/TODO.md b/TODO.md index a5f95d5..5b5961c 100644 --- a/TODO.md +++ b/TODO.md @@ -52,3 +52,9 @@ - [x] Tags page: back button, admin-main--list, no padding, icon buttons, #admin-table-wrap - [x] Move #bulk-actions into fixed-height #bulk-meta-bar at top, prevent layout shift - [x] Credits: move Iconographie below Typographies +- [x] Rename Accès étudiant·e → Liens étudiant·e +- [x] Add 'name' column to share_links (schema + migration + model) +- [x] Add edit dialog for share links (edit name, password, expiration) +- [x] Row click opens link in new tab, remove Visiter button +- [x] Add update action to acces-etudiante.php controller +- [x] Add ShareLink::update() method diff --git a/app/public/admin/actions/acces-etudiante.php b/app/public/admin/actions/acces-etudiante.php index aa2eab6..5ce6ea9 100644 --- a/app/public/admin/actions/acces-etudiante.php +++ b/app/public/admin/actions/acces-etudiante.php @@ -23,6 +23,7 @@ $logger = AdminLogger::make(); switch ($action) { case 'create': + $name = !empty($_POST['name']) ? trim($_POST['name']) : null; $password = !empty($_POST['password']) ? trim($_POST['password']) : null; $expiresRaw = !empty($_POST['expires_at']) ? trim($_POST['expires_at']) : null; $expiresAt = null; @@ -36,7 +37,7 @@ switch ($action) { $validObjet = ['tfe', 'thèse', 'frart']; $selected = is_array($objetRaw) ? array_intersect($objetRaw, $validObjet) : []; $objetRestriction = !empty($selected) ? implode(',', $selected) : 'tfe'; - $link = $shareLink->create(1, $password, $expiresAt, $objetRestriction); + $link = $shareLink->create(1, $password, $expiresAt, $objetRestriction, $name); $logger->logLinkCreate( $link['slug'] ?? '', $password !== null, @@ -77,6 +78,18 @@ switch ($action) { } break; + case 'update': + if ($id > 0) { + $name = isset($_POST['name']) ? trim($_POST['name']) : null; + $password = isset($_POST['password']) ? trim($_POST['password']) : null; + $expiresRaw = isset($_POST['expires_at']) ? trim($_POST['expires_at']) : null; + $shareLink->update($id, $name, $password, $expiresRaw); + App::redirect('/admin/acces.php', success: 'Lien mis à jour.'); + } else { + App::redirect('/admin/acces.php', error: 'Lien introuvable.'); + } + break; + default: App::redirect('/admin/acces.php', error: 'Action inconnue.'); break; diff --git a/app/src/Database.php b/app/src/Database.php index c1d4d1a..c15f4cf 100644 --- a/app/src/Database.php +++ b/app/src/Database.php @@ -29,12 +29,26 @@ class Database $this->pdo->exec('PRAGMA journal_mode = WAL'); $this->pdo->exec('PRAGMA synchronous = NORMAL'); $this->pdo->exec('PRAGMA cache_size = -8000'); + $this->runMigrations(); } catch (PDOException $e) { error_log('Database connection failed: ' . $e->getMessage()); throw new Exception('Impossible de se connecter à la base de données.'); } } + /** + * Run one-off schema migrations. + */ + private function runMigrations(): void + { + // Add 'name' column to share_links if missing + try { + $this->pdo->exec("ALTER TABLE share_links ADD COLUMN name TEXT"); + } catch (\PDOException $e) { + // Column already exists — ignore + } + } + /** * Determine database path. * Priority: explicit override → APP_ROOT /storage/xamxam.db. diff --git a/app/src/ShareLink.php b/app/src/ShareLink.php index cd63382..c8288ec 100644 --- a/app/src/ShareLink.php +++ b/app/src/ShareLink.php @@ -49,7 +49,7 @@ class ShareLink * @param string|null $expiresAt ISO-8601 expiration date, null = never expires * @return array|null The created link row */ - public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null, ?string $objetRestriction = null): ?array + public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null, ?string $objetRestriction = null, ?string $name = null): ?array { $slug = self::generateSlug(); $passwordHash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null; @@ -62,10 +62,10 @@ class ShareLink } $stmt = $this->db->getConnection()->prepare( - 'INSERT INTO share_links (slug, objet_restriction, password_hash, is_active, created_by, expires_at) - VALUES (?, ?, ?, 1, ?, ?)' + 'INSERT INTO share_links (slug, name, objet_restriction, password_hash, is_active, created_by, expires_at) + VALUES (?, ?, ?, ?, 1, ?, ?)' ); - $stmt->execute([$slug, $objetRestriction, $passwordHash, $createdBy, $expiresAt]); + $stmt->execute([$slug, $name, $objetRestriction, $passwordHash, $createdBy, $expiresAt]); return $this->findBySlug($slug); } @@ -185,6 +185,35 @@ class ShareLink )->execute([$id]); } + /** + * Update a share link (name, password, expiration). + */ + public function update(int $id, ?string $name = null, ?string $password = null, ?string $expiresAt = null): void + { + $pdo = $this->db->getConnection(); + $fields = []; + $params = []; + + if ($name !== null) { + $fields[] = 'name = ?'; + $params[] = $name; + } + if ($password !== null) { + $fields[] = 'password_hash = ?'; + $params[] = $password !== '' ? password_hash($password, PASSWORD_BCRYPT) : null; + } + if ($expiresAt !== null) { + $expiresAtVal = $expiresAt !== '' ? date('Y-m-d H:i:s', strtotime($expiresAt)) : null; + $fields[] = 'expires_at = ?'; + $params[] = $expiresAtVal; + } + + if (!empty($fields)) { + $params[] = $id; + $pdo->prepare('UPDATE share_links SET ' . implode(', ', $fields) . ' WHERE id = ?')->execute($params); + } + } + // ── Validation ──────────────────────────────────────────────────────────── /** diff --git a/app/storage/schema.sql b/app/storage/schema.sql index 5ffd815..c8e3005 100644 --- a/app/storage/schema.sql +++ b/app/storage/schema.sql @@ -338,6 +338,7 @@ INSERT OR IGNORE INTO pages (slug, title, content) VALUES CREATE TABLE IF NOT EXISTS share_links ( id INTEGER PRIMARY KEY AUTOINCREMENT, slug TEXT NOT NULL UNIQUE, -- Format: YYYYMMDD-, e.g. 20260416-a3f9k2 + name TEXT, -- user-defined label (optional) objet_restriction TEXT CHECK (objet_restriction IN ('tfe', 'thèse', 'frart')), -- NULL = no restriction password_hash TEXT, -- bcrypt hash; NULL = no password required is_active INTEGER NOT NULL DEFAULT 1, -- 1 = active, 0 = disabled diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php index 7949ca7..63879f4 100644 --- a/app/templates/admin/acces.php +++ b/app/templates/admin/acces.php @@ -2,11 +2,11 @@

Accès

-

Accès étudiant·e

+

Liens étudiant·e

-

Aucun lien d'accès créé. Cliquez sur « Créer un lien » pour générer un lien partageable.

+

Aucun lien créé. Cliquez sur « Créer un lien » pour générer un lien partageable.

+ @@ -44,8 +45,13 @@ $linkName = $link['name'] ?? ''; $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; $linkLockedYear = $link['locked_year'] ?? null; +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +\\\\\\\ to: vumvtlyz 95fdf9fe "Rename Liens étudiant·e, add link name + edit dialog" (rebased revision) ++ $linkName = $link['name'] ?? ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ?> - + +
Nom Lien Objet Année
@@ -79,15 +85,15 @@
- - - + -
+ @@ -101,12 +107,8 @@
-