diff --git a/public/css/default.css b/public/css/default.css index b04aaf5..16261a7 100644 --- a/public/css/default.css +++ b/public/css/default.css @@ -12,6 +12,9 @@ --color-flash-success: darkgreen; --color-flash-success-bg: honeydew; --color-flash-success-border-left: forestgreen; + --color-flash-warning: darkgoldenrod; + --color-flash-warning-bg: lightgoldenrodyellow; + --color-flash-warning-border-left: gold; --color-mood-border: darkslateblue; --color-mood-hover: lightsteelblue; --color-mood-selected: lightblue; @@ -324,6 +327,12 @@ summary:focus, color: var(--color-flash-error); } +.flash-warning { + background-color: var(--color-flash-warning-bg); + border-left-color: var(--color-flash-warning-border-left); + color: var(--color-flash-warning); +} + .fieldset-items { margin-bottom: 14px; display: grid; diff --git a/src/Controller/CssController/CssController.php b/src/Controller/CssController/CssController.php index 05c94fc..fb49b15 100644 --- a/src/Controller/CssController/CssController.php +++ b/src/Controller/CssController/CssController.php @@ -26,24 +26,30 @@ class CssController extends Controller { $cssRow = $cssModel->getByFilename($filename); if (!$cssRow){ http_response_code(404); - Log::error("Custom css file not in database: {$filename}"); - exit; + $msg = "Custom css file not in database: {$filename}"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // Make sure the file exists on the filesystem and is readable $filePath = CSS_UPLOAD_DIR . "/$filename"; if (!file_exists($filePath) || !is_readable($filePath)) { http_response_code(404); - Log::error("Custom css file not found or not readable: $filePath"); - exit; + $msg = "Custom css file not found or not readable: {$filePath}"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // Make sure the file has a .css extension $ext = strToLower(pathinfo($filename, PATHINFO_EXTENSION)); if($ext != 'css'){ http_response_code(400); - Log::error("Invalid file type requested: $ext"); - exit; + $msg = "Invalid file type requested: {$ext}"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // If we get here, serve the file @@ -61,8 +67,11 @@ class CssController extends Controller { // Make sure the default CSS file exists and is readable if (!file_exists($filePath) || !is_readable($filePath)) { http_response_code(404); - Log::error("Default CSS file not found"); - exit; + $msg = "Default CSS file not found"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + + return; } // Serve the file @@ -96,7 +105,10 @@ class CssController extends Controller { // Don't try to delete the default theme. if (!$_POST['selectCssFile']){ http_response_code(400); - exit("Cannot delete default theme"); + $msg = "Cannot delete default theme."; + Log::warning($msg); + Session::setFlashMessage('warning', $msg); + return; } // Get the data for the selected CSS file @@ -107,7 +119,10 @@ class CssController extends Controller { // exit if the requested file isn't in the database if (!$cssRow){ http_response_code(400); - exit("No entry found for css id $cssId"); + $msg = "No entry found for css id {$cssId}."; + Log::warning($msg); + Session::setFlashMessage('warning', $msg); + return; } // get the filename @@ -115,8 +130,11 @@ class CssController extends Controller { // delete the file from the database if (!$cssModel->delete($cssId)){ - http_response_code(400); - exit("Error deleting theme"); + http_response_code(500); + $msg = "Error deleting theme {$cssId}."; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // Build the full path to the file @@ -125,27 +143,36 @@ class CssController extends Controller { // Exit if the file doesn't exist or isn't readable if (!file_exists($filePath) || !is_readable($filePath)) { http_response_code(404); - exit("CSS file not found: $filePath"); + $msg = "CSS file not found: {$filePath}"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // Delete the file if (!unlink($filePath)){ - http_response_code(400); - exit("Error deleting file: $filePath"); + http_response_code(500); + $msg = "Error deleting file: {$filePath}"; + Log::error($msg); + Session::setFlashMessage('error', $msg); + return; } // Set the theme back to default try { $app['settings']->cssId = null; $app['settings'] = $app['settings']->save(); - Session::setFlashMessage('success', 'Theme ' . $cssFilename . ' deleted.'); + $msg = "Theme {$cssFilename} deleted."; + Log::debug($msg); + Session::setFlashMessage('success', $msg); } catch (Exception $e) { - Log::error("Failed to update config after deleting theme: " . $e->getMessage()); - Session::setFlashMessage('error', 'Theme deleted but failed to update settings'); + $msg = "Failed to update config after deleting theme."; + Log::error($msg . ' ' . $e->getMessage()); + Session::setFlashMessage('error', $msg); } } - private function handleSetTheme() { + private function handleSetTheme(): void { global $app; try { @@ -166,7 +193,7 @@ class CssController extends Controller { } } - private function handleUpload() { + private function handleUpload(): void { try { // Check if file was uploaded if (!isset($_FILES['uploadCssFile']) || $_FILES['uploadCssFile']['error'] !== UPLOAD_ERR_OK) { @@ -221,12 +248,11 @@ class CssController extends Controller { } catch (Exception $e) { // Set error flash message - // Todo - don't do a global catch like this. Subclass Exception. Session::setFlashMessage('error', 'Upload exception: ' . $e->getMessage()); } } - private function validateCssContent($content) { + private function validateCssContent($content): void { // Remove comments $content = preg_replace('/\/\*.*?\*\//s', '', $content); @@ -247,7 +273,7 @@ class CssController extends Controller { } } - private function scanForMaliciousContent($content, $fileName) { + private function scanForMaliciousContent($content, $fileName): void { // Check for suspicious patterns $suspiciousPatterns = [ '/javascript:/i', @@ -286,12 +312,11 @@ class CssController extends Controller { } } - private function generateSafeFileName($originalName) { + private function generateSafeFileName($originalName): string { // Remove path information and dangerous characters $fileName = basename($originalName); $fileName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $fileName); return $fileName; } - } diff --git a/src/Controller/EmojiController/EmojiController.php b/src/Controller/EmojiController/EmojiController.php index bc889de..63c5aa6 100644 --- a/src/Controller/EmojiController/EmojiController.php +++ b/src/Controller/EmojiController/EmojiController.php @@ -10,9 +10,10 @@ declare(strict_types=1); $emojiModel = new EmojiModel($app['db']); $emojiList = $emojiModel->getAll(); } catch (Exception $e) { - Log::error("Failed to load emoji list: " . $e->getMessage()); $emojiList = []; - Session::setFlashMessage('error', 'Failed to load custom emoji'); + $msg = "Failed to load emoji list."; + Log::error($msg . " " . $e->getMessage()); + Session::setFlashMessage('error', $msg); } $vars = [ @@ -49,23 +50,31 @@ declare(strict_types=1); // TODO - log a warning if mbstring isn't loaded $charCount = mb_strlen($emoji, 'UTF-8'); if ($charCount !== 1) { - // TODO - handle error + $msg = "Emoji must be a single UTF-8 encoded character."; + Log::error($msg); + Session::setFlashMessage('error', $msg); return false; } + } else { + Log::warning("mbstring extension not loaded. Skipping emoji character count validation."); } // Validate the emoji is actually an emoji $emojiPattern = '/^[\x{1F000}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{1F900}-\x{1F9FF}\x{1FA70}-\x{1FAFF}]$/u'; if (!preg_match($emojiPattern, $emoji)) { - // TODO - handle error + $msg = "Character is not a valid emoji."; + Log::error($msg); + Session::setFlashMessage('error', $msg); return false; } // emojis should have more bytes than characters $byteCount = strlen($emoji); if ($byteCount <= 1) { - // TODO - handle error + $msg = "Character is not a valid emoji (too few bytes)."; + Log::error($msg); + Session::setFlashMessage('error', $msg); return false; } @@ -76,7 +85,7 @@ declare(strict_types=1); global $app; if (!$this->isValidEmoji($emoji)){ - Session::setFlashMessage('error', 'Invalid emoji format'); + // exceptions are handled in isValidEmoji return; } @@ -84,10 +93,14 @@ declare(strict_types=1); try { $emojiModel = new EmojiModel($app['db']); $emojiModel->add($emoji, $description); - Session::setFlashMessage('success', 'Emoji added successfully'); + $msg = "Emoji added: {$emoji} - {$description}"; + + Log::debug($msg); + Session::setFlashMessage('success', $msg); } catch (Exception $e) { - Log::error("Failed to add emoji: " . $e->getMessage()); - Session::setFlashMessage('error', 'Failed to add emoji'); + $msg = "Failed to add emoji."; + Log::error($msg . " " . $e->getMessage()); + Session::setFlashMessage('error', $msg); } } @@ -100,10 +113,14 @@ declare(strict_types=1); try { $emojiModel = new EmojiModel($app['db']); $emojiModel->delete($ids); - Session::setFlashMessage('success', 'Emoji deleted successfully'); + $msg = "Emoji deleted."; + + Log::debug($msg); + Session::setFlashMessage('success', $msg); } catch (Exception $e) { - Log::error("Failed to delete emoji: " . $e->getMessage()); - Session::setFlashMessage('error', 'Failed to delete emoji'); + $msg = "Failed to delete emoji."; + Log::error($msg . " " . $e->getMessage()); + Session::setFlashMessage('error', $msg); } } } diff --git a/src/Controller/TickController/TickController.php b/src/Controller/TickController/TickController.php index 6474b30..5b39f88 100644 --- a/src/Controller/TickController/TickController.php +++ b/src/Controller/TickController/TickController.php @@ -61,6 +61,6 @@ class TickController extends Controller{ // Redirect back to homepage header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, '')); - exit(); + exit; } } \ No newline at end of file diff --git a/src/Framework/Session/Session.php b/src/Framework/Session/Session.php index bff3b9a..6319c79 100644 --- a/src/Framework/Session/Session.php +++ b/src/Framework/Session/Session.php @@ -67,7 +67,6 @@ class Session { // valid types are: // - success // - error - // - info // - warning public static function setFlashMessage(string $type, string $message): void { if (!isset($_SESSION['flash'][$type])){