Change ConfigModel to SettingsModel (#62)

Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/62
Co-authored-by: Greg Sarjeant <greg@subcultureofone.org>
Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
Greg Sarjeant 2025-08-08 19:44:31 +00:00 committed by greg
parent 38c35f9bff
commit 3df38de9fb
38 changed files with 425 additions and 425 deletions

View File

@ -46,10 +46,10 @@ $db = $prerequisites->getDatabase();
if (!(preg_match('/tkr-setup$/', $path))) {
try {
$user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
$config = (new ConfigModel($db))->get();
$settings = (new SettingsModel($db))->get();
$hasUser = $user_count > 0;
$hasUrl = !empty($config->baseUrl) && !empty($config->basePath);
$hasUrl = !empty($settings->baseUrl) && !empty($settings->basePath);
if (!$hasUser || !$hasUrl) {
// Redirect to setup with auto-detected URL
@ -77,7 +77,7 @@ global $app;
$app = [
'db' => $db,
'config' => (new ConfigModel($db))->get(),
'settings' => (new SettingsModel($db))->get(),
'user' => (new UserModel($db))->get(),
];
@ -86,8 +86,8 @@ Session::start();
Session::generateCsrfToken();
// Remove the base path from the URL
if (strpos($path, $app['config']->basePath) === 0) {
$path = substr($path, strlen($app['config']->basePath));
if (strpos($path, $app['settings']->basePath) === 0) {
$path = substr($path, strlen($app['settings']->basePath));
}
// strip the trailing slash from the resulting route
@ -105,7 +105,7 @@ if ($method === 'POST' && $path != 'setup') {
if (!Session::isValid($_POST['csrf_token'])) {
// Invalid session - redirect to /login
Log::info('Attempt to POST with invalid session. Redirecting to login.');
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'login'));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login'));
exit;
}
} else {

View File

@ -13,16 +13,16 @@ class AdminController extends Controller {
$data = $this->getAdminData(true);
// Auto-detect URL and pre-fill if not already configured
if (empty($data['config']->baseUrl) || empty($data['config']->basePath)) {
if (empty($data['settings']->baseUrl) || empty($data['settings']->basePath)) {
$autodetected = Util::getAutodetectedUrl();
$data['autodetectedUrl'] = $autodetected;
// Pre-fill empty values with auto-detected ones
if (empty($data['config']->baseUrl)) {
$data['config']->baseUrl = $autodetected['baseUrl'];
if (empty($data['settings']->baseUrl)) {
$data['settings']->baseUrl = $autodetected['baseUrl'];
}
if (empty($data['config']->basePath)) {
$data['config']->basePath = $autodetected['basePath'];
if (empty($data['settings']->basePath)) {
$data['settings']->basePath = $autodetected['basePath'];
}
}
@ -36,7 +36,7 @@ class AdminController extends Controller {
return [
'user' => $app['user'],
'config' => $app['config'],
'settings' => $app['settings'],
'isSetup' => $isSetup,
];
}
@ -45,7 +45,7 @@ class AdminController extends Controller {
global $app;
if (!Session::isLoggedIn()){
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'login'));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login'));
exit;
}
@ -145,16 +145,16 @@ class AdminController extends Controller {
if (empty($errors)) {
try {
// Update site settings
$app['config']->siteTitle = $siteTitle;
$app['config']->siteDescription = $siteDescription;
$app['config']->baseUrl = $baseUrl;
$app['config']->basePath = $basePath;
$app['config']->itemsPerPage = $itemsPerPage;
$app['config']->strictAccessibility = $strictAccessibility;
$app['config']->logLevel = $logLevel;
$app['settings']->siteTitle = $siteTitle;
$app['settings']->siteDescription = $siteDescription;
$app['settings']->baseUrl = $baseUrl;
$app['settings']->basePath = $basePath;
$app['settings']->itemsPerPage = $itemsPerPage;
$app['settings']->strictAccessibility = $strictAccessibility;
$app['settings']->logLevel = $logLevel;
// Save site settings and reload config from database
$app['config'] = $app['config']->save();
$app['settings'] = $app['settings']->save();
Log::info("Site settings updated");
// Update user profile

View File

@ -8,7 +8,7 @@ class AuthController extends Controller {
$csrf_token = Session::getCsrfToken();
$vars = [
'config' => $app['config'],
'settings' => $app['settings'],
'csrf_token' => $csrf_token,
'error' => $error,
];
@ -34,7 +34,7 @@ class AuthController extends Controller {
try {
Session::newLoginSession($user);
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath));
exit;
} catch (Exception $e) {
Log::error("Failed to create login session for {$username}: " . $e->getMessage());
@ -65,7 +65,7 @@ class AuthController extends Controller {
Log::info("Logout from user " . $_SESSION['username']);
Session::end();
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath));
exit;
}
}

View File

@ -17,9 +17,9 @@ class Controller {
// Add custom CSS filename if needed
global $app;
if ($app['config']->cssId) {
if ($app['settings']->cssId) {
$cssModel = new CssModel($app['db']);
$cssFile = $cssModel->getById($app['config']->cssId);
$cssFile = $cssModel->getById($app['settings']->cssId);
$vars['customCssFilename'] = $cssFile['filename'] ?? null;
} else {
$vars['customCssFilename'] = null;

View File

@ -9,7 +9,7 @@ class CssController extends Controller {
$vars = [
'user' => $app['user'],
'config' => $app['config'],
'settings' => $app['settings'],
'customCss' => $customCss,
];
@ -114,8 +114,8 @@ class CssController extends Controller {
// Set the theme back to default
try {
$app['config']->cssId = null;
$app['config'] = $app['config']->save();
$app['settings']->cssId = null;
$app['settings'] = $app['settings']->save();
Session::setFlashMessage('success', 'Theme ' . $cssFilename . ' deleted.');
} catch (Exception $e) {
Log::error("Failed to update config after deleting theme: " . $e->getMessage());
@ -129,14 +129,14 @@ class CssController extends Controller {
try {
if ($_POST['selectCssFile']){
// Set custom theme
$app['config']->cssId = $_POST['selectCssFile'];
$app['settings']->cssId = $_POST['selectCssFile'];
} else {
// Set default theme
$app['config']->cssId = null;
$app['settings']->cssId = null;
}
// Update the site theme
$app['config'] = $app['config']->save();
$app['settings'] = $app['settings']->save();
Session::setFlashMessage('success', 'Theme applied.');
} catch (Exception $e) {
Log::error("Failed to save theme setting: " . $e->getMessage());

View File

@ -16,7 +16,7 @@ declare(strict_types=1);
}
$vars = [
'config' => $app['config'],
'settings' => $app['settings'],
'emojiList' => $emojiList,
];
@ -39,7 +39,7 @@ declare(strict_types=1);
break;
}
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'admin/emoji'));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'admin/emoji'));
exit;
}

View File

@ -8,8 +8,8 @@ class FeedController extends Controller {
global $app;
try {
$tickModel = new TickModel($app['db'], $app['config']);
$this->ticks = $tickModel->getPage($app['config']->itemsPerPage);
$tickModel = new TickModel($app['db'], $app['settings']);
$this->ticks = $tickModel->getPage($app['settings']->itemsPerPage);
Log::debug("Loaded " . count($this->ticks) . " ticks for feeds");
} catch (Exception $e) {
Log::error("Failed to load ticks for feed: " . $e->getMessage());
@ -21,7 +21,7 @@ class FeedController extends Controller {
public function rss(){
global $app;
$generator = new RssGenerator($app['config'], $this->ticks);
$generator = new RssGenerator($app['settings'], $this->ticks);
Log::debug("Generating RSS feed with " . count($this->ticks) . " ticks");
header('Content-Type: ' . $generator->getContentType());
@ -31,7 +31,7 @@ class FeedController extends Controller {
public function atom(){
global $app;
$generator = new AtomGenerator($app['config'], $this->ticks);
$generator = new AtomGenerator($app['settings'], $this->ticks);
Log::debug("Generating Atom feed with " . count($this->ticks) . " ticks");
header('Content-Type: ' . $generator->getContentType());

View File

@ -15,18 +15,18 @@ class HomeController extends Controller {
Log::debug("Loading home page $page");
$tickModel = new TickModel($app['db'], $app['config']);
$limit = $app['config']->itemsPerPage;
$tickModel = new TickModel($app['db'], $app['settings']);
$limit = $app['settings']->itemsPerPage;
$offset = ($page - 1) * $limit;
$ticks = $tickModel->getPage($limit, $offset);
$view = new TicksView($app['config'], $ticks, $page);
$view = new TicksView($app['settings'], $ticks, $page);
$tickList = $view->getHtml();
Log::info("Home page loaded with " . count($ticks) . " ticks");
return [
'config' => $app['config'],
'settings' => $app['settings'],
'user' => $app['user'],
'tickList' => $tickList,
];
@ -40,7 +40,7 @@ class HomeController extends Controller {
$result = $this->processTick($_POST);
// redirect to the index (will show the latest tick if one was sent)
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath));
exit;
}
@ -63,7 +63,7 @@ class HomeController extends Controller {
}
try {
$tickModel = new TickModel($app['db'], $app['config']);
$tickModel = new TickModel($app['db'], $app['settings']);
$tickModel->insert($tickContent);
Log::info("New tick created: " . substr($tickContent, 0, 50) . (strlen($tickContent) > 50 ? '...' : ''));
$result['success'] = true;

View File

@ -13,7 +13,7 @@ class LogController extends Controller {
// Ensure user is logged in
if (!Session::isLoggedIn()) {
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'login'));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login'));
exit;
}
@ -49,7 +49,7 @@ class LogController extends Controller {
}
return [
'config' => $app['config'],
'settings' => $app['settings'],
'logEntries' => $logEntries,
'availableRoutes' => $availableRoutes,
'availableLevels' => $availableLevels,

View File

@ -10,7 +10,7 @@ declare(strict_types=1);
$moodPicker = $view->renderMoodPicker(self::getEmojisWithLabels(), $app['user']->mood);
$vars = [
'config' => $app['config'],
'settings' => $app['settings'],
'moodPicker' => $moodPicker,
];
@ -41,7 +41,7 @@ declare(strict_types=1);
}
// go back to the index and show the updated mood
header('Location: ' . Util::buildRelativeUrl($app['config']->basePath));
header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath));
exit;
}
}

View File

@ -8,7 +8,7 @@ class TickController extends Controller{
Log::debug("Fetching tick with ID: {$id}");
try {
$tickModel = new TickModel($app['db'], $app['config']);
$tickModel = new TickModel($app['db'], $app['settings']);
$vars = $tickModel->get($id);
if (empty($vars) || !isset($vars['tick'])) {

View File

@ -15,10 +15,10 @@ class AtomGenerator extends FeedGenerator {
}
private function buildFeed(): string {
Log::debug("Building Atom feed for " . $this->config->siteTitle);
$feedTitle = Util::escape_xml($this->config->siteTitle . " Atom Feed");
$siteUrl = Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath));
$feedUrl = Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath, 'feed/atom'));
Log::debug("Building Atom feed for " . $this->settings->siteTitle);
$feedTitle = Util::escape_xml($this->settings->siteTitle . " Atom Feed");
$siteUrl = Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath));
$feedUrl = Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath, 'feed/atom'));
$updated = date(DATE_ATOM, strtotime($this->ticks[0]['timestamp'] ?? 'now'));
ob_start();
@ -33,7 +33,7 @@ class AtomGenerator extends FeedGenerator {
<updated><?php echo $updated ?></updated>
<id><?php echo $siteUrl ?></id>
<author>
<name><?= Util::escape_xml($this->config->siteTitle) ?></name>
<name><?= Util::escape_xml($this->settings->siteTitle) ?></name>
</author>
<?php foreach ($this->ticks as $tick):
// build the tick entry components

View File

@ -5,11 +5,11 @@ declare(strict_types=1);
// Specific feeds (RSS, Atom, etc.) will inherit from this.
// This will wrap the basic generator functionality.
abstract class FeedGenerator {
protected $config;
protected $settings;
protected $ticks;
public function __construct(ConfigModel $config, array $ticks) {
$this->config = $config;
public function __construct(SettingsModel $settings, array $ticks) {
$this->settings = $settings;
$this->ticks = $ticks;
}
@ -17,10 +17,10 @@ abstract class FeedGenerator {
abstract public function getContentType(): string;
protected function buildTickUrl(int $tickId): string {
return Util::buildUrl($this->config->baseUrl, $this->config->basePath, "tick/{$tickId}");
return Util::buildUrl($this->settings->baseUrl, $this->settings->basePath, "tick/{$tickId}");
}
protected function getSiteUrl(): string {
return Util::buildUrl($this->config->baseUrl, $this->config->basePath);
return Util::buildUrl($this->settings->baseUrl, $this->settings->basePath);
}
}

View File

@ -17,16 +17,16 @@ class RssGenerator extends FeedGenerator {
}
private function buildChannel(): string {
Log::debug("Building RSS channel for " . $this->config->siteTitle);
Log::debug("Building RSS channel for " . $this->settings->siteTitle);
ob_start();
?>
<channel>
<title><?php echo Util::escape_xml($this->config->siteTitle . ' RSS Feed') ?></title>
<link><?php echo Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath))?></link>
<atom:link href="<?php echo Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath, 'feed/rss'))?>"
<title><?php echo Util::escape_xml($this->settings->siteTitle . ' RSS Feed') ?></title>
<link><?php echo Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath))?></link>
<atom:link href="<?php echo Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath, 'feed/rss'))?>"
rel="self"
type="application/rss+xml" />
<description><?php echo Util::escape_xml($this->config->siteDescription) ?></description>
<description><?php echo Util::escape_xml($this->settings->siteDescription) ?></description>
<language>en-us</language>
<lastBuildDate><?php echo date(DATE_RSS); ?></lastBuildDate>
<?php foreach ($this->ticks as $tick):

View File

@ -52,7 +52,7 @@ class Log {
private static function write($level, $message) {
global $app;
$logLevel = $app['config']->logLevel ?? self::LEVELS['INFO'];
$logLevel = $app['settings']->logLevel ?? self::LEVELS['INFO'];
// Only log messages if they're at or above the configured log level.
if (self::LEVELS[$level] < $logLevel){

View File

@ -30,7 +30,7 @@ class Util {
global $app;
$escaped_url = rtrim($matches[1], '.,!?;:)]}>');
$clean_url = html_entity_decode($escaped_url, ENT_QUOTES, 'UTF-8');
$tabIndex = $app['config']->strictAccessibility ? ' tabindex="0"' : '';
$tabIndex = $app['settings']->strictAccessibility ? ' tabindex="0"' : '';
return '<a' . $tabIndex . ' href="' . $clean_url . '"' . $link_attrs . '>' . $escaped_url . '</a>';
},

View File

@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
class ConfigModel {
class SettingsModel {
// properties and default values
public string $siteTitle = 'My tkr';
public string $siteDescription = '';

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
class TickModel {
public function __construct(private PDO $db, private ConfigModel $config) {}
public function __construct(private PDO $db, private SettingsModel $settings) {}
public function getPage(int $limit, int $offset = 0): array {
$stmt = $this->db->prepare("SELECT id, timestamp, tick FROM tick ORDER BY timestamp DESC LIMIT ? OFFSET ?");
@ -32,7 +32,7 @@ class TickModel {
return [
'tickTime' => $row['timestamp'],
'tick' => $row['tick'],
'config' => $this->config,
'settings' => $this->settings,
];
}
}

View File

@ -4,15 +4,15 @@ declare(strict_types=1);
class TicksView {
private $html;
public function __construct(ConfigModel $config, array $ticks, int $page){
$this->html = $this->render($config, $ticks, $page);
public function __construct(SettingsModel $settings, array $ticks, int $page){
$this->html = $this->render($settings, $ticks, $page);
}
public function getHtml(): string {
return $this->html;
}
private function render(ConfigModel $config, array $ticks, int $page): string{
private function render(SettingsModel $settings, array $ticks, int $page): string{
ob_start();
?>
@ -23,18 +23,18 @@ class TicksView {
$relativeTime = Util::relative_time($tick['timestamp']);
?>
<li class="tick" tabindex="0">
<time datetime="<?php echo $datetime->format('c') ?>"><?php echo Util::escape_html($relativeTime) ?></time>
🗑️ <time datetime="<?php echo $datetime->format('c') ?>"><?php echo Util::escape_html($relativeTime) ?></time>
<span class="tick-text"><?php echo Util::linkify(Util::escape_html($tick['tick'])) ?></span>
</li>
<?php endforeach; ?>
</ul>
<div class="tick-pagination">
<?php if ($page > 1): ?>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="?page=<?php echo $page - 1 ?>">&laquo; Newer</a>
<?php endif; ?>
<?php if (count($ticks) === $config->itemsPerPage): ?>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
<?php if (count($ticks) === $settings->itemsPerPage): ?>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="?page=<?php echo $page + 1 ?>">Older &raquo;</a>
<?php endif; ?>
</div>

View File

@ -1,5 +1,5 @@
<?php /** @var bool $isLoggedIn */ ?>
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var UserModel $user */ ?>
<?php /** @var string $childTemplateFile */ ?>
<?php /** @var string $customCssFilename */ ?>
@ -7,23 +7,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title><?= $config->siteTitle ?></title>
<title><?= $settings->siteTitle ?></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet"
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/default.css')) ?>">
<?php if (!empty($config->cssId)): ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'css/default.css')) ?>">
<?php if (!empty($settings->cssId)): ?>
<link rel="stylesheet"
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/custom/' . $customCssFilename)) ?>">
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'css/custom/' . $customCssFilename)) ?>">
<?php endif; ?>
<link rel="alternate"
type="application/rss+xml"
title="<?php echo Util::escape_html($config->siteTitle) ?> RSS Feed"
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/rss/">
title="<?php echo Util::escape_html($settings->siteTitle) ?> RSS Feed"
href="<?php echo Util::escape_html($settings->baseUrl . $settings->basePath)?>feed/rss/">
<link rel="alternate"
type="application/atom+xml"
title="<?php echo Util::escape_html($config->siteTitle) ?> Atom Feed"
href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/atom/">
title="<?php echo Util::escape_html($settings->siteTitle) ?> Atom Feed"
href="<?php echo Util::escape_html($settings->baseUrl . $settings->basePath)?>feed/atom/">
</head>
<body>
<?php include TEMPLATES_DIR . '/partials/navbar.php'?>

View File

@ -1,10 +1,10 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var UserModel $user */ ?>
<?php /** @var isSetup bool */ ?>
<h1><?php if ($isSetup): ?>Setup<?php else: ?>Admin<?php endif; ?></h1>
<main>
<form
action="<?php echo Util::buildRelativeUrl($config->basePath, ($isSetup ? 'setup' : 'admin')) ?>"
action="<?php echo Util::buildRelativeUrl($settings->basePath, ($isSetup ? 'setup' : 'admin')) ?>"
method="post">
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
<fieldset>
@ -36,43 +36,43 @@
<input type="text"
id="site_title"
name="site_title"
value="<?= Util::escape_html($config->siteTitle) ?>"
value="<?= Util::escape_html($settings->siteTitle) ?>"
required>
<label for="site_description">Description <span class=required>*</span></label>
<input type="text"
id="site_description"
name="site_description"
value="<?= Util::escape_html($config->siteDescription) ?>">
value="<?= Util::escape_html($settings->siteDescription) ?>">
<label for="base_url">Base URL <span class=required>*</span></label>
<input type="text"
id="base_url"
name="base_url"
value="<?= Util::escape_html($config->baseUrl) ?>"
value="<?= Util::escape_html($settings->baseUrl) ?>"
required>
<label for="base_path">Base path <span class=required>*</span></label>
<input type="text"
id="base_path"
name="base_path"
value="<?= Util::escape_html($config->basePath) ?>"
value="<?= Util::escape_html($settings->basePath) ?>"
required>
<label for="items_per_page">Ticks per page (max 50) <span class=required>*</span></label>
<input type="number"
id="items_per_page"
name="items_per_page"
value="<?= $config->itemsPerPage ?>" min="1" max="50"
value="<?= $settings->itemsPerPage ?>" min="1" max="50"
required>
<label for="strict_accessibility">Strict accessibility</label>
<input type="checkbox"
id="strict_accessibility"
name="strict_accessibility"
value="1"
<?php if ($config->strictAccessibility): ?> checked <?php endif; ?>>
<?php if ($settings->strictAccessibility): ?> checked <?php endif; ?>>
<label for="strict_accessibility">Log Level</label>
<select id="log_level" name="log_level">
<option value="1" <?= ($config->logLevel ?? 2) == 1 ? 'selected' : '' ?>>DEBUG</option>
<option value="2" <?= ($config->logLevel ?? 2) == 2 ? 'selected' : '' ?>>INFO</option>
<option value="3" <?= ($config->logLevel ?? 2) == 3 ? 'selected' : '' ?>>WARNING</option>
<option value="4" <?= ($config->logLevel ?? 2) == 4 ? 'selected' : '' ?>>ERROR</option>
<option value="1" <?= ($settings->logLevel ?? 2) == 1 ? 'selected' : '' ?>>DEBUG</option>
<option value="2" <?= ($settings->logLevel ?? 2) == 2 ? 'selected' : '' ?>>INFO</option>
<option value="3" <?= ($settings->logLevel ?? 2) == 3 ? 'selected' : '' ?>>WARNING</option>
<option value="4" <?= ($settings->logLevel ?? 2) == 4 ? 'selected' : '' ?>>ERROR</option>
</select>
</div>
</fieldset>

View File

@ -1,18 +1,18 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var Array $customCss */ ?>
<h1>CSS Management</h1>
<main>
<form action="<?= Util::buildRelativeUrl($config->basePath, 'admin/css') ?>" method="post" enctype="multipart/form-data">
<form action="<?= Util::buildRelativeUrl($settings->basePath, 'admin/css') ?>" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
<fieldset>
<legend>Manage</legend>
<div class="fieldset-items">
<label for="selectCssFile">Select CSS File</label>
<select id="selectCssFile" name="selectCssFile">
<option value="" <?php if(!$config->cssId): ?>selected<?php endif; ?>>Default</option>
<option value="" <?php if(!$settings->cssId): ?>selected<?php endif; ?>>Default</option>
<?php foreach ($customCss as $cssFile): ?>
<?php
if ((int) $cssFile['id'] == $config->cssId){
if ((int) $cssFile['id'] == $settings->cssId){
$cssDescription = $cssFile['description'];
$selected = "selected";
}

View File

@ -1,8 +1,8 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var array $emojiList */ ?>
<h1>Emoji Management</h1>
<main>
<form action="<?= Util::buildRelativeUrl($config->basePath, 'admin/emoji') ?>" method="post" enctype="multipart/form-data">
<form action="<?= Util::buildRelativeUrl($settings->basePath, 'admin/emoji') ?>" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
<fieldset>
<legend>Add Emoji</legend>
@ -24,7 +24,7 @@
</fieldset>
</form>
<?php if (!empty($emojiList)): ?>
<form action="<?= Util::buildRelativeUrl($config->basePath, 'admin/emoji') ?>" method="post" enctype="multipart/form-data">
<form action="<?= Util::buildRelativeUrl($settings->basePath, 'admin/emoji') ?>" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>">
<fieldset class="delete-emoji-fieldset">
<legend>Delete Emoji</legend>

View File

@ -1,5 +1,5 @@
<?php /** @var bool $isLoggedIn */ ?>
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var UserModel $user */ ?>
<?php /** @var string $tickList */ ?>
<div class="home-container">
@ -13,8 +13,8 @@
</span>
<?php if (Session::isLoggedIn()): ?>
<a
<?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'mood')) ?>"
<?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'mood')) ?>"
class="change-mood">Change mood</a>
<?php endif ?>
</dd>
@ -48,7 +48,7 @@
<?php endif; ?>
</aside>
<main id="ticks">
<h1 class="site-description"><?= Util::escape_html($config->siteDescription) ?></h1>
<h1 class="site-description"><?= Util::escape_html($settings->siteDescription) ?></h1>
<?php echo $tickList ?>
</main>
</div>

View File

@ -1,8 +1,8 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var string $csrf_token */ ?>
<?php /** @var string $error */ ?>
<h2>Login</h2>
<form method="post" action="<?= Util::buildRelativeUrl($config->basePath, 'login') ?>">
<form method="post" action="<?= Util::buildRelativeUrl($settings->basePath, 'login') ?>">
<div class="fieldset-items">
<input type="hidden" name="csrf_token" value="<?= Util::escape_html($csrf_token) ?>">
<label for="username">Username:</label>

View File

@ -1,4 +1,4 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /** @var array $logEntries */ ?>
<?php /** @var array $availableRoutes */ ?>
<?php /** @var array $availableLevels */ ?>
@ -8,7 +8,7 @@
<main>
<!-- Filters -->
<div class="log-filters">
<form method="get" action="<?= Util::buildRelativeUrl($config->basePath, 'admin/logs') ?>">
<form method="get" action="<?= Util::buildRelativeUrl($settings->basePath, 'admin/logs') ?>">
<fieldset>
<legend>Filter Logs</legend>
<div class="fieldset-items">
@ -35,7 +35,7 @@
</select>
<div></div><button type="submit">Filter</button>
<div></div><a href="<?= Util::buildRelativeUrl($config->basePath, 'admin/logs') ?>">Clear</a>
<div></div><a href="<?= Util::buildRelativeUrl($settings->basePath, 'admin/logs') ?>">Clear</a>
</div>
</fieldset>
</form>

View File

@ -1,35 +1,35 @@
<?php /** @var ConfigModel $config */ ?>
<?php /** @var SettingsModel $settings */ ?>
<?php /* https://www.w3schools.com/howto/howto_css_dropdown.asp */ ?>
<nav aria-label="Main navigation">
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath)) ?>">home</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath)) ?>">home</a>
<details>
<summary aria-haspopup="true">feeds</summary>
<div class="dropdown-items">
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'feed/rss')) ?>">rss</a>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'feed/atom')) ?>">atom</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'feed/rss')) ?>">rss</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'feed/atom')) ?>">atom</a>
</div>
</details>
<?php if (!Session::isLoggedIn()): ?>
<a tabindex="0"
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'login')) ?>">login</a>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'login')) ?>">login</a>
<?php else: ?>
<details>
<summary aria-haspopup="true">admin</summary>
<div class="dropdown-items">
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin')) ?>">settings</a>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/css')) ?>">css</a>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/emoji')) ?>">emoji</a>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/logs')) ?>">logs</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin')) ?>">settings</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/css')) ?>">css</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/emoji')) ?>">emoji</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/logs')) ?>">logs</a>
</div>
</details>
<a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'logout')) ?>">logout</a>
<a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'logout')) ?>">logout</a>
<?php endif; ?>
</nav>

