34464-vm/admin.php
2025-09-29 00:35:53 +00:00

365 lines
14 KiB
PHP

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
// Gatekeeper: redirect if not logged in
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
header('Location: login.php');
exit;
}
require_once 'db/config.php';
$pdo = db();
// --- IMAGE SLICING FUNCTION ---
function create_puzzle_pieces($source_image_path, $puzzle_id, $pieces_count) {
if (!function_exists('gd_info')) {
error_log("GD Library is not installed or enabled.");
return false;
}
$image_info = getimagesize($source_image_path);
$source_width = $image_info[0];
$source_height = $image_info[1];
$mime_type = $image_info['mime'];
switch ($mime_type) {
case 'image/jpeg':
$source_image = imagecreatefromjpeg($source_image_path);
break;
case 'image/png':
$source_image = imagecreatefrompng($source_image_path);
break;
case 'image/gif':
$source_image = imagecreatefromgif($source_image_path);
break;
default:
error_log("Unsupported image type: " . $mime_type);
return false;
}
if (!$source_image) {
error_log("Failed to create image from source.");
return false;
}
// Grid dimensions
$cols = 0;
$rows = 0;
if ($pieces_count == 16) { $cols = 4; $rows = 4; }
elseif ($pieces_count == 32) { $cols = 8; $rows = 4; }
elseif ($pieces_count == 64) { $cols = 8; $rows = 8; }
else {
error_log("Invalid pieces count: " . $pieces_count);
return false;
}
$piece_width = floor($source_width / $cols);
$piece_height = floor($source_height / $rows);
$output_dir = __DIR__ . '/puzzles/' . $puzzle_id . '/' . $pieces_count;
if (!is_dir($output_dir)) {
mkdir($output_dir, 0777, true);
}
for ($r = 0; $r < $rows; $r++) {
for ($c = 0; $c < $cols; $c++) {
$piece = imagecreatetruecolor($piece_width, $piece_height);
imagecopy(
$piece,
$source_image,
0, 0, // dest x, y
$c * $piece_width, $r * $piece_height, // source x, y
$piece_width, $piece_height
);
$piece_path = $output_dir . '/piece_' . $r . '_' . $c . '.jpg';
imagejpeg($piece, $piece_path, 90); // Save as JPG with 90% quality
imagedestroy($piece);
}
}
imagedestroy($source_image);
return true;
}
// Handle new puzzle upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_puzzle'])) {
$puzzle_name = trim($_POST['puzzle_name'] ?? '');
$pieces = (int)($_POST['pieces'] ?? 16);
$is_public = isset($_POST['is_public']) ? 1 : 0;
$image_file = $_FILES['puzzle_image'] ?? null;
if (!empty($puzzle_name) && $image_file && $image_file['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$original_filename = basename($image_file['name']);
$image_extension = pathinfo($original_filename, PATHINFO_EXTENSION);
$safe_filename = 'puzzle_' . uniqid() . '.' . $image_extension;
$upload_path = $upload_dir . $safe_filename;
if (move_uploaded_file($image_file['tmp_name'], $upload_path)) {
$stmt = $pdo->prepare('INSERT INTO puzzles (name, file_name, original_image, pieces, is_public, is_admin_upload) VALUES (?, ?, ?, ?, ?, 1)');
$stmt->execute([$puzzle_name, $safe_filename, $safe_filename, $pieces, $is_public]);
$puzzle_id = $pdo->lastInsertId();
create_puzzle_pieces($upload_path, $puzzle_id, $pieces);
header("Location: admin.php?success=1");
exit;
}
}
header("Location: admin.php?error=1");
exit;
}
// Handle puzzle deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_puzzle'])) {
$puzzle_id_to_delete = $_POST['puzzle_id'];
$stmt = $pdo->prepare('SELECT file_name FROM puzzles WHERE id = ?');
$stmt->execute([$puzzle_id_to_delete]);
$puzzle = $stmt->fetch(PDO::FETCH_ASSOC);
if ($puzzle) {
$original_image_path = __DIR__ . '/uploads/' . $puzzle['file_name'];
if (file_exists($original_image_path)) {
unlink($original_image_path);
}
}
$stmt = $pdo->prepare('DELETE FROM puzzles WHERE id = ?');
$stmt->execute([$puzzle_id_to_delete]);
$puzzle_dir = __DIR__ . '/puzzles/' . $puzzle_id_to_delete;
if (is_dir($puzzle_dir)) {
function delete_directory($dir) {
if (!is_dir($dir)) return;
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delete_directory("$dir/$file") : unlink("$dir/$file");
}
rmdir($dir);
}
delete_directory($puzzle_dir);
}
header("Location: admin.php?deleted=1");
exit;
}
// Handle user deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_user'])) {
$user_id_to_delete = $_POST['user_id'];
// Prevent self-deletion
if ($user_id_to_delete == $_SESSION['user_id']) {
header("Location: admin.php?error=self_delete");
exit;
}
// Add logic here to delete user-related data if necessary (e.g., scores)
$stmt = $pdo->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([$user_id_to_delete]);
header("Location: admin.php?user_deleted=1");
exit;
}
$puzzles = $pdo->query('SELECT id, name, file_name, pieces, is_public, created_at FROM puzzles ORDER BY created_at DESC')->fetchAll(PDO::FETCH_ASSOC);
// Fetch users
$users = $pdo->query('SELECT id, username, email, created_at FROM users ORDER BY created_at DESC')->fetchAll(PDO::FETCH_ASSOC);
$page_title = 'Pannello di Amministrazione';
require_once 'includes/header.php';
?>
<main class="container mt-5">
<?php if (isset($_GET['success'])):
?><div class="alert alert-success">Puzzle aggiunto con successo!</div>
<?php endif; ?>
<?php if (isset($_GET['deleted'])):
?><div class="alert alert-info">Puzzle eliminato con successo.</div>
<?php endif; ?>
<?php if (isset($_GET['user_deleted'])):
?><div class="alert alert-info">Utente eliminato con successo.</div>
<?php endif; ?>
<?php if (isset($_GET['error'])):
$error_message = 'Errore durante l\'upload. Controlla i dati e riprova.';
if ($_GET['error'] === 'self_delete') {
$error_message = 'Non puoi eliminare il tuo stesso account.';
}
?><div class="alert alert-danger"><?php echo $error_message; ?></div>
<?php endif; ?>
<!-- Upload Form -->
<div class="card p-4 mb-4">
<h2 class="mb-4">Aggiungi Nuovo Puzzle</h2>
<form action="admin.php" method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label for="puzzle_name" class="form-label">Nome del Puzzle</label>
<input type="text" class="form-control" id="puzzle_name" name="puzzle_name" required>
</div>
<div class="mb-3">
<label for="puzzle_image" class="form-label">Immagine</label>
<input type="file" class="form-control" id="puzzle_image" name="puzzle_image" accept="image/jpeg, image/png, image/gif" required>
</div>
<div class="mb-3">
<label for="pieces" class="form-label">Numero di Pezzi</label>
<select class="form-select" id="pieces" name="pieces">
<option value="16">16 (4x4)</option>
<option value="32">32 (8x4)</option>
<option value="64">64 (8x8)</option>
</select>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="is_public" name="is_public" value="1" checked>
<label class="form-check-label" for="is_public">
Rendi Pubblico
</label>
</div>
<button type="submit" name="add_puzzle" class="btn btn-primary">Aggiungi Puzzle</button>
</form>
</div>
<h1 class="mb-4">Gestione Puzzle</h1>
<div class="table-responsive card p-3">
<table class="table table-striped table-bordered table-dark">
<thead>
<tr>
<th>ID</th>
<th>Nome</th>
<th>Immagine</th>
<th>Pezzi</th>
<th>Pubblico</th>
<th>Creato il</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php if (empty($puzzles)):
?><tr class="text-center"><td colspan="7">Nessun puzzle trovato.</td></tr>
<?php else:
?><?php foreach ($puzzles as $puzzle):
$image_path = __DIR__ . '/uploads/' . $puzzle['file_name'];
if (file_exists($image_path)) {
$imageUrl = 'uploads/' . htmlspecialchars($puzzle['file_name']) . '?v=' . filemtime($image_path);
} else {
$imageUrl = 'assets/images/placeholder.png';
}
?><tr >
<td><?php echo htmlspecialchars($puzzle['id']); ?></td>
<td><a href="puzzle.php?id=<?php echo htmlspecialchars($puzzle['id']); ?>" target="_blank"><?php echo htmlspecialchars($puzzle['name']); ?></a></td>
<td><img src="<?php echo $imageUrl; ?>" alt="Puzzle" width="100" class="rounded"></td>
<td><?php echo htmlspecialchars($puzzle['pieces']); ?></td>
<td>
<label class="toggle-switch">
<input type="checkbox" class="is-public-toggle" data-puzzle-id="<?php echo $puzzle['id']; ?>" <?php echo $puzzle['is_public'] ? 'checked' : ''; ?>>
<span class="slider"></span>
</label>
</td>
<td><?php echo htmlspecialchars($puzzle['created_at']); ?></td>
<td>
<form method="POST" action="admin.php" onsubmit="return confirm('Sei sicuro di voler eliminare questo puzzle? L\\'azione è irreversibile.');">
<input type="hidden" name="puzzle_id" value="<?php echo htmlspecialchars($puzzle['id']); ?>">
<button type="submit" name="delete_puzzle" class="btn btn-danger btn-sm">Elimina</button>
</form>
</td>
</tr>
<?php endforeach; ?><?php endif; ?>
</tbody>
</table>
</div>
<!-- User Management Section -->
<div class="mt-5">
<h1 class="mb-4">Gestione Utenti</h1>
<div class="table-responsive card p-3">
<table class="table table-striped table-bordered table-dark">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Registrato il</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php if (empty($users)):
?><tr class="text-center"><td colspan="5">Nessun utente trovato.</td></tr>
<?php else:
?><?php foreach ($users as $user):
?><tr >
<td><?php echo htmlspecialchars($user['id']); ?></td>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td><?php echo htmlspecialchars($user['created_at']); ?></td>
<td>
<?php if ($user['id'] != $_SESSION['user_id']): // Prevent deleting yourself ?>
<form method="POST" action="admin.php" onsubmit="return confirm('Sei sicuro di voler eliminare questo utente? L\'azione è irreversibile.');">
<input type="hidden" name="user_id" value="<?php echo htmlspecialchars($user['id']); ?>">
<button type="submit" name="delete_user" class="btn btn-danger btn-sm">Elimina</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?><?php endif; ?>
</tbody>
</table>
</div>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', function () {
const toggles = document.querySelectorAll('.is-public-toggle');
toggles.forEach(toggle => {
toggle.addEventListener('change', function () {
const puzzleId = this.dataset.puzzleId;
const isPublic = this.checked;
fetch('api/toggle_puzzle_status.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
puzzle_id: puzzleId,
is_public: isPublic
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error('Error updating puzzle status:', data.message);
// Optionally, revert the switch on failure
this.checked = !isPublic;
}
})
.catch(error => {
console.error('Fetch error:', error);
this.checked = !isPublic;
});
});
});
});
</script>
<?php require_once 'includes/footer.php'; ?>