mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Error tests, FK violations fix
- ErrorHandler tests: 77 assertions covering FK extraction, normalization, dedup, edge cases. Fix FK table map for child tables. - Fix FK violation: (int)null → 0 in createThesis for orientation/ap/finality/license FK columns. Add FK value logging to updateThesis. - Add CURRENT_ISSUES.md with summary of FK violation, dev debugging, and tag dedup status for next conversation
This commit is contained in:
@@ -18,6 +18,7 @@ if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
require_once APP_ROOT . '/src/SmtpRelay.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
$db = Database::getInstance();
|
||||
$logger = AdminLogger::make();
|
||||
@@ -59,7 +60,7 @@ try {
|
||||
$logger->logAccessRequest($requestId, 'approve', $request['email'], $request['title']);
|
||||
App::flash('success', "Demande approuvée. Email envoyé à {$request['email']}.");
|
||||
} catch (SmtpSendException $e) {
|
||||
error_log('[access-request] Email delivery failed after approval: ' . $e->getMessage());
|
||||
ErrorHandler::log('access_request_smtp', $e, ['request_id' => $requestId]);
|
||||
$logger->logAccessRequest($requestId, 'approve', $request['email'], $request['title']);
|
||||
$smtpMsg = $e->isRecipientRejected()
|
||||
? "Demande approuvée, mais l'email n'a pas pu être délivré : adresse inconnue ({$request['email']})."
|
||||
@@ -78,7 +79,7 @@ try {
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('Access request action failed: ' . $e->getMessage());
|
||||
ErrorHandler::log('access_request', $e);
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Erreur lors du traitement']);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ if (!in_array($aproposKey, $allowedKeys)) {
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
require_once __DIR__ . '/../../../src/AdminLogger.php';
|
||||
require_once __DIR__ . '/../../../src/ErrorHandler.php';
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
@@ -54,8 +55,8 @@ try {
|
||||
AdminLogger::make()->logAproposEdit($aproposKey);
|
||||
App::flash('success', "Contenu « $aproposKey » mis à jour avec succès.");
|
||||
} catch (Exception $e) {
|
||||
error_log("Apropos save error: " . $e->getMessage());
|
||||
die("Erreur lors de la sauvegarde : " . htmlspecialchars($e->getMessage()));
|
||||
ErrorHandler::log('apropos', $e);
|
||||
die('Erreur lors de la sauvegarde : ' . htmlspecialchars(ErrorHandler::userMessage($e)));
|
||||
}
|
||||
|
||||
header('Location: /admin/contenus.php');
|
||||
|
||||
@@ -6,6 +6,7 @@ AdminAuth::requireLogin();
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
require_once __DIR__ . '/../../../src/AdminLogger.php';
|
||||
require_once __DIR__ . '/../../../src/ErrorHandler.php';
|
||||
|
||||
// CSRF validation
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
@@ -57,8 +58,8 @@ try {
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('delete.php error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la suppression : ' . $e->getMessage());
|
||||
ErrorHandler::log('thesis_delete', $e);
|
||||
App::flash('error', 'Erreur lors de la suppression : ' . ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
@@ -26,6 +26,7 @@ if ($thesisId <= 0) {
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ThesisEditController.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
try {
|
||||
$ctrl = ThesisEditController::create();
|
||||
@@ -41,9 +42,8 @@ try {
|
||||
exit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Edit action error: " . $e->getMessage());
|
||||
|
||||
App::flash('error', $e->getMessage());
|
||||
ErrorHandler::log('thesis_edit', $e, ['thesis_id' => $thesisId]);
|
||||
App::flash('error', ErrorHandler::userMessage($e));
|
||||
|
||||
// WCAG 3.3.1 — map error message to field name for autofocus on re-render.
|
||||
$autofocusField = ThesisEditController::autofocusFieldForError($e->getMessage());
|
||||
|
||||
@@ -15,6 +15,7 @@ AdminAuth::requireLogin();
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ExportController.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
try {
|
||||
$controller = ExportController::create();
|
||||
@@ -40,9 +41,9 @@ try {
|
||||
// Clean up temp file
|
||||
@unlink($zipPath);
|
||||
} catch (Exception $e) {
|
||||
error_log('Files export error: ' . $e->getMessage());
|
||||
ErrorHandler::log('export_files', $e);
|
||||
http_response_code(500);
|
||||
exit('Erreur lors de la création de l\'archive : ' . htmlspecialchars($e->getMessage()));
|
||||
exit('Erreur lors de la création de l\'archive : ' . htmlspecialchars(ErrorHandler::userMessage($e)));
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
@@ -15,6 +15,7 @@ AdminAuth::requireLogin();
|
||||
|
||||
require_once APP_ROOT . '/src/Controllers/ExportController.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
$doCsv = !empty($_GET['csv']);
|
||||
$doFiles = !empty($_GET['files']);
|
||||
@@ -69,9 +70,9 @@ if ($doFiles) {
|
||||
readfile($zipPath);
|
||||
@unlink($zipPath);
|
||||
} catch (Exception $e) {
|
||||
error_log('Files export error: ' . $e->getMessage());
|
||||
ErrorHandler::log('export', $e);
|
||||
http_response_code(500);
|
||||
exit('Erreur lors de la création de l\'archive : ' . htmlspecialchars($e->getMessage()));
|
||||
exit('Erreur lors de la création de l\'archive : ' . htmlspecialchars(ErrorHandler::userMessage($e)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ $content = $_POST['content'] ?? '';
|
||||
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
$db = new Database();
|
||||
|
||||
if (!in_array($key, Database::FORM_HELP_KEYS, true)) {
|
||||
@@ -32,8 +33,8 @@ try {
|
||||
AdminLogger::make()->logFormStructureEdit($key);
|
||||
App::flash('success', 'Bloc « ' . htmlspecialchars($key) . ' » mis à jour.');
|
||||
} catch (Exception $e) {
|
||||
error_log('form-help save error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la sauvegarde : ' . htmlspecialchars($e->getMessage()));
|
||||
ErrorHandler::log('form_help', $e);
|
||||
App::flash('error', 'Erreur lors de la sauvegarde : ' . ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
@@ -26,6 +26,7 @@ require_once APP_ROOT . '/src/Controllers/ThesisCreateController.php';
|
||||
require_once APP_ROOT . '/src/AppLogger.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/DuplicateThesisException.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
|
||||
$logger = new AppLogger();
|
||||
$adminLogger = AdminLogger::make();
|
||||
@@ -47,8 +48,7 @@ try {
|
||||
|
||||
} catch (DuplicateThesisException $e) {
|
||||
$logger->logDuplicate('admin', $authorName, $e->existingThesisId, $e->existingIdentifier);
|
||||
|
||||
error_log('ThesisCreateController duplicate: ' . $e->getMessage());
|
||||
ErrorHandler::log('thesis_create_duplicate', $e, ['author' => $authorName]);
|
||||
|
||||
// Build a warning with a clickable link to the existing thesis.
|
||||
$existingUrl = htmlspecialchars('/admin/edit.php?id=' . $e->existingThesisId);
|
||||
@@ -68,10 +68,8 @@ try {
|
||||
'author' => $authorName,
|
||||
'post_keys' => array_keys($_POST),
|
||||
]);
|
||||
|
||||
error_log('ThesisCreateController error: ' . $e->getMessage());
|
||||
|
||||
App::flash('error', $e->getMessage());
|
||||
ErrorHandler::log('thesis_create', $e, ['author' => $authorName]);
|
||||
App::flash('error', ErrorHandler::userMessage($e));
|
||||
$_SESSION['form_data'] = $_POST;
|
||||
|
||||
$redirect = '../add.php';
|
||||
|
||||
@@ -25,6 +25,7 @@ if (!in_array($slug, $allowedSlugs, true)) {
|
||||
|
||||
require_once APP_ROOT . '/src/Database.php';
|
||||
require_once APP_ROOT . '/src/AdminLogger.php';
|
||||
require_once APP_ROOT . '/src/ErrorHandler.php';
|
||||
$db = new Database();
|
||||
|
||||
try {
|
||||
@@ -32,8 +33,8 @@ try {
|
||||
AdminLogger::make()->logPageEdit($slug);
|
||||
App::flash('success', 'Page « ' . htmlspecialchars($slug) . ' » mise à jour.');
|
||||
} catch (Exception $e) {
|
||||
error_log('page save error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la sauvegarde : ' . htmlspecialchars($e->getMessage()));
|
||||
ErrorHandler::log('page', $e);
|
||||
App::flash('error', 'Erreur lors de la sauvegarde : ' . ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
@@ -6,6 +6,7 @@ AdminAuth::requireLogin();
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
require_once __DIR__ . '/../../../src/AdminLogger.php';
|
||||
require_once __DIR__ . '/../../../src/ErrorHandler.php';
|
||||
|
||||
if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|| !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
@@ -61,8 +62,8 @@ try {
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('publish.php error: ' . $e->getMessage());
|
||||
App::flash('error', 'Erreur lors de la modification : ' . $e->getMessage());
|
||||
ErrorHandler::log('thesis_publish', $e);
|
||||
App::flash('error', 'Erreur lors de la modification : ' . ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
@@ -12,6 +12,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST'
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
require_once __DIR__ . '/../../../src/AdminLogger.php';
|
||||
require_once __DIR__ . '/../../../src/ErrorHandler.php';
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
@@ -48,7 +49,8 @@ try {
|
||||
|
||||
App::flash('success', "Opération effectuée.");
|
||||
} catch (Exception $e) {
|
||||
App::flash('error', $e->getMessage());
|
||||
ErrorHandler::log('tag', $e);
|
||||
App::flash('error', ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
header('Location: /admin/tags.php');
|
||||
|
||||
@@ -12,6 +12,7 @@ if (!isset($_POST['csrf_token'], $_SESSION['csrf_token'])
|
||||
|
||||
require_once __DIR__ . '/../../../src/Database.php';
|
||||
require_once __DIR__ . '/../../../src/AdminLogger.php';
|
||||
require_once __DIR__ . '/../../../src/ErrorHandler.php';
|
||||
|
||||
$action = $_POST['action'] ?? ''; // 'set_visibility'
|
||||
$accessTypeId = filter_var($_POST['access_type_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
|
||||
@@ -51,8 +52,8 @@ try {
|
||||
App::flash('success', "Visibilité mise à jour.");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("visibility.php error: " . $e->getMessage());
|
||||
App::flash('error', "Erreur : " . $e->getMessage());
|
||||
ErrorHandler::log('visibility', $e);
|
||||
App::flash('error', 'Erreur : ' . ErrorHandler::userMessage($e));
|
||||
}
|
||||
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
|
||||
@@ -39,7 +39,9 @@ function withAutofocus(string $fieldName, array $attrs = []): array {
|
||||
|
||||
function old($key, $default = "") {
|
||||
global $formData;
|
||||
return isset($formData[$key]) ? htmlspecialchars($formData[$key]) : $default;
|
||||
if (!isset($formData[$key])) return $default;
|
||||
if (is_array($formData[$key])) return $formData[$key]; // Return raw array for callers that handle it
|
||||
return htmlspecialchars($formData[$key]);
|
||||
}
|
||||
|
||||
function wasSelected($key, $value) {
|
||||
|
||||
@@ -23,7 +23,9 @@ $helpFn = fn(string $key) => empty($helpBlocks[$key]['enabled']) ? '' : ($helpBl
|
||||
|
||||
function old($key, $default = "") {
|
||||
global $formData;
|
||||
return isset($formData[$key]) ? htmlspecialchars($formData[$key]) : $default;
|
||||
if (!isset($formData[$key])) return $default;
|
||||
if (is_array($formData[$key])) return $formData[$key]; // Return raw array for callers that handle it
|
||||
return htmlspecialchars($formData[$key]);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -330,13 +330,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
}
|
||||
}
|
||||
if (!empty($keywordsRaw)) {
|
||||
foreach (array_slice(array_map('trim', explode(',', $keywordsRaw)), 0, 10) as $kw) {
|
||||
if ($kw) {
|
||||
$tId = $importDb->findOrCreateTag($kw);
|
||||
if ($tId) {
|
||||
$s = $importPdo->prepare("INSERT INTO thesis_tags (thesis_id, tag_id) VALUES (?,?)");
|
||||
$s->execute([$thesisId, $tId]);
|
||||
}
|
||||
$normalizeTag = fn(string $t): string => strtolower(trim(preg_replace('/\s+/', ' ', $t)));
|
||||
$tags = array_values(array_unique(array_map($normalizeTag, explode(',', $keywordsRaw))));
|
||||
$tags = array_filter($tags, fn($t) => $t !== '');
|
||||
foreach (array_slice($tags, 0, 10) as $kw) {
|
||||
$tId = $importDb->findOrCreateTag($kw);
|
||||
if ($tId) {
|
||||
$s = $importPdo->prepare("INSERT INTO thesis_tags (thesis_id, tag_id) VALUES (?,?)");
|
||||
$s->execute([$thesisId, $tId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
app/public/admin/tag-search-fragment.php
Normal file
14
app/public/admin/tag-search-fragment.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* tag-search-fragment.php (admin)
|
||||
*
|
||||
* HTMX fragment: returns matching tag suggestions for the mot-clé
|
||||
* interactive search input. Admin-auth gated wrapper.
|
||||
*/
|
||||
require_once __DIR__ . '/../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
App::boot();
|
||||
|
||||
require_once APP_ROOT . '/public/partage/tag-search-fragment.php';
|
||||
Reference in New Issue
Block a user