View File

@ -7,7 +7,7 @@ use PHPUnit\Framework\TestCase;
class AdminControllerTest extends TestCase
{
private PDO $mockPdo;
private ConfigModel $config;
private SettingsModel $settings;
private UserModel $user;
protected function setUp(): void
@ -16,12 +16,12 @@ class AdminControllerTest extends TestCase
$this->mockPdo = $this->createMock(PDO::class);
// Create real config and user objects with mocked PDO
$this->config = new ConfigModel($this->mockPdo);
$this->config->siteTitle = 'Test Site';
$this->config->siteDescription = 'Test Description';
$this->config->baseUrl = 'https://example.com';
$this->config->basePath = '/tkr';
$this->config->itemsPerPage = 10;
$this->settings = new SettingsModel($this->mockPdo);
$this->settings->siteTitle = 'Test Site';
$this->settings->siteDescription = 'Test Description';
$this->settings->baseUrl = 'https://example.com';
$this->settings->basePath = '/tkr';
$this->settings->itemsPerPage = 10;
$this->user = new UserModel($this->mockPdo);
$this->user->username = 'testuser';
@ -32,7 +32,7 @@ class AdminControllerTest extends TestCase
global $app;
$app = [
'db' => $this->mockPdo,
'config' => $this->config,
'settings' => $this->settings,
'user' => $this->user,
];
}
@ -43,12 +43,12 @@ class AdminControllerTest extends TestCase
$data = $controller->getAdminData(false);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
// Should be the injected instances
$this->assertSame($this->config, $data['config']);
$this->assertSame($this->settings, $data['settings']);
$this->assertSame($this->user, $data['user']);
$this->assertFalse($data['isSetup']);
}
@ -59,12 +59,12 @@ class AdminControllerTest extends TestCase
$data = $controller->getAdminData(true);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
// Should be the injected instances
$this->assertSame($this->config, $data['config']);
$this->assertSame($this->settings, $data['settings']);
$this->assertSame($this->user, $data['user']);
$this->assertTrue($data['isSetup']);
}
@ -133,12 +133,12 @@ class AdminControllerTest extends TestCase
$this->mockPdo->method('query')->willReturn($mockStatement);
// Create models with mocked PDO
$config = new ConfigModel($this->mockPdo);
$settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['settings'] = $settings;
$app['user'] = $user;
$controller = new AdminController();
@ -195,12 +195,12 @@ class AdminControllerTest extends TestCase
$this->mockPdo->method('query')->willReturn($mockStatement);
// Create models with mocked PDO
$config = new ConfigModel($this->mockPdo);
$settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['settings'] = $settings;
$app['user'] = $user;
$controller = new AdminController();
@ -228,12 +228,12 @@ class AdminControllerTest extends TestCase
$this->mockPdo->method('query')
->willThrowException(new PDOException("Database error"));
$config = new ConfigModel($this->mockPdo);
$settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['settings'] = $settings;
$app['user'] = $user;
$controller = new AdminController();

