Convert login and mood pages to MVC pattern.
This commit is contained in:
parent
cacbf85283
commit
20129d9fcf
@ -1,5 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['csrf_token'])) {
|
||||||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
define('APP_ROOT', dirname(dirname(__FILE__)));
|
define('APP_ROOT', dirname(dirname(__FILE__)));
|
||||||
|
|
||||||
define('SRC_DIR', APP_ROOT . '/src');
|
define('SRC_DIR', APP_ROOT . '/src');
|
||||||
@ -54,20 +60,28 @@ if (strpos($path, $config->basePath) === 0) {
|
|||||||
|
|
||||||
$path = trim($path, '/');
|
$path = trim($path, '/');
|
||||||
|
|
||||||
function route($pattern, $callback, $methods = ['GET']) {
|
function route(string $pattern, string $controller, array $methods = ['GET']) {
|
||||||
global $path, $method;
|
global $path, $method;
|
||||||
|
|
||||||
if (!in_array($method, $methods)) {
|
if (!in_array($method, $methods)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert route pattern to regex
|
|
||||||
$pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $pattern);
|
$pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $pattern);
|
||||||
$pattern = '#^' . $pattern . '$#';
|
$pattern = '#^' . $pattern . '$#';
|
||||||
|
|
||||||
if (preg_match($pattern, $path, $matches)) {
|
if (preg_match($pattern, $path, $matches)) {
|
||||||
array_shift($matches); // Remove full match
|
array_shift($matches);
|
||||||
call_user_func_array($callback, $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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +92,22 @@ function route($pattern, $callback, $methods = ['GET']) {
|
|||||||
header('Content-Type: text/html; charset=utf-8');
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
|
||||||
// routes
|
// routes
|
||||||
route('', function(){
|
$routes = [
|
||||||
$hc = new HomeController();
|
['', 'HomeController'],
|
||||||
echo $hc->render();
|
['', '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;
|
||||||
|
}
|
||||||
|
};
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
#require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
#confirm_setup();
|
|
||||||
|
|
||||||
#require_once CLASSES_DIR . '/Config.php';
|
|
||||||
#require LIB_DIR . '/session.php';
|
|
||||||
#require LIB_DIR . '/ticks.php';
|
|
||||||
#require LIB_DIR . '/util.php';
|
|
||||||
|
|
||||||
|
|
||||||
// ticks must be sent via POST
|
|
||||||
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();
|
|
||||||
|
|
||||||
// go back to the index (will show the latest tick if one was sent)
|
|
||||||
header('Location: ' . $config->basePath);
|
|
||||||
exit;
|
|
@ -1,5 +1,52 @@
|
|||||||
<?php
|
<?php
|
||||||
class HomeController{
|
class HomeController{
|
||||||
|
// GET handler
|
||||||
|
// renders the homepage view.
|
||||||
|
public function index(){
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
private function stream_ticks(int $limit, int $offset = 0): Generator {
|
||||||
$tick_files = glob(TICKS_DIR . '/*/*/*.txt');
|
$tick_files = glob(TICKS_DIR . '/*/*/*.txt');
|
||||||
usort($tick_files, fn($a, $b) => strcmp($b, $a)); // sort filenames in reverse chronological order
|
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
48
src/Controller/Login/Login.php
Normal file
48
src/Controller/Login/Login.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
class LoginController {
|
||||||
|
function index(?string $error = null){
|
||||||
|
$config = Config::load();
|
||||||
|
$csrf_token = $_SESSION['csrf_token'];
|
||||||
|
|
||||||
|
$vars = [
|
||||||
|
'config' => $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();
|
||||||
|
}
|
||||||
|
}
|
247
src/Controller/Mood/Mood.php
Normal file
247
src/Controller/Mood/Mood.php
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
<?php
|
||||||
|
class MoodController {
|
||||||
|
public function index(){
|
||||||
|
$config = Config::load();
|
||||||
|
$user = User::load();
|
||||||
|
$view = new MoodView();
|
||||||
|
|
||||||
|
$moodPicker = $view->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),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
43
src/View/Mood/Mood.php
Normal file
43
src/View/Mood/Mood.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
class MoodView {
|
||||||
|
private function render_emoji_tabs(array $emojiGroups, string $currentMood): string {
|
||||||
|
$selected_emoji = $currentMood; //user->mood;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php foreach ($emojiGroups as $group => $emojis): ?>
|
||||||
|
<fieldset id="<?= htmlspecialchars($group) ?>" class="emoji-tab-content">
|
||||||
|
<legend><?= ucfirst($group) ?></legend>
|
||||||
|
<?php foreach ($emojis as [$emoji, $description]): ?>
|
||||||
|
<label class="emoji-option">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="mood"
|
||||||
|
value="<?= htmlspecialchars($emoji) ?>"
|
||||||
|
aria-label="<?=htmlspecialchars($description ?? 'emoji') ?>"
|
||||||
|
<?= $emoji === $selected_emoji ? 'checked' : '' ?>
|
||||||
|
>
|
||||||
|
<span><?= htmlspecialchars($emoji) ?></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</fieldset>
|
||||||
|
<?php endforeach;
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_mood_picker(array $emojiGroups, string $currentMood): string {
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<form method="post" class="emoji-picker-form">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
|
<?= $this->render_emoji_tabs($emojiGroups, $currentMood) ?>
|
||||||
|
<button type="submit">Set the mood</button>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function save_mood(string $mood): void {
|
|
||||||
$config = Config::load();
|
|
||||||
$user = User::load();
|
|
||||||
|
|
||||||
$user->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();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php foreach ($emoji_groups as $group => $emojis): ?>
|
|
||||||
<fieldset id="<?= htmlspecialchars($group) ?>" class="emoji-tab-content">
|
|
||||||
<legend><?= ucfirst($group) ?></legend>
|
|
||||||
<?php foreach ($emojis as [$emoji, $desctiption]): ?>
|
|
||||||
<label class="emoji-option">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="mood"
|
|
||||||
value="<?= htmlspecialchars($emoji) ?>"
|
|
||||||
aria-label="<?=htmlspecialchars($desctiption ?? 'emoji') ?>"
|
|
||||||
<?= $emoji === $selected_emoji ? 'checked' : '' ?>
|
|
||||||
>
|
|
||||||
<span><?= htmlspecialchars($emoji) ?></span>
|
|
||||||
</label>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</fieldset>
|
|
||||||
<?php endforeach;
|
|
||||||
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
function render_mood_picker(): string {
|
|
||||||
ob_start();
|
|
||||||
?>
|
|
||||||
<form action="set_mood.php" method="post" class="emoji-picker-form">
|
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
|
||||||
<?= render_emoji_tabs() ?>
|
|
||||||
<button type="submit">Set the mood</button>
|
|
||||||
</form>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
@ -16,10 +16,10 @@
|
|||||||
<a href="<?= $config->basePath ?>rss">rss</a>
|
<a href="<?= $config->basePath ?>rss">rss</a>
|
||||||
<a href="<?= $config->basePath ?>atom">atom</a>
|
<a href="<?= $config->basePath ?>atom">atom</a>
|
||||||
<?php if (!$isLoggedIn): ?>
|
<?php if (!$isLoggedIn): ?>
|
||||||
<a href="<?= $config->basePath ?>login.php">login</a>
|
<a href="<?= $config->basePath ?>login">login</a>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a href="<?= $config->basePath ?>admin.php">admin</a>
|
<a href="<?= $config->basePath ?>admin">admin</a>
|
||||||
<a href="<?= $config->basePath ?>logout.php">logout</a>
|
<a href="<?= $config->basePath ?>logout">logout</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="home-container">
|
<div class="home-container">
|
||||||
@ -33,14 +33,14 @@
|
|||||||
<div class="mood-bar">
|
<div class="mood-bar">
|
||||||
<span>Current mood: <?= $user->mood ?></span>
|
<span>Current mood: <?= $user->mood ?></span>
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
<a href="<?= $config->basePath ?>set_mood.php">Change</a>
|
<a href="<?= $config->basePath ?>mood">Change</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($isLoggedIn): ?>
|
<?php if ($isLoggedIn): ?>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="profile-row">
|
<div class="profile-row">
|
||||||
<form class="tick-form" action="save_tick.php" method="post">
|
<form class="tick-form" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||||
<textarea name="tick" placeholder="What's ticking?" rows="3"></textarea>
|
<textarea name="tick" placeholder="What's ticking?" rows="3"></textarea>
|
||||||
<button type="submit">Tick</button>
|
<button type="submit">Tick</button>
|
||||||
|
@ -1,42 +1,6 @@
|
|||||||
<?php
|
<?php /** @var Config $config */ ?>
|
||||||
#require_once __DIR__ . '/../bootstrap.php';
|
<?php /** @var string $csrfToken */ ?>
|
||||||
|
<?php /** @var string $error */ ?>
|
||||||
#confirm_setup();
|
|
||||||
|
|
||||||
#require_once CLASSES_DIR . '/Config.php';
|
|
||||||
#require LIB_DIR . '/session.php';
|
|
||||||
|
|
||||||
$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();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@ -51,7 +15,7 @@ $csrf_token = generateCsrfToken();
|
|||||||
<?php if ($error): ?>
|
<?php if ($error): ?>
|
||||||
<p style="color:red"><?= htmlspecialchars($error) ?></p>
|
<p style="color:red"><?= htmlspecialchars($error) ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<form method="post" action="<?= $config->basePath ?>login.php">
|
<form method="post" action="<?= $config->basePath ?>login">
|
||||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf_token) ?>">
|
||||||
<label>Username: <input type="text" name="username" required></label><br>
|
<label>Username: <input type="text" name="username" required></label><br>
|
||||||
<label>Password: <input type="password" name="password" required></label><br>
|
<label>Password: <input type="password" name="password" required></label><br>
|
||||||
|
19
templates/mood.php
Normal file
19
templates/mood.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php /** @var Config $config */ ?>
|
||||||
|
<?php /** @var string $moodPicker */ ?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title><?= $config->siteTitle ?></title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="<?= htmlspecialchars($config->basePath) ?>css/tkr.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>How are you feeling?</h2>
|
||||||
|
|
||||||
|
<?php echo $moodPicker; ?>
|
||||||
|
|
||||||
|
<a class="back-link" href="<?= htmlspecialchars($config->basePath) ?>">Back to home</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
#require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
#confirm_setup();
|
|
||||||
|
|
||||||
#require_once CLASSES_DIR . '/Config.php';
|
|
||||||
#require LIB_DIR . '/session.php';
|
|
||||||
#require LIB_DIR . '/mood.php';
|
|
||||||
|
|
||||||
|
|
||||||
// get the config
|
|
||||||
$config = Config::load();
|
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the mood
|
|
||||||
save_mood($_POST['mood']);
|
|
||||||
|
|
||||||
// go back to the index and show the latest tick
|
|
||||||
header('Location: ' . $config->basePath);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title><?= $config->siteTitle ?></title>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" href="<?= htmlspecialchars($config->basePath) ?>css/tkr.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>How are you feeling?</h2>
|
|
||||||
|
|
||||||
<?= render_mood_picker(); ?>
|
|
||||||
|
|
||||||
<a class="back-link" href="<?= htmlspecialchars($config->basePath) ?>">Back to home</a>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,9 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
#require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
#confirm_setup();
|
|
||||||
|
|
||||||
#require LIB_DIR . '/util.php';
|
|
||||||
|
|
||||||
$path = $_GET['path'] ?? '';
|
$path = $_GET['path'] ?? '';
|
||||||
$parts = explode('/', $path);
|
$parts = explode('/', $path);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user