Add Mots-clés and Langues management to contenus page

- Add searchLanguages, getAllLanguagesWithCount, renameLanguage, mergeLanguage, deleteLanguage to Database
- Create actions/language.php handler with rename/merge/merge_bulk/delete actions
- Add merge_bulk action to actions/tag.php
- Add Mots-clés section to contenus template with HTMX search, select checkboxes, rename/delete/merge buttons, and multi-select merge toolbar
- Add Langues section to contenus template with same pattern
- Create contenus-tags-fragment.php and contenus-languages-fragment.php HTMX fragments
- Remove form-settings- from flat-fieldset CSS selector so fieldsets in contenus retain border/padding
- contenus.php: add 'Gérer les mots-clés' link to /admin/tags.php
- contenus.php: add Langues fieldset with HTMX search + table (rename/merge/delete/bulk)
- tags.php: add HTMX search bar, checkbox column, bulk merge toolbar
- Create tags-fragment.php and contenus-langues-fragment.php for HTMX
- Remove tab component and associated CSS
- Simplify JS: separate tags/langues-prefixed functions
- Fix redirects: tag.php defaults to /admin/tags.php, supports return override
- Keep tags.php standalone page and Mots-clés button unchanged
This commit is contained in:
Pontoporeia
2026-05-10 12:13:26 +02:00
parent 494675d78c
commit 396cf19e9f
13 changed files with 814 additions and 195 deletions

View File

@@ -1252,6 +1252,107 @@ class Database
$this->pdo->prepare('DELETE FROM tags WHERE id = ?')->execute([$id]);
}
// ========================================================================
// LANGUAGE MANAGEMENT (admin)
// ========================================================================
/**
* Search languages by name prefix. Returns up to 10 matching languages.
* If $query is empty, returns the most-used languages (up to 10).
*/
public function searchLanguages(string $query = ''): array
{
$query = trim($query);
if ($query === '') {
$stmt = $this->pdo->query('
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
COUNT(DISTINCT tl.thesis_id) as thesis_count
FROM languages l
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
GROUP BY l.id
ORDER BY thesis_count DESC, l.name COLLATE NOCASE
LIMIT 10
');
} else {
$stmt = $this->pdo->prepare('
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
COUNT(DISTINCT tl.thesis_id) as thesis_count
FROM languages l
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
WHERE LOWER(l.name) LIKE LOWER(?)
GROUP BY l.id
ORDER BY LOWER(l.name) = LOWER(?) DESC, thesis_count DESC, l.name COLLATE NOCASE
LIMIT 10
');
$stmt->execute([$query . '%', $query]);
}
return $stmt->fetchAll();
}
/**
* Return all languages with a count of associated theses.
*/
public function getAllLanguagesWithCount(): array
{
$stmt = $this->pdo->query('
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
COUNT(DISTINCT tl.thesis_id) as thesis_count
FROM languages l
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
GROUP BY l.id
ORDER BY l.name COLLATE NOCASE
');
return $stmt->fetchAll();
}
/**
* Rename a language. Throws if the new name already exists.
*/
public function renameLanguage(int $id, string $newName): void
{
$newName = trim($newName);
if ($newName === '') {
throw new Exception('Le nom de la langue ne peut pas être vide.');
}
$stmt = $this->pdo->prepare('SELECT id FROM languages WHERE LOWER(name) = LOWER(?) AND id != ?');
$stmt->execute([$newName, $id]);
if ($stmt->fetch()) {
throw new Exception('Une langue avec ce nom existe déjà.');
}
$this->pdo->prepare('UPDATE languages SET name = ? WHERE id = ?')->execute([$newName, $id]);
}
/**
* Merge sourceId into targetId: reassign all thesis_languages rows, then delete source.
*/
public function mergeLanguage(int $sourceId, int $targetId): void
{
if ($sourceId === $targetId) {
throw new Exception('Source et destination identiques.');
}
$this->pdo->beginTransaction();
try {
$this->pdo->prepare('
INSERT OR IGNORE INTO thesis_languages (language_id, thesis_id)
SELECT ?, thesis_id FROM thesis_languages WHERE language_id = ?
')->execute([$targetId, $sourceId]);
$this->pdo->prepare('DELETE FROM thesis_languages WHERE language_id = ?')->execute([$sourceId]);
$this->pdo->prepare('DELETE FROM languages WHERE id = ?')->execute([$sourceId]);
$this->pdo->commit();
} catch (\Throwable $e) {
$this->pdo->rollBack();
throw $e;
}
}
/**
* Delete a language and all its thesis_languages rows.
*/
public function deleteLanguage(int $id): void
{
$this->pdo->prepare('DELETE FROM languages WHERE id = ?')->execute([$id]);
}
/**
* Get orientation ID by name
*/