View File

@ -7,7 +7,7 @@ class FeedControllerTest extends TestCase
{
private PDO $mockPdo;
private PDOStatement $mockStatement;
private ConfigModel $mockConfig;
private SettingsModel $mockConfig;
private UserModel $mockUser;
private string $tempLogDir;
@ -23,7 +23,7 @@ class FeedControllerTest extends TestCase
$this->mockPdo = $this->createMock(PDO::class);
// Mock config with feed-relevant properties
$this->mockConfig = new ConfigModel($this->mockPdo);
$this->mockConfig = new SettingsModel($this->mockPdo);
$this->mockConfig->itemsPerPage = 10;
$this->mockConfig->basePath = '/tkr';
$this->mockConfig->siteTitle = 'Test Site';
@ -38,7 +38,7 @@ class FeedControllerTest extends TestCase
global $app;
$app = [
'db' => $this->mockPdo,
'config' => $this->mockConfig,
'settings' => $this->mockConfig,
'user' => $this->mockUser,
];

View File

@ -7,7 +7,7 @@ class HomeControllerTest extends TestCase
{
private PDO $mockPdo;
private PDOStatement $mockStatement;
private ConfigModel $mockConfig;
private SettingsModel $mockConfig;
private UserModel $mockUser;
protected function setUp(): void
@ -20,7 +20,7 @@ class HomeControllerTest extends TestCase
$this->mockPdo = $this->createMock(PDO::class);
// Mock config
$this->mockConfig = new ConfigModel($this->mockPdo);
$this->mockConfig = new SettingsModel($this->mockPdo);
$this->mockConfig->itemsPerPage = 10;
$this->mockConfig->basePath = '/tkr';
@ -33,7 +33,7 @@ class HomeControllerTest extends TestCase
global $app;
$app = [
'db' => $this->mockPdo,
'config' => $this->mockConfig,
'settings' => $this->mockConfig,
'user' => $this->mockUser,
];
}
@ -77,12 +77,12 @@ class HomeControllerTest extends TestCase
$data = $controller->getHomeData(1);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('tickList', $data);
// Config and user should be the injected instances
$this->assertSame($this->mockConfig, $data['config']);
$this->assertSame($this->mockConfig, $data['settings']);
$this->assertSame($this->mockUser, $data['user']);
// Should have tick list HTML (even if empty)
@ -104,7 +104,7 @@ class HomeControllerTest extends TestCase
$data = $controller->getHomeData(1);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('tickList', $data);

View File

@ -23,7 +23,7 @@ class LogControllerTest extends TestCase
// Set up global $app for simplified dependency access
$mockPdo = $this->createMock(PDO::class);
$mockConfig = new ConfigModel($mockPdo);
$mockConfig = new SettingsModel($mockPdo);
$mockConfig->baseUrl = 'https://example.com';
$mockConfig->basePath = '/tkr/';
@ -32,7 +32,7 @@ class LogControllerTest extends TestCase
global $app;
$app = [
'db' => $mockPdo,
'config' => $mockConfig,
'settings' => $mockConfig,
'user' => $mockUser,
];
}

