From 20129d9fcfb196672789dcb8da9e75970e80a9e9 Mon Sep 17 00:00:00 2001 From: Greg Sarjeant <1686767+gsarjeant@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:33:32 -0400 Subject: [PATCH] Convert login and mood pages to MVC pattern. --- public/index.php | 45 ++++-- public/save_tick.php | 29 ---- src/Controller/Home/Home.php | 71 ++++++---- src/Controller/Login/Login.php | 48 +++++++ src/Controller/Mood/Mood.php | 247 +++++++++++++++++++++++++++++++++ src/View/Mood/Mood.php | 43 ++++++ src/lib/mood.php | 53 ------- templates/home.php | 10 +- templates/login.php | 44 +----- templates/mood.php | 19 +++ templates/set_mood.php | 44 ------ templates/tick.php | 5 - 12 files changed, 450 insertions(+), 208 deletions(-) delete mode 100644 public/save_tick.php create mode 100644 src/Controller/Login/Login.php create mode 100644 src/Controller/Mood/Mood.php create mode 100644 src/View/Mood/Mood.php delete mode 100644 src/lib/mood.php create mode 100644 templates/mood.php delete mode 100644 templates/set_mood.php diff --git a/public/index.php b/public/index.php index 308f76d..a7a9daf 100644 --- a/public/index.php +++ b/public/index.php @@ -1,5 +1,11 @@ basePath) === 0) { $path = trim($path, '/'); -function route($pattern, $callback, $methods = ['GET']) { +function route(string $pattern, string $controller, array $methods = ['GET']) { global $path, $method; if (!in_array($method, $methods)) { return false; } - // Convert route pattern to regex $pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $pattern); $pattern = '#^' . $pattern . '$#'; if (preg_match($pattern, $path, $matches)) { - array_shift($matches); // Remove full match - call_user_func_array($callback, $matches); + array_shift($matches); + + if (strpos($controller, '@') !== false) { + [$className, $methodName] = explode('@', $controller); + } else { + // Default to 'index' method if no method specified + $className = $controller; + $methodName = 'index'; + } + $instance = new $className(); + call_user_func_array([$instance, $methodName], $matches); return true; } @@ -78,7 +92,22 @@ function route($pattern, $callback, $methods = ['GET']) { header('Content-Type: text/html; charset=utf-8'); // routes -route('', function(){ - $hc = new HomeController(); - echo $hc->render(); -}); +$routes = [ + ['', 'HomeController'], + ['', 'HomeController@tick', ['POST']], + ['login', 'LoginController'], + ['login', 'LoginController@login', ['POST']], + ['mood', 'MoodController'], + ['mood', 'MoodController@set_mood', ['POST']], + +]; + +foreach ($routes as $routeConfig) { + $pattern = $routeConfig[0]; + $controller = $routeConfig[1]; + $methods = $routeConfig[2] ?? ['GET']; + + if (route($pattern, $controller, $methods)) { + break; + } +}; \ No newline at end of file diff --git a/public/save_tick.php b/public/save_tick.php deleted file mode 100644 index d0b21d4..0000000 --- a/public/save_tick.php +++ /dev/null @@ -1,29 +0,0 @@ -basePath); -exit; \ No newline at end of file diff --git a/src/Controller/Home/Home.php b/src/Controller/Home/Home.php index 6bcfe6d..69924a3 100644 --- a/src/Controller/Home/Home.php +++ b/src/Controller/Home/Home.php @@ -1,5 +1,52 @@ itemsPerPage; + $offset = ($page - 1) * $limit; + $ticks = iterator_to_array(stream_ticks($limit, $offset)); + + $view = new HomeView(); + $tickList = $view->renderTicksSection($config->siteDescription, $ticks, $page, $limit); + + $vars = [ + 'isLoggedIn' => $isLoggedIn, + 'config' => $config, + 'user' => $user, + 'tickList' => $tickList, + ]; + + echo render_template(TEMPLATES_DIR . "/home.php", $vars); + } + + // POST handler + // Saves the tick and reloads the homepage + public function tick(){ + if ($_SERVER['REQUEST_METHOD'] === 'POST' and isset($_POST['tick'])) { + // ensure that the session is valid before proceeding + if (!validateCsrfToken($_POST['csrf_token'])) { + // TODO: maybe redirect to login? Maybe with tick preserved? + die('Invalid CSRF token'); + } + + // save the tick + save_tick($_POST['tick']); + } + + // get the config + $config = Config::load(); + + // redirect to the index (will show the latest tick if one was sent) + header('Location: ' . $config->basePath); + exit; + } + private function stream_ticks(int $limit, int $offset = 0): Generator { $tick_files = glob(TICKS_DIR . '/*/*/*.txt'); usort($tick_files, fn($a, $b) => strcmp($b, $a)); // sort filenames in reverse chronological order @@ -50,28 +97,4 @@ class HomeController{ } } } - - public function render(){ - $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; - $isLoggedIn = isset($_SESSION['user_id']); - $config = Config::load(); - $user = User::load(); - - $limit = $config->itemsPerPage; - $offset = ($page - 1) * $limit; - $ticks = iterator_to_array(stream_ticks($limit, $offset)); - - $view = new HomeView(); - $tickList = $view->renderTicksSection($config->siteDescription, $ticks, $page, $limit); - - $vars = [ - 'isLoggedIn' => $isLoggedIn, - 'config' => $config, - 'user' => $user, - 'tickList' => $tickList, - ]; - - echo render_template(TEMPLATES_DIR . "/home.php", $vars); - - } } \ No newline at end of file diff --git a/src/Controller/Login/Login.php b/src/Controller/Login/Login.php new file mode 100644 index 0000000..dda1ee6 --- /dev/null +++ b/src/Controller/Login/Login.php @@ -0,0 +1,48 @@ + $config, + 'csrf_token' => $csrf_token, + 'error' => $error, + ]; + + echo render_template(TEMPLATES_DIR . '/login.php', $vars); + } + + function login(){ + $config = Config::load(); + + $error = ''; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!validateCsrfToken($_POST['csrf_token'])) { + die('Invalid CSRF token'); + } + + // TODO: move into session.php + $username = $_POST['username'] ?? ''; + $password = $_POST['password'] ?? ''; + + $db = get_db(); + $stmt = $db->prepare("SELECT id, username, password_hash FROM user WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password_hash'])) { + session_regenerate_id(true); + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + header('Location: ' . $config->basePath); + exit; + } else { + $error = 'Invalid username or password'; + } + } + + $csrf_token = generateCsrfToken(); + } +} \ No newline at end of file diff --git a/src/Controller/Mood/Mood.php b/src/Controller/Mood/Mood.php new file mode 100644 index 0000000..04d0715 --- /dev/null +++ b/src/Controller/Mood/Mood.php @@ -0,0 +1,247 @@ +render_mood_picker(self::get_emojis_with_labels(), $user->mood); + + $vars = [ + 'config' => $config, + 'moodPicker' => $moodPicker, + ]; + + echo render_template(TEMPLATES_DIR . "/mood.php", $vars); + } + + public function set_mood(){ + if ($_SERVER['REQUEST_METHOD'] === 'POST' and isset($_POST['mood'])) { + // ensure that the session is valid before proceeding + if (!validateCsrfToken($_POST['csrf_token'])) { + die('Invalid CSRF token'); + } + + // Get the data we need + $config = Config::load(); + $user = User::load(); + $mood = $_POST['mood']; + + // set the mood + $user->mood = $mood; + $user = $user->save(); + + // go back to the index and show the updated mood + header('Location: ' . $config->basePath); + //exit; + } + } + + private static function get_emojis_with_labels(): array { + return [ + 'faces' => [ + ['๐', 'grinning face'], + ['๐', 'grinning face with smiling eyes'], + ['๐', 'beaming face with smiling eyes'], + ['๐', 'grinning squinting face'], + ['๐ ', 'grinning face with sweat'], + ['๐', 'face with tears of joy'], + ['๐คฃ', 'rolling on the floor laughing'], + ['๐', 'smiling face with smiling eyes'], + ['๐', 'smiling face with halo'], + ['๐', 'slightly smiling face'], + ['๐', 'upside-down face'], + ['๐', 'winking face'], + ['๐', 'relieved face'], + ['๐', 'smiling face with heart-eyes'], + ['๐ฅฐ', 'smiling face with hearts'], + ['๐', 'face blowing a kiss'], + ['๐', 'kissing face'], + ['๐', 'kissing face with closed eyes'], + ['๐', 'face savoring food'], + ['๐', 'face with tongue'], + ['๐', 'winking face with tongue'], + ['๐', 'squinting face with tongue'], + ['๐คช', 'zany face'], + ['๐ฆธ', 'superhero'], + ['๐ฆน', 'supervillain'], + ['๐ง', 'mage'], + ['๐ง', 'vampire'], + ['๐ง', 'zombie'], + ['๐ง', 'genie'], + ], + 'gestures' => [ + ['๐', 'waving hand'], + ['๐', 'vulcan salute'], + ['๐', 'OK hand'], + ['๐ค', 'pinched fingers'], + ['โ๏ธ', 'victory hand'], + ['๐ค', 'crossed fingers'], + ['๐ค', 'love-you gesture'], + ['๐ค', 'sign of the horns'], + ['๐ค', 'call me hand'], + ['๐', 'thumbs up'], + ['๐', 'thumbs down'], + ['โ', 'raised fist'], + ['๐', 'oncoming fist'], + ], + 'nature' => [ + ['โ๏ธ', 'sun'], + ['โ ', 'sun behind cloud'], + ['๐ง๏ธ', 'cloud with rain'], + ['๐จ๏ธ', 'cloud with snow'], + ['โ๏ธ', 'snowflake'], + ['๐ฉ๏ธ', 'cloud with lightning'], + ['๐ช๏ธ', 'tornado'], + ['๐', 'rainbow'], + ['๐ฅ', 'fire'], + ['๐ง', 'droplet'], + ['๐', 'water wave'], + ['๐ซ๏ธ', 'fog'], + ['๐ฌ๏ธ', 'wind face'], + ['๐', 'fallen leaf'], + ['๐ต', 'cactus'], + ['๐ด', 'palm tree'], + ['๐ธ', 'cherry blossom'], + ], + 'animals' => [ + ['๐ถ', 'dog face'], + ['๐ฑ', 'cat face'], + ['๐ญ', 'mouse face'], + ['๐น', 'hamster face'], + ['๐ฐ', 'rabbit face'], + ['๐ฆ', 'fox face'], + ['๐ป', 'bear face'], + ['๐ผ', 'panda face'], + ['๐จ', 'koala'], + ['๐ฏ', 'tiger face'], + ['๐ฆ', 'lion face'], + ['๐ฎ', 'cow face'], + ['๐ท', 'pig face'], + ['๐ธ', 'frog face'], + ['๐ต', 'monkey face'], + ['๐', 'chicken'], + ['๐ง', 'penguin'], + ['๐ฆ', 'bird'], + ['๐ฃ', 'hatching chick'], + ['๐บ', 'wolf face'], + ['๐ฆ', 'unicorn face'], + ], + 'hearts' => [ + ['โค๏ธ', 'red heart'], + ['๐งก', 'orange heart'], + ['๐', 'yellow heart'], + ['๐', 'green heart'], + ['๐', 'blue heart'], + ['๐', 'purple heart'], + ['๐ค', 'black heart'], + ['๐ค', 'white heart'], + ['๐ค', 'brown heart'], + ['๐', 'sparkling heart'], + ['๐', 'growing heart'], + ['๐', 'beating heart'], + ['๐', 'revolving hearts'], + ['๐', 'two hearts'], + ['๐', 'heart with arrow'], + ['๐', 'heart with ribbon'], + ['๐', 'broken heart'], + ['โฃ๏ธ', 'heart exclamation'], + ], + 'activities' => [ + ['๐ด', 'person biking'], + ['๐ต', 'person mountain biking'], + ['๐', 'person running'], + ['๐๏ธ', 'person lifting weights'], + ['๐', 'person swimming'], + ['๐', 'person surfing'], + ['๐ฃ', 'person rowing boat'], + ['๐คธ', 'person cartwheeling'], + ['๐ง', 'person in lotus position'], + ['๐ง', 'person climbing'], + ['๐๏ธ', 'camping'], + ['๐ฃ', 'fishing pole'], + ['๐ฟ', 'skis'], + ['๐', 'snowboarder'], + ['๐น', 'skateboard'], + ['๐งบ', 'basket'], + ['๐ฏ', 'bullseye'], + ], + 'hobbies' => [ + ['๐', 'books'], + ['๐', 'open book'], + ['๐ง', 'headphone'], + ['๐ต', 'musical note'], + ['๐ค', 'microphone'], + ['๐ท', 'saxophone'], + ['๐ธ', 'guitar'], + ['๐น', 'musical keyboard'], + ['๐บ', 'trumpet'], + ['๐ป', 'violin'], + ['๐ช', 'banjo'], + ['โ๏ธ', 'writing hand'], + ['๐', 'memo'], + ['๐ท', 'camera'], + ['๐จ', 'artist palette'], + ['๐งต', 'thread'], + ['๐งถ', 'yarn'], + ['๐ชก', 'sewing needle'], + ['๐น', 'video camera'], + ['๐ฌ', 'clapper board'], + ], + 'food' => [ + ['๐', 'red apple'], + ['๐', 'banana'], + ['๐', 'grapes'], + ['๐', 'strawberry'], + ['๐', 'watermelon'], + ['๐', 'pineapple'], + ['๐ฅญ', 'mango'], + ['๐', 'peach'], + ['๐', 'cherries'], + ['๐ ', 'tomato'], + ['๐ฅฆ', 'broccoli'], + ['๐ฅ', 'carrot'], + ['๐ฝ', 'ear of corn'], + ['๐ฅ', 'potato'], + ['๐', 'bread'], + ['๐ฅ', 'croissant'], + ['๐ฅ', 'baguette bread'], + ['๐ง', 'cheese wedge'], + ['๐', 'pizza'], + ['๐', 'hamburger'], + ['๐', 'french fries'], + ['๐ญ', 'hot dog'], + ['๐ฃ', 'sushi'], + ], + 'vibes' => [ + ['๐ค', 'zzz'], + ['๐คฏ', 'exploding head'], + ['๐ฑ', 'face screaming in fear'], + ['๐ฅต', 'hot face'], + ['๐ฅถ', 'cold face'], + ['๐คฌ', 'face with symbols on mouth'], + ['๐คจ', 'face with raised eyebrow'], + ], + 'tech' => [ + ['๐ป', 'laptop'], + ['๐', 'telephone receiver'], + ['๐', 'battery'], + ['๐ฟ', 'optical disk'], + ['๐น๏ธ', 'joystick'], + ['๐', 'magnifying glass tilted left'], + ['๐', 'chart increasing'], + ], + 'travel' => [ + ['โ๏ธ', 'airplane'], + ['๐', 'automobile'], + ['๐', 'taxi'], + ['๐ฒ', 'bicycle'], + ['๐ด', 'kick scooter'], + ['โต', 'sailboat'], + ], + + //'custom' => get_user_emojis($db), + ]; + } +} +?> \ No newline at end of file diff --git a/src/View/Mood/Mood.php b/src/View/Mood/Mood.php new file mode 100644 index 0000000..38c1473 --- /dev/null +++ b/src/View/Mood/Mood.php @@ -0,0 +1,43 @@ +mood; + + ob_start(); + ?> + + $emojis): ?> +
+ + + \ No newline at end of file diff --git a/src/lib/mood.php b/src/lib/mood.php deleted file mode 100644 index 3c3904d..0000000 --- a/src/lib/mood.php +++ /dev/null @@ -1,53 +0,0 @@ -mood = $mood; - $user = $user->save(); - header("Location: $config->basePath"); - exit; -} - -function render_emoji_tabs(): string { - $user = User::load(); - $emoji_groups = get_emojis_with_labels(); - $selected_emoji = $user->mood; - - ob_start(); - ?> - - $emojis): ?> - - - - basePath ?>rss">rss atom - login + login - admin - logout + admin + logout