diff --git a/include/header.php b/include/header.php
index 04c6646..6fdfde5 100644
--- a/include/header.php
+++ b/include/header.php
@@ -57,6 +57,11 @@ if ($theme === 'dark' || in_array('dark-background-active', $body_classes)) {
+
+
+
+
+
diff --git a/settings.php b/settings.php
index edf41c4..77637ad 100644
--- a/settings.php
+++ b/settings.php
@@ -6,6 +6,86 @@ $GLOBALS['body_class'] = 'settings-page';
require_once 'include/auth.php';
+/**
+ * Resize an image while maintaining aspect ratio and transparency
+ * @param string $source_path Path to source image
+ * @param string $destination_path Path to save resized image
+ * @param int $max_width Maximum width
+ * @param int $max_height Maximum height
+ * @param string $extension File extension
+ * @return bool Success status
+ */
+function resizeImage($source_path, $destination_path, $max_width, $max_height, $extension) {
+ // Get original image dimensions
+ $image_info = getimagesize($source_path);
+ if ($image_info === false) {
+ return false;
+ }
+
+ $original_width = $image_info[0];
+ $original_height = $image_info[1];
+
+ // Calculate new dimensions maintaining aspect ratio
+ $ratio = min($max_width / $original_width, $max_height / $original_height);
+ $new_width = round($original_width * $ratio);
+ $new_height = round($original_height * $ratio);
+
+ // Create image resource based on file type
+ switch (strtolower($extension)) {
+ case 'jpg':
+ case 'jpeg':
+ $source_image = imagecreatefromjpeg($source_path);
+ break;
+ case 'png':
+ $source_image = imagecreatefrompng($source_path);
+ break;
+ case 'gif':
+ $source_image = imagecreatefromgif($source_path);
+ break;
+ default:
+ return false;
+ }
+
+ if ($source_image === false) {
+ return false;
+ }
+
+ // Create new image with transparency support
+ $new_image = imagecreatetruecolor($new_width, $new_height);
+
+ // Preserve transparency for PNG and GIF
+ if ($extension === 'png' || $extension === 'gif') {
+ imagealphablending($new_image, false);
+ imagesavealpha($new_image, true);
+ $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
+ imagefilledrectangle($new_image, 0, 0, $new_width, $new_height, $transparent);
+ }
+
+ // Resize the image
+ imagecopyresampled($new_image, $source_image, 0, 0, 0, 0, $new_width, $new_height, $original_width, $original_height);
+
+ // Save the resized image
+ $result = false;
+ switch (strtolower($extension)) {
+ case 'jpg':
+ case 'jpeg':
+ $result = imagejpeg($new_image, $destination_path, 90);
+ break;
+ case 'png':
+ $result = imagepng($new_image, $destination_path, 9);
+ break;
+ case 'gif':
+ $result = imagegif($new_image, $destination_path);
+ break;
+ }
+
+ // Clean up memory
+ imagedestroy($source_image);
+ imagedestroy($new_image);
+
+ return $result;
+}
+
// Flash messages for feedback
$message = '';
$message_type = '';
@@ -68,10 +148,44 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (in_array($favicon_ext, $allowed_favicon_ext)) {
$favicon_filename = 'favicon.' . $favicon_ext;
$favicon_path = $uploadDir . $favicon_filename;
- if (move_uploaded_file($_FILES['site_favicon']['tmp_name'], $favicon_path)) {
- $upsert_stmt->execute(['site_favicon', $favicon_path . '?v=' . time()]);
+
+ // Check if it's an image file (not SVG or ICO)
+ if (in_array($favicon_ext, ['png', 'jpg', 'jpeg', 'gif'])) {
+ // Get image dimensions
+ $image_info = getimagesize($_FILES['site_favicon']['tmp_name']);
+ if ($image_info === false) {
+ $_SESSION['flash_message'] = ['text' => 'Invalid image file for favicon.', 'type' => 'danger'];
+ } else {
+ $width = $image_info[0];
+ $height = $image_info[1];
+
+ // Validate dimensions (minimum 180x180, maximum 256x256)
+ if ($width < 180 || $height < 180) {
+ $_SESSION['flash_message'] = ['text' => 'Favicon must be at least 180x180 pixels for Apple Touch Icon compatibility.', 'type' => 'danger'];
+ } elseif ($width > 256 || $height > 256) {
+ // Resize if too large
+ if (resizeImage($_FILES['site_favicon']['tmp_name'], $favicon_path, 256, 256, $favicon_ext)) {
+ $upsert_stmt->execute(['site_favicon', $favicon_path . '?v=' . time()]);
+ $_SESSION['flash_message'] = ['text' => 'Favicon uploaded and resized to 256x256 pixels for optimal Apple Touch Icon compatibility.', 'type' => 'success'];
+ } else {
+ $_SESSION['flash_message'] = ['text' => 'Failed to resize favicon.', 'type' => 'danger'];
+ }
+ } else {
+ // Size is acceptable, just move the file
+ if (move_uploaded_file($_FILES['site_favicon']['tmp_name'], $favicon_path)) {
+ $upsert_stmt->execute(['site_favicon', $favicon_path . '?v=' . time()]);
+ } else {
+ $_SESSION['flash_message'] = ['text' => 'Failed to move uploaded favicon.', 'type' => 'danger'];
+ }
+ }
+ }
} else {
- $_SESSION['flash_message'] = ['text' => 'Failed to move uploaded favicon.', 'type' => 'danger'];
+ // For SVG and ICO files, just move them without resizing
+ if (move_uploaded_file($_FILES['site_favicon']['tmp_name'], $favicon_path)) {
+ $upsert_stmt->execute(['site_favicon', $favicon_path . '?v=' . time()]);
+ } else {
+ $_SESSION['flash_message'] = ['text' => 'Failed to move uploaded favicon.', 'type' => 'danger'];
+ }
}
} else {
$_SESSION['flash_message'] = ['text' => 'Invalid file type for favicon.', 'type' => 'danger'];
@@ -277,7 +391,15 @@ require_once 'include/header.php';