getConnection(); $offset = ($page - 1) * $per_page; if ($user_id) { $sql = "SELECT g.*, u.user_name FROM gallery g JOIN users u ON g.user_id = u.user_id WHERE g.user_id = :user_id ORDER BY g.created_at DESC LIMIT :offset, :per_page"; $query = $database->prepare($sql); $query->bindParam(':user_id', $user_id, PDO::PARAM_INT); } else { $sql = "SELECT g.*, u.user_name FROM gallery g JOIN users u ON g.user_id = u.user_id WHERE g.is_public = 1 ORDER BY g.created_at DESC LIMIT :offset, :per_page"; $query = $database->prepare($sql); } $query->bindParam(':offset', $offset, PDO::PARAM_INT); $query->bindParam(':per_page', $per_page, PDO::PARAM_INT); $query->execute(); return $query->fetchAll(PDO::FETCH_OBJ); } public static function getImageCount($user_id = null) { $database = DatabaseFactory::getFactory()->getConnection(); if ($user_id) { $sql = "SELECT COUNT(*) as count FROM gallery WHERE user_id = :user_id"; $query = $database->prepare($sql); $query->execute(array(':user_id' => $user_id)); } else { $sql = "SELECT COUNT(*) as count FROM gallery WHERE is_public = 1"; $query = $database->prepare($sql); $query->execute(); } $result = $query->fetch(PDO::FETCH_ASSOC); return (int)$result['count']; } public static function getImage($image_id) { $database = DatabaseFactory::getFactory()->getConnection(); $sql = "SELECT g.*, u.user_name FROM gallery g JOIN users u ON g.user_id = u.user_id WHERE g.id = :image_id LIMIT 1"; $query = $database->prepare($sql); $query->execute(array(':image_id' => $image_id)); return $query->fetch(PDO::FETCH_OBJ); } public static function uploadImage($file, $title, $description, $is_public) { $user_id = Session::get('user_id'); if (!$user_id) { Session::add('feedback_negative', 'You must be logged in to upload'); return false; } $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!in_array($file['type'], $allowed_types)) { Session::add('feedback_negative', 'Invalid file type. Allowed: JPG, PNG, GIF, WebP'); return false; } $max_size = 10 * 1024 * 1024; if ($file['size'] > $max_size) { Session::add('feedback_negative', 'File too large. Maximum size: 10MB'); return false; } $image_data = file_get_contents($file['tmp_name']); if ($image_data === false) { Session::add('feedback_negative', 'Failed to read uploaded file'); return false; } $thumb_data = self::createThumbnailData($file['tmp_name'], $file['type'], 300); if (!$thumb_data) { Session::add('feedback_negative', 'Failed to create thumbnail'); return false; } $encryption_key = bin2hex(random_bytes(32)); $iv = random_bytes(openssl_cipher_iv_length(self::$cipher)); $encrypted_image = openssl_encrypt($image_data, self::$cipher, $encryption_key, OPENSSL_RAW_DATA, $iv); $encrypted_thumb = openssl_encrypt($thumb_data, self::$cipher, $encryption_key, OPENSSL_RAW_DATA, $iv); $encrypted_image = base64_encode($iv . $encrypted_image); $encrypted_thumb = base64_encode($iv . $encrypted_thumb); $upload_dir = dirname(__FILE__) . '/../../public/gallery_uploads/'; if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true); } $filename = uniqid('enc_') . '_' . time() . '.bin'; $thumb_filename = 'thumb_' . $filename; if (file_put_contents($upload_dir . $filename, $encrypted_image) === false) { Session::add('feedback_negative', 'Failed to save encrypted image'); return false; } if (file_put_contents($upload_dir . $thumb_filename, $encrypted_thumb) === false) { unlink($upload_dir . $filename); Session::add('feedback_negative', 'Failed to save encrypted thumbnail'); return false; } try { $database = DatabaseFactory::getFactory()->getConnection(); $sql = "INSERT INTO gallery (user_id, filename, thumb_filename, title, description, is_public, file_size, mime_type, encryption_key) VALUES (:user_id, :filename, :thumb_filename, :title, :description, :is_public, :file_size, :mime_type, :encryption_key)"; $query = $database->prepare($sql); $result = $query->execute(array( ':user_id' => $user_id, ':filename' => $filename, ':thumb_filename' => $thumb_filename, ':title' => $title ?: pathinfo($file['name'], PATHINFO_FILENAME), ':description' => $description, ':is_public' => $is_public ? 1 : 0, ':file_size' => $file['size'], ':mime_type' => $file['type'], ':encryption_key' => $encryption_key )); if ($result) { return $database->lastInsertId(); } unlink($upload_dir . $filename); unlink($upload_dir . $thumb_filename); Session::add('feedback_negative', 'Failed to save to database'); return false; } catch (PDOException $e) { unlink($upload_dir . $filename); unlink($upload_dir . $thumb_filename); Session::add('feedback_negative', 'Database error: ' . $e->getMessage()); return false; } } public static function getDecryptedImage($image_id, $thumbnail = false) { $image = self::getImage($image_id); if (!$image) { return null; } $upload_dir = dirname(__FILE__) . '/../../public/gallery_uploads/'; $filename = $thumbnail ? $image->thumb_filename : $image->filename; $filepath = $upload_dir . $filename; if (!file_exists($filepath)) { return null; } $encrypted_data = file_get_contents($filepath); if ($encrypted_data === false) { return null; } $encrypted_data = base64_decode($encrypted_data); $iv_length = openssl_cipher_iv_length(self::$cipher); $iv = substr($encrypted_data, 0, $iv_length); $encrypted_data = substr($encrypted_data, $iv_length); $decrypted = openssl_decrypt($encrypted_data, self::$cipher, $image->encryption_key, OPENSSL_RAW_DATA, $iv); return array( 'data' => $decrypted, 'mime_type' => $image->mime_type ); } public static function updateImage($image_id, $title, $description, $is_public) { $user_id = Session::get('user_id'); $database = DatabaseFactory::getFactory()->getConnection(); $sql = "UPDATE gallery SET title = :title, description = :description, is_public = :is_public WHERE id = :image_id AND user_id = :user_id"; $query = $database->prepare($sql); return $query->execute(array( ':image_id' => $image_id, ':user_id' => $user_id, ':title' => $title, ':description' => $description, ':is_public' => $is_public ? 1 : 0 )); } public static function deleteImage($image_id) { $user_id = Session::get('user_id'); $image = self::getImage($image_id); if (!$image || $image->user_id != $user_id) { return false; } $upload_dir = dirname(__FILE__) . '/../../public/gallery_uploads/'; $filepath = $upload_dir . $image->filename; $thumbpath = $upload_dir . $image->thumb_filename; if (file_exists($filepath)) { unlink($filepath); } if (file_exists($thumbpath)) { unlink($thumbpath); } $database = DatabaseFactory::getFactory()->getConnection(); $sql = "DELETE FROM gallery WHERE id = :image_id AND user_id = :user_id"; $query = $database->prepare($sql); return $query->execute(array(':image_id' => $image_id, ':user_id' => $user_id)); } private static function createThumbnailData($source, $mime_type, $max_size) { $image_info = getimagesize($source); if (!$image_info) { return false; } $width = $image_info[0]; $height = $image_info[1]; $type = $image_info[2]; switch ($type) { case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($source); break; case IMAGETYPE_PNG: $image = imagecreatefrompng($source); break; case IMAGETYPE_GIF: $image = imagecreatefromgif($source); break; case IMAGETYPE_WEBP: $image = imagecreatefromwebp($source); break; default: return false; } if (!$image) { return false; } $ratio = min($max_size / $width, $max_size / $height); if ($ratio >= 1) { $new_width = $width; $new_height = $height; } else { $new_width = (int)($width * $ratio); $new_height = (int)($height * $ratio); } $thumb = imagecreatetruecolor($new_width, $new_height); if ($type == IMAGETYPE_PNG || $type == IMAGETYPE_GIF) { imagealphablending($thumb, false); imagesavealpha($thumb, true); $transparent = imagecolorallocatealpha($thumb, 0, 0, 0, 127); imagefilledrectangle($thumb, 0, 0, $new_width, $new_height, $transparent); } imagecopyresampled($thumb, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height); ob_start(); switch ($type) { case IMAGETYPE_JPEG: imagejpeg($thumb, null, 85); break; case IMAGETYPE_PNG: imagepng($thumb, null, 8); break; case IMAGETYPE_GIF: imagegif($thumb); break; case IMAGETYPE_WEBP: imagewebp($thumb, null, 85); break; } $thumb_data = ob_get_clean(); return $thumb_data; } public static function formatFileSize($bytes) { if ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } return $bytes . ' bytes'; } }