'Non identifié — veuillez vous connecter.']); exit; } try { $pdo = getDbConnection(); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Connexion base de données impossible']); exit; } $method = $_SERVER['REQUEST_METHOD']; if ($method === 'GET') { try { $stmt = $pdo->query( 'SELECT AGN_Incr, AGN_Prenom, AGN_Fonction, AGN_Photo, AGN_Couleur, AGN_Identite, AGN_Experience, AGN_Relationnel FROM ASP_Agents ORDER BY AGN_OrdreAffichage ASC' ); echo json_encode(['collaborateurs' => $stmt->fetchAll()]); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); } exit; } if ($method === 'PUT') { $data = json_decode(file_get_contents('php://input'), true) ?? []; $incr = (int) ($data['AGN_Incr'] ?? 0); // Un seul des champs à la fois dans le corps de la requête — jamais // plusieurs ensemble côté front (voir champActif dans bo_collaborateurs.html : // un onglet par champ — Identité/Expérience/Relationnel — jamais deux // édités dans le même appel). AGN_Relationnel (11 septembre 2026) : posture // relationnelle du collaborateur envers l'équipe, statique pour l'instant // — distincte d'AGN_Identite (caractère individuel) et d'AGN_Experience // (qui s'enrichit par append, voir collaborateur_experience_append.php). // Whitelist stricte plutôt qu'un nom de colonne construit depuis une clé // arbitraire du JSON reçu, même si en pratique cette donnée ne vient // jamais d'ailleurs que du front interne — même esprit défensif que le // reste du projet (voir contacts.php, contact_ia_interprete.php). $champsAutorises = ['AGN_Identite', 'AGN_Experience', 'AGN_Relationnel']; $champ = null; foreach ($champsAutorises as $c) { if (array_key_exists($c, $data)) { $champ = $c; break; } } if ($incr <= 0) { http_response_code(400); echo json_encode(['error' => 'AGN_Incr manquant ou invalide']); exit; } if ($champ === null) { http_response_code(400); echo json_encode(['error' => 'Aucun champ à mettre à jour (AGN_Identite ou AGN_Experience attendu)']); exit; } // Champ volontairement libre, sans limite de longueur imposée ici // (MEDIUMTEXT côté base, voir en-tête) : une identité — ou une expérience // accumulée — bien écrite peut dépasser largement la taille d'un champ de // fiche habituel (Nom, Ville...) de ce projet, et grandira encore avec // l'usage réel de l'application — le seul garde-fou vit côté front-end // (voir TEXTE_IDENTITE_MAX_OCTETS/compteur-identite dans ecran_fond.html). $valeur = trim((string) ($data[$champ] ?? '')); try { // Existence vérifiée par un SELECT séparé plutôt que sur rowCount() de // l'UPDATE (même précaution que contacts.php, section PUT — un texte // identique au texte déjà enregistré donnerait rowCount() = 0 alors // que le collaborateur existe bien). $verif = $pdo->prepare('SELECT AGN_Incr FROM ASP_Agents WHERE AGN_Incr = ?'); $verif->execute([$incr]); if (!$verif->fetch()) { http_response_code(404); echo json_encode(['error' => 'Collaborateur introuvable']); exit; } // $champ vient exclusivement de $champsAutorises ci-dessus, jamais // directement de $data : interpolation sans risque d'injection. $stmt = $pdo->prepare("UPDATE ASP_Agents SET {$champ} = ? WHERE AGN_Incr = ?"); $stmt->execute([$valeur !== '' ? $valeur : null, $incr]); echo json_encode(['success' => true]); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Erreur base de données : ' . $e->getMessage()]); } exit; } http_response_code(405); echo json_encode(['error' => 'Méthode non autorisée']);