View File

@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase;
class TickControllerTest extends TestCase
{
private $mockPdo;
private $config;
private $settings;
private $user;
protected function setUp(): void
@ -17,10 +17,10 @@ class TickControllerTest extends TestCase
// Set up mocks
$this->mockPdo = $this->createMock(PDO::class);
$this->config = new ConfigModel($this->mockPdo);
$this->config->baseUrl = 'https://example.com';
$this->config->basePath = '/tkr/';
$this->config->itemsPerPage = 10;
$this->settings = new SettingsModel($this->mockPdo);
$this->settings->baseUrl = 'https://example.com';
$this->settings->basePath = '/tkr/';
$this->settings->itemsPerPage = 10;
$this->user = new UserModel($this->mockPdo);
@ -28,7 +28,7 @@ class TickControllerTest extends TestCase
global $app;
$app = [
'db' => $this->mockPdo,
'config' => $this->config,
'settings' => $this->settings,
'user' => $this->user,
];
}

View File

@ -7,12 +7,12 @@ class AtomGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->siteDescription = 'Test Description';
$config->baseUrl = 'https://example.com';
$config->basePath = '/tkr/';
return $config;
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->siteDescription = 'Test Description';
$settings->baseUrl = 'https://example.com';
$settings->basePath = '/tkr/';
return $settings;
}
private function createSampleTicks() {
@ -23,10 +23,10 @@ class AtomGeneratorTest extends TestCase
}
public function testCanGenerateValidAtom() {
$config = $this->createMockConfig();
$settings = $this->createMockConfig();
$ticks = $this->createSampleTicks();
$generator = new AtomGenerator($config, $ticks);
$generator = new AtomGenerator($settings, $ticks);
$xml = $generator->generate();
// Test XML structure
@ -58,8 +58,8 @@ class AtomGeneratorTest extends TestCase
}
public function testCanHandleEmptyTickList() {
$config = $this->createMockConfig();
$generator = new AtomGenerator($config, []);
$settings = $this->createMockConfig();
$generator = new AtomGenerator($settings, []);
$xml = $generator->generate();
// Should still be valid Atom with no entries
@ -85,7 +85,7 @@ class AtomGeneratorTest extends TestCase
}
public function testCanHandleSpecialCharactersAndUnicode() {
$config = $this->createMockConfig();
$settings = $this->createMockConfig();
// Test various challenging characters
$ticks = [
@ -111,7 +111,7 @@ class AtomGeneratorTest extends TestCase
]
];
$generator = new AtomGenerator($config, $ticks);
$generator = new AtomGenerator($settings, $ticks);
$xml = $generator->generate();
// Test that emojis are preserved

