pas de collision avec index.php */ require_once __DIR__ . '/../../public/includes/init_admin.php'; if (($_GET['page'] ?? '') !== 'purge_conversations') { return; } // Sécurité (l'admin/index.php a normalement déjà vérifié ça) if (session_status() !== PHP_SESSION_ACTIVE) session_start(); // Lang (pour $translations) $roles = (array)($_SESSION['role'] ?? []); if (!in_array('admin', $roles, true)) { echo '
{TABLE} introuvable.");
$errors[] = str_replace('{TABLE}', $e($t), $tpl);
}
}
$hasCounters = $tableExists($pdo, 'conversation_counters');
$hasReadMk = $tableExists($pdo, 'read_markers');
if (empty($errors)) {
// 1) Candidats
$lastSql = "SELECT id, updated_at FROM conversations WHERE deleted_for_praticien=1 AND deleted_for_client=1";
if ($retentionDays > 0) { $lastSql .= " AND updated_at < (NOW() - INTERVAL {$retentionDays} DAY)"; }
$rows = $pdo->query($lastSql)->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $r) { $candidates[] = [(int)$r['id'], (string)$r['updated_at']]; }
$stats['conversations'] = count($candidates);
if ($stats['conversations'] > 0) {
$ids = array_column($candidates, 0);
// 2) Stats pré-purge
foreach ($chunked($ids, 500) as $batch) {
$ph = $placeholders(count($batch));
// messages
$st = $pdo->prepare("SELECT COUNT(*) FROM messages WHERE conversation_id IN ($ph)");
$st->execute($batch);
$stats['messages'] += (int)$st->fetchColumn();
// attachments (sample)
$st = $pdo->prepare("SELECT attachment_path FROM messages WHERE conversation_id IN ($ph)
AND attachment_path IS NOT NULL AND attachment_path<>''");
$st->execute($batch);
while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
$p = (string)$row['attachment_path'];
if ($p !== '') {
$stats['attachments']++;
if (count($attachList) < 50) { $attachList[] = $p; }
}
}
if ($hasCounters) {
$st = $pdo->prepare("SELECT COUNT(*) FROM conversation_counters WHERE conversation_id IN ($ph)");
$st->execute($batch);
$stats['counters'] += (int)$st->fetchColumn();
}
if ($hasReadMk) {
$st = $pdo->prepare("SELECT COUNT(*) FROM read_markers WHERE conversation_id IN ($ph)");
$st->execute($batch);
$stats['read_markers'] += (int)$st->fetchColumn();
}
}
// 3) Purge LIVE
if ($doLive) {
$pdo->beginTransaction();
try {
// supprimer fichiers de pièces jointes
foreach ($chunked($ids, 500) as $batch) {
$ph = $placeholders(count($batch));
$st = $pdo->prepare("SELECT attachment_path FROM messages WHERE conversation_id IN ($ph)
AND attachment_path IS NOT NULL AND attachment_path<>''");
$st->execute($batch);
while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
$rel = (string)$row['attachment_path']; // ex: messages/uploads/xxx
$abs = $siteRoot . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $rel);
if ($safeWithin($abs, $siteRoot) && is_file($abs)) {
if (@unlink($abs)) { $log("[OK] ".$e($translations['purge_log_file_deleted'] ?? 'Fichier supprimé').": {$rel}"); }
else { $log("[WARN] ".$e($translations['purge_log_file_delete_failed'] ?? 'Échec suppression fichier').": {$rel}"); }
} else {
$log("[INFO] ".$e($translations['purge_log_file_missing_or_outside'] ?? 'Fichier introuvable/hors périmètre').": {$rel}");
}
}
}
// dépendances
foreach ($chunked($ids, 500) as $batch) {
$ph = $placeholders(count($batch));
$st = $pdo->prepare("DELETE FROM messages WHERE conversation_id IN ($ph)");
$st->execute($batch);
$log("[OK] ".$e($translations['purge_log_messages_deleted_batch'] ?? 'messages supprimés (batch)').": ".$st->rowCount());
if ($hasCounters) {
$st = $pdo->prepare("DELETE FROM conversation_counters WHERE conversation_id IN ($ph)");
$st->execute($batch);
$log("[OK] ".$e($translations['purge_log_counters_deleted_batch'] ?? 'conversation_counters supprimés (batch)').": ".$st->rowCount());
}
if ($hasReadMk) {
$st = $pdo->prepare("DELETE FROM read_markers WHERE conversation_id IN ($ph)");
$st->execute($batch);
$log("[OK] ".$e($translations['purge_log_read_markers_deleted_batch'] ?? 'read_markers supprimés (batch)').": ".$st->rowCount());
}
}
// parent
foreach ($chunked($ids, 500) as $batch) {
$ph = $placeholders(count($batch));
$st = $pdo->prepare("DELETE FROM conversations WHERE id IN ($ph)");
$st->execute($batch);
$log("[OK] ".$e($translations['purge_log_conversations_deleted_batch'] ?? 'conversations supprimées (batch)').": ".$st->rowCount());
}
$pdo->commit();
$log($translations['purge_done_ok'] ?? 'Purge effectuée avec succès.');
} catch (\Throwable $ex) {
$pdo->rollBack();
$tpl = (string)($translations['purge_error'] ?? 'Erreur purge: {ERR}');
$errors[] = str_replace('{ERR}', $e($ex->getMessage()), $tpl);
}
} else {
$log($translations['purge_dry_run_hint'] ?? 'DRY-RUN: aucune suppression; utilisez le bouton Purger en LIVE pour exécuter.');
}
} else {
$log($translations['purge_none'] ?? 'Aucune conversation à purger.');
}
}
} catch (\Throwable $ex) {
$tpl = (string)($translations['purge_exception'] ?? 'Exception: {ERR}');
$errors[] = str_replace('{ERR}', $e($ex->getMessage()), $tpl);
}
$stats['duration'] = microtime(true) - $start;
?>
| = $e($translations['th_id'] ?? 'ID') ?> | = $e($translations['th_last_update'] ?? 'Dernière MAJ') ?> |
|---|---|
| = (int)$cid ?> |
= $e($p) ?>= $e(implode("\n", $logs)) ?>