365 lines
14 KiB
PHP
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'; ?>
|