Naming fixes. Add partials. Get single ticks working with clean urls.

This commit is contained in:
Greg Sarjeant 2025-06-03 21:22:20 -04:00
parent 97e2c205a3
commit 78ebb67fc0
22 changed files with 119 additions and 99 deletions

View File

@ -1,4 +1,16 @@
<?php
// Store and validate request data
$method = $_SERVER['REQUEST_METHOD'];
$request = $_SERVER['REQUEST_URI'];
$path = parse_url($request, PHP_URL_PATH);
// return a 404 if a request for a .php file gets this far.
if (preg_match('/\.php$/', $path)) {
http_response_code(404);
echo '<h1>404 Not Found</h1>';
exit;
}
// Define all the important paths
define('APP_ROOT', dirname(dirname(__FILE__)));
define('SRC_DIR', APP_ROOT . '/src');
@ -34,18 +46,6 @@ Session::start();
Session::generateCsrfToken();
$config = Config::load();
// Get request data
$method = $_SERVER['REQUEST_METHOD'];
$request = $_SERVER['REQUEST_URI'];
$path = parse_url($request, PHP_URL_PATH);
// return a 404 if a request for a .php file gets this far.
if (preg_match('/\.php$/', $path)) {
http_response_code(404);
echo '<h1>404 Not Found</h1>';
exit;
}
// Remove the base path from the URL
// and strip the trailing slash from the resulting route
if (strpos($path, $config->basePath) === 0) {
@ -100,6 +100,7 @@ $routeHandlers = [
['mood', 'MoodController@handleMood', ['POST']],
['feed/rss', 'FeedController@rss'],
['feed/atom', 'FeedController@atom'],
['tick/{y}/{m}/{d}/{h}/{i}/{s}', 'TickController'],
];
// Set content type

View File

@ -18,8 +18,8 @@ class AdminController extends Controller {
// POST handler
// save updated settings
public function handleSave(){
$isLoggedIn = isset($_SESSION['user_id']);
if (!$isLoggedIn){
//$isLoggedIn = isset($_SESSION['user_id']);
if (!Session::isLoggedIn()){
header('Location: ' . $config->basePath . 'login.php');
exit;
}

View File

@ -6,7 +6,6 @@ class HomeController extends Controller {
// 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();
@ -18,7 +17,6 @@ class HomeController extends Controller {
$tickList = $view->renderTicksSection($config->siteDescription, $ticks, $page, $limit);
$vars = [
'isLoggedIn' => $isLoggedIn,
'config' => $config,
'user' => $user,
'tickList' => $tickList,

View File

@ -0,0 +1,11 @@
<?php
class TickController extends Controller{
// every tick is identified by its timestamp
public function index(string $year, string $month, string $day, string $hour, string $minute, string $second){
$model = new Tick();
$tick = $model->get($year, $month, $day, $hour, $minute, $second);
$this->render('tick.php', $tick);
}
}

View File

@ -24,6 +24,10 @@ class Session {
return $_SESSION['csrf_token'];
}
public static function isLoggedIn(): bool {
return isset($_SESSION['user_id']);
}
public static function end(): void {
$_SESSION = [];
session_destroy();

View File

@ -104,6 +104,17 @@ class Util {
};
}
public static function tick_time_to_tick_path($tickTime){
[$date, $time] = explode(' ', $tickTime);
$dateParts = explode('-', $date);
$timeParts = explode(':', $time);
[$year, $month, $day] = $dateParts;
[$hour, $minute, $second] = $timeParts;
return "$year/$month/$day/$hour/$minute/$second";
}
// TODO: Move to model base class?
public static function get_db(): PDO {
Util::verify_data_dir(DATA_DIR, true);

View File

@ -1,6 +1,7 @@
<?php
class Tick {
// Everything in this class just reads from and writes to the filesystem
// It doesn't maintain state, so everything's just a static function
public static function streamTicks(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
@ -74,4 +75,29 @@ class Tick {
$content = $time . "|" . $tick . "\n";
file_put_contents($filename, $content, FILE_APPEND);
}
public static function get(string $y, string $m, string $d, string $H, string $i, string $s): array{
$tickTime = new DateTime("$y-$m-$d $H:$i:$s");
$timestamp = "$H:$i:$s";
$file = TICKS_DIR . "/$y/$m/$d.txt";
if (!file_exists($file)) {
http_response_code(404);
echo "Tick not found: $file.";
exit;
}
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (str_starts_with($line, $timestamp)) {
$tick = Util::escape_and_linkify(explode('|', $line)[1]);
return [
'tickTime' => $tickTime,
'tick' => $tick,
'config' => Config::load(),
];
}
}
}
}

View File

@ -1,17 +1,14 @@
<?php /** @var Config $config */ ?>
<?php /** @var User $user */ ?>
<!DOCTYPE html>
<html lang="en">
<html>
<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">
<?php include TEMPLATES_DIR . '/partials/head.php'?>
</head>
<body>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
<html lang="en">
<h1>Admin</h1>
<div><a href="<?= $config->basePath ?>">Back to home</a></div>
<div>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">

View File

@ -27,7 +27,7 @@ echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
[$hour, $minute, $second] = $timeParts;
$tickPath = "$year/$month/$day/$hour/$minute/$second";
$tickUrl = htmlspecialchars($basePath . "tick.php?path=" . $tickPath);
$tickUrl = htmlspecialchars($basePath . "tick/$tickPath");
$tickTime = date(DATE_ATOM, strtotime($tick['timestamp']));
$tickText = htmlspecialchars($tick['tick']);
?>

View File

@ -15,7 +15,6 @@ echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
<language>en-us</language>
<lastBuildDate><?php echo date(DATE_RSS); ?></lastBuildDate>
<?php foreach ($ticks as $tick):
// TODO: Make this a util function.
[$date, $time] = explode(' ', $tick['timestamp']);
$dateParts = explode('-', $date);
$timeParts = explode(':', $time);
@ -27,7 +26,7 @@ echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
?>
<item>
<title><?php echo htmlspecialchars($tick['tick']); ?></title>
<link><?php echo htmlspecialchars($config->basePath . "tick.php?path=$tickPath"); ?></link>
<link><?php echo htmlspecialchars($config->basePath . "tick/$tickPath"); ?></link>
<description><?php echo htmlspecialchars($tick['tick']); ?></description>
<pubDate><?php echo date(DATE_RSS, strtotime($tick['timestamp'])); ?></pubDate>
<guid><?php echo htmlspecialchars($tickPath); ?></guid>

View File

@ -2,26 +2,13 @@
<?php /** @var Config $config */ ?>
<?php /** @var User $user */ ?>
<?php /** @var string $tickList */ ?>
<!DOCTYPE html>
<html>
<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?v=<?= time() ?>">
<?php include TEMPLATES_DIR . '/partials/head.php'?>
</head>
<body>
<div class="home-navbar">
<a href="<?= $config->basePath ?>feed/rss">rss</a>
<a href="<?= $config->basePath ?>feed/atom">atom</a>
<?php if (!$isLoggedIn): ?>
<a href="<?= $config->basePath ?>login">login</a>
<?php else: ?>
<a href="<?= $config->basePath ?>admin">admin</a>
<a href="<?= $config->basePath ?>logout">logout</a>
<?php endif; ?>
</div>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
<div class="home-container">
<section id="sidebar" class="home-sidebar">
<div class="home-header">
@ -32,12 +19,12 @@
<div class="profile-row">
<div class="mood-bar">
<span>Current mood: <?= $user->mood ?></span>
<?php if ($isLoggedIn): ?>
<?php if (Session::isLoggedIn()): ?>
<a href="<?= $config->basePath ?>mood">Change</a>
<?php endif; ?>
</div>
</div>
<?php if ($isLoggedIn): ?>
<?php if (Session::isLoggedIn()): ?>
<hr/>
<div class="profile-row">
<form class="tick-form" method="post">

View File

@ -1,16 +1,13 @@
<?php /** @var Config $config */ ?>
<?php /** @var string $csrfToken */ ?>
<?php /** @var string $error */ ?>
<!DOCTYPE html>
<html>
<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?v=<?= time() ?>">
<?php include TEMPLATES_DIR . '/partials/head.php'?>
</head>
<body>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
<h2>Login</h2>
<?php if ($error): ?>
<p style="color:red"><?= htmlspecialchars($error) ?></p>

View File

@ -1,19 +1,13 @@
<?php /** @var Config $config */ ?>
<?php /** @var string $moodPicker */ ?>
<!DOCTYPE html>
<html lang="en">
<html>
<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">
<?php include TEMPLATES_DIR . '/partials/head.php'?>
</head>
<body>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
<h2>How are you feeling?</h2>
<?php echo $moodPicker; ?>
<a class="back-link" href="<?= htmlspecialchars($config->basePath) ?>">Back to home</a>
</body>
</html>

View File

@ -0,0 +1,5 @@
<?php /** @var Config $config */ ?>
<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?v=<?= time() ?>">

View File

@ -0,0 +1,12 @@
<?php /** @var Config $config */ ?>
<div class="navbar">
<a href="<?= $config->basePath ?>">home</a>
<a href="<?= $config->basePath ?>feed/rss">rss</a>
<a href="<?= $config->basePath ?>feed/atom">atom</a>
<?php if (!Session::isLoggedIn()): ?>
<a href="<?= $config->basePath ?>login">login</a>
<?php else: ?>
<a href="<?= $config->basePath ?>admin">admin</a>
<a href="<?= $config->basePath ?>logout">logout</a>
<?php endif; ?>
</div>

View File

@ -1,10 +1,5 @@
<?php
#require_once __DIR__ . '/../bootstrap.php';
#confirm_setup();
// If we got past confirm_setup(), then setup isn't complete.
$db = get_db();
$db = Utli::get_db();
// Handle submitted form
if ($_SERVER['REQUEST_METHOD'] === 'POST') {

View File

@ -1,32 +1,15 @@
<?php
$path = $_GET['path'] ?? '';
$parts = explode('/', $path);
if (count($parts) !== 6) {
http_response_code(400);
echo "Invalid tick path.";
exit;
}
[$y, $m, $d, $H, $i, $s] = $parts;
$timestamp = "$H:$i:$s";
$file = TICKS_DIR . "/$y/$m/$d.txt";
if (!file_exists($file)) {
http_response_code(404);
echo "Tick not found.";
exit;
}
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (str_starts_with($line, $timestamp)) {
echo "<h1>Tick from $timestamp on $y-$m-$d</h1>";
echo "<p>" . escape_and_linkify(explode('|', $line)[1]) . "</p>";
exit;
}
}
http_response_code(404);
echo "Tick not found.";
<?php /** @var Config $config */ ?>
<?php /** @var Date $tickTime */ ?>
<?php /** @var string $tick */ ?>
<!DOCTYPE html>
<html>
<head>
<?php include TEMPLATES_DIR . '/partials/head.php'?>
</head>
<body>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>
<!DOCTYPE html>
<h1>Tick from <?= $tickTime->format('Y-m-d H:i:s'); ?></h1>
<p><?= $tick ?></p>
</body>
</html>