View File

@ -7,12 +7,12 @@ class FeedGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->siteDescription = 'Test Description';
$config->baseUrl = 'https://example.com';
$config->basePath = '/tkr/';
return $config;
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->siteDescription = 'Test Description';
$settings->baseUrl = 'https://example.com';
$settings->basePath = '/tkr/';
return $settings;
}
private function createSampleTicks() {
@ -22,11 +22,11 @@ class FeedGeneratorTest extends TestCase
];
}
private function createTestGenerator($config = null, $ticks = null) {
$config = $config ?? $this->createMockConfig();
private function createTestGenerator($settings = null, $ticks = null) {
$settings = $settings ?? $this->createMockConfig();
$ticks = $ticks ?? $this->createSampleTicks();
return new class($config, $ticks) extends FeedGenerator {
return new class($settings, $ticks) extends FeedGenerator {
public function generate(): string {
return '<test>content</test>';
}
@ -69,12 +69,12 @@ class FeedGeneratorTest extends TestCase
public function testUrlMethodsHandleSubdomainConfiguration() {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->baseUrl = 'https://tkr.example.com';
$config->basePath = '/';
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->baseUrl = 'https://tkr.example.com';
$settings->basePath = '/';
$generator = $this->createTestGenerator($config, []);
$generator = $this->createTestGenerator($settings, []);
$this->assertEquals('https://tkr.example.com/', $generator->testGetSiteUrl());
$this->assertEquals('https://tkr.example.com/tick/456', $generator->testBuildTickUrl(456));
@ -82,12 +82,12 @@ class FeedGeneratorTest extends TestCase
public function testUrlMethodsHandleEmptyBasePath() {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->baseUrl = 'https://example.com';
$config->basePath = '';
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->baseUrl = 'https://example.com';
$settings->basePath = '';
$generator = $this->createTestGenerator($config, []);
$generator = $this->createTestGenerator($settings, []);
$this->assertEquals('https://example.com/', $generator->testGetSiteUrl());
$this->assertEquals('https://example.com/tick/789', $generator->testBuildTickUrl(789));
@ -106,12 +106,12 @@ class FeedGeneratorTest extends TestCase
foreach ($testCases as [$basePath, $expectedSiteUrl, $expectedTickUrl]) {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->baseUrl = 'https://example.com';
$config->basePath = $basePath;
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->baseUrl = 'https://example.com';
$settings->basePath = $basePath;
$generator = $this->createTestGenerator($config, []);
$generator = $this->createTestGenerator($settings, []);
$this->assertEquals($expectedSiteUrl, $generator->testGetSiteUrl(), "Failed for basePath: '$basePath'");
$this->assertEquals($expectedTickUrl, $generator->testBuildTickUrl(123), "Failed for basePath: '$basePath'");

View File

@ -7,12 +7,12 @@ class RssGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
$config = new ConfigModel($mockPdo);
$config->siteTitle = 'Test Site';
$config->siteDescription = 'Test Description';
$config->baseUrl = 'https://example.com';
$config->basePath = '/tkr/';
return $config;
$settings = new SettingsModel($mockPdo);
$settings->siteTitle = 'Test Site';
$settings->siteDescription = 'Test Description';
$settings->baseUrl = 'https://example.com';
$settings->basePath = '/tkr/';
return $settings;
}
private function createSampleTicks() {
@ -23,10 +23,10 @@ class RssGeneratorTest extends TestCase
}
public function testCanGenerateValidRss() {
$config = $this->createMockConfig();
$settings = $this->createMockConfig();
$ticks = $this->createSampleTicks();
$generator = new RssGenerator($config, $ticks);
$generator = new RssGenerator($settings, $ticks);
$xml = $generator->generate();
// Test XML structure
@ -56,8 +56,8 @@ class RssGeneratorTest extends TestCase
}
public function testCanHandleEmptyTickList() {
$config = $this->createMockConfig();
$generator = new RssGenerator($config, []);
$settings = $this->createMockConfig();
$generator = new RssGenerator($settings, []);
$xml = $generator->generate();
// Should still be valid RSS with no items
@ -81,7 +81,7 @@ class RssGeneratorTest extends TestCase
}
public function testCanHandleSpecialCharactersAndUnicode() {
$config = $this->createMockConfig();
$settings = $this->createMockConfig();
// Test various challenging characters
$ticks = [
@ -107,7 +107,7 @@ class RssGeneratorTest extends TestCase
]
];
$generator = new RssGenerator($config, $ticks);
$generator = new RssGenerator($settings, $ticks);
$xml = $generator->generate();
// Test that emojis are preserved

View File

@ -47,7 +47,7 @@ class LogTest extends TestCase
private function setLogLevel(int $level): void
{
global $app;
$app = ['config' => (object)['logLevel' => $level]];
$app = ['settings' => (object)['logLevel' => $level]];
}
private function assertLogContains(string $message): void
@ -188,7 +188,7 @@ class LogTest extends TestCase
{
// Set up config without logLevel property (simulates missing config value)
global $app;
$app = ['config' => (object)[]];
$app = ['settings' => (object)[]];
// Should not throw errors and should default to INFO level
Log::debug('Debug message'); // Should be filtered out (default INFO level = 2)

View File

@ -151,7 +151,7 @@ final class UtilTest extends TestCase
// Set up global $app with config
global $app;
$app = [
'config' => (object)['strictAccessibility' => $strictAccessibility]
'settings' => (object)['strictAccessibility' => $strictAccessibility]
];
$result = Util::linkify($input);
@ -162,7 +162,7 @@ final class UtilTest extends TestCase
// Test linkify without new window
global $app;
$app = [
'config' => (object)['strictAccessibility' => false]
'settings' => (object)['strictAccessibility' => false]
];
$input = 'Visit https://example.com';

View File

@ -175,11 +175,11 @@ try {
echo "💾 Saving configuration...\n";
// Create/update settings
$configModel = new ConfigModel($db);
$configModel->siteTitle = $siteTitle;
$configModel->baseUrl = $baseUrl;
$configModel->basePath = $basePath;
$config = $configModel->save();
$settingsModel = new SettingsModel($db);
$settingsModel->siteTitle = $siteTitle;
$settingsModel->baseUrl = $baseUrl;
$settingsModel->basePath = $basePath;
$settings = $settingsModel->save();
// Create admin user
$userModel = new UserModel($db);