From 856677659ec7a82fa57af0ed470110f04bec1901 Mon Sep 17 00:00:00 2001 From: Greg Sarjeant <1686767+gsarjeant@users.noreply.github.com> Date: Mon, 16 Jun 2025 16:47:35 -0400 Subject: [PATCH] Add flash messages. --- public/css/tkr.css | 57 +++++++++++++++++++ .../AdminController/AdminController.php | 16 ++++-- src/Controller/Controller.php | 9 +++ .../CssController/CssController.php | 18 ++++-- src/Framework/Session/Session.php | 24 ++++++++ src/View/FlashView/FlashView.php | 21 +++++++ templates/main.php | 4 ++ templates/partials/admin.php | 10 ++-- templates/partials/emoji.php | 2 + templates/partials/login.php | 13 +++-- 10 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 src/View/FlashView/FlashView.php diff --git a/public/css/tkr.css b/public/css/tkr.css index bb1cbe4..aa9e619 100644 --- a/public/css/tkr.css +++ b/public/css/tkr.css @@ -38,6 +38,23 @@ /* Shadow colors */ --shadow-primary: rgba(66, 153, 225, 0.1); --shadow-primary-strong: rgba(66, 153, 225, 0.3); + + /* Flash colors */ + --color-flash-success: #155724; + --color-flash-success-bg: #d4edda; + --color-flash-success-border-left: #28a745; + + --color-flash-error: #721c24; + --color-flash-error-bg: #f8d7da; + --color-flash-error-border-left: #dc3545; + + --color-flash-warning: #856404; + --color-flash-warning-bg: #fff3cd; + --color-flash-warning-border-left: #ffc107; + + --color-flash-info: #0c5460; + --color-flash-info-bg: #d1ecf1; + --color-flash-info-border-left: #17a2b8; } body { @@ -227,6 +244,46 @@ label.description { gap: 0.5em; } +.flash-messages { + background: white; + margin-top: 10px; + padding: 15px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); +} + +.flash-message { + padding: 12px 16px; + margin: 5px 0; + border-radius: 4px; + border-left: 4px solid; + font-weight: 500; +} + +.flash-success { + background-color: var(--color-flash-success-bg); + border-left-color: var(--color-flash-success-border-left); + color: var(--color-flash-success); +} + +.flash-error { + background-color: var(--color-flash-error-bg); + border-left-color: var(--color-flash-error-border-left); + 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); +} + +.flash-info { + background-color: var(--color-flash-info-bg); + border-left-color: var(--color-flash-info-border-left); + color: var(--color-flash-info); +} + .fieldset-items { margin-bottom: 14px; display: grid; diff --git a/src/Controller/AdminController/AdminController.php b/src/Controller/AdminController/AdminController.php index 8338189..869f89c 100644 --- a/src/Controller/AdminController/AdminController.php +++ b/src/Controller/AdminController/AdminController.php @@ -100,11 +100,11 @@ class AdminController extends Controller { } // If a password was sent, make sure it matches the confirmation - if ($password && !($password = $confirmPassword)){ + if ($password && !($password === $confirmPassword)){ $errors[] = "Passwords do not match"; } - // TODO: Actually handle errors + // Validation complete if (empty($errors)) { // Update site settings $config->siteTitle = $siteTitle; @@ -114,6 +114,7 @@ class AdminController extends Controller { $config->itemsPerPage = $itemsPerPage; // Save site settings and reload config from database + // TODO - raise and handle exception on failure $config = $config->save(); // Update user profile @@ -123,19 +124,24 @@ class AdminController extends Controller { $user->website = $website; // Save user profile and reload user from database + // TODO - raise and handle exception on failure $user = $user->save(); // Update the password if one was sent + // TODO - raise and handle exception on failure if($password){ $user->set_password($password); } + + Session::setFlashMessage('success', 'Settings updated'); } else { - echo implode(",", $errors); - exit; + foreach($errors as $error){ + Session::setFlashMessage('error', $error); + } } } - header('Location: ' . $config->basePath . 'admin'); + header('Location: ' . $_SERVER['PHP_SELF']); exit; } } diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php index b8c59d2..6c26b94 100644 --- a/src/Controller/Controller.php +++ b/src/Controller/Controller.php @@ -13,6 +13,15 @@ class Controller { throw new RuntimeException("Template not found: $childTemplatePath"); } + // always check for flash messages and add them if they exist + if (Session::hasFlashMessages()){ + $flashMessages = Session::getFlashMessages(); + $flashView = new FlashView(); + $flashSection = $flashView->renderFlashSection($flashMessages); + + $vars['flashSection'] = $flashSection; + } + extract($vars, EXTR_SKIP); include $templatePath; } diff --git a/src/Controller/CssController/CssController.php b/src/Controller/CssController/CssController.php index 514ee9f..32660fa 100644 --- a/src/Controller/CssController/CssController.php +++ b/src/Controller/CssController/CssController.php @@ -63,7 +63,8 @@ class CssController extends Controller { break; } - header('Location: ' . $config->basePath . 'admin/css'); + // redirect after handling to avoid resubmitting form + header('Location: ' . $_SERVER['PHP_SELF']); exit; } @@ -114,6 +115,9 @@ class CssController extends Controller { // Set the theme back to default $config->cssId = null; $config = $config->save(); + + // Set flash message + Session::setFlashMessage('success', 'Theme ' . $cssFilename . ' deleted.'); } private function handleSetTheme() { @@ -129,6 +133,9 @@ class CssController extends Controller { // Update the site theme $config = $config->save(); + + // Set flash message + Session::setFlashMessage('success', 'Theme applied.'); } private function handleUpload() { @@ -179,11 +186,14 @@ class CssController extends Controller { // Add upload to database $cssModel = new CssModel(); $cssModel->save($safeFilename, $description); - - return true; + + // Set success flash message + Session::setFlashMessage('success', 'Theme uploaded as ' . $safeFilename); } catch (Exception $e) { - return false; + // Set error flash message + // Todo - don't do a global catch like this. Subclass Exception. + Session::setFlashMessage('error', 'Upload exception: ' . $e->getMessage()); } } diff --git a/src/Framework/Session/Session.php b/src/Framework/Session/Session.php index 88ccf58..4220451 100644 --- a/src/Framework/Session/Session.php +++ b/src/Framework/Session/Session.php @@ -34,6 +34,30 @@ class Session { return self::isLoggedIn() && self::isValidCsrfToken($token); } + // valid types are: + // - success + // - error + // - info + // - warning + public static function setFlashMessage(string $type, string $message): void { + if (!isset($_SESSION['flash'][$type])){ + $_SESSION['flash'][$type] = []; + } + $_SESSION['flash'][$type][] = $message; + } + + public static function getFlashMessages(): ?array { + if (isset($_SESSION['flash'])) { + $messages = $_SESSION['flash']; + unset($_SESSION['flash']); + return $messages; + } + } + + public static function hasFlashMessages(): bool { + return isset($_SESSION['flash']) && !empty($_SESSION['flash']); + } + public static function end(): void { $_SESSION = []; session_destroy(); diff --git a/src/View/FlashView/FlashView.php b/src/View/FlashView/FlashView.php new file mode 100644 index 0000000..cf7e9a0 --- /dev/null +++ b/src/View/FlashView/FlashView.php @@ -0,0 +1,21 @@ + + + 0): ?> +
+ $messages): ?> + +
+ +
+ + +
+ + @@ -25,6 +26,9 @@ + + + \ No newline at end of file diff --git a/templates/partials/admin.php b/templates/partials/admin.php index 0e615ce..bc6d13c 100644 --- a/templates/partials/admin.php +++ b/templates/partials/admin.php @@ -8,7 +8,7 @@ method="post">
- UserModel settings + User settings
- + +
+
diff --git a/templates/partials/login.php b/templates/partials/login.php index 35e1773..8920ff7 100644 --- a/templates/partials/login.php +++ b/templates/partials/login.php @@ -6,8 +6,13 @@

- -
-
- +
+ + + + + +
+ +