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:
		
							parent
							
								
									38c35f9bff
								
							
						
					
					
						commit
						3df38de9fb
					
				| @ -46,10 +46,10 @@ $db = $prerequisites->getDatabase(); | |||||||
| if (!(preg_match('/tkr-setup$/', $path))) { | if (!(preg_match('/tkr-setup$/', $path))) { | ||||||
|     try { |     try { | ||||||
|         $user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn(); |         $user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn(); | ||||||
|         $config = (new ConfigModel($db))->get(); |         $settings = (new SettingsModel($db))->get(); | ||||||
| 
 | 
 | ||||||
|         $hasUser = $user_count > 0; |         $hasUser = $user_count > 0; | ||||||
|         $hasUrl = !empty($config->baseUrl) && !empty($config->basePath); |         $hasUrl = !empty($settings->baseUrl) && !empty($settings->basePath); | ||||||
| 
 | 
 | ||||||
|         if (!$hasUser || !$hasUrl) { |         if (!$hasUser || !$hasUrl) { | ||||||
|             // Redirect to setup with auto-detected URL
 |             // Redirect to setup with auto-detected URL
 | ||||||
| @ -77,7 +77,7 @@ global $app; | |||||||
| 
 | 
 | ||||||
| $app = [ | $app = [ | ||||||
|     'db' => $db, |     'db' => $db, | ||||||
|     'config' => (new ConfigModel($db))->get(), |     'settings' => (new SettingsModel($db))->get(), | ||||||
|     'user' => (new UserModel($db))->get(), |     'user' => (new UserModel($db))->get(), | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @ -86,8 +86,8 @@ Session::start(); | |||||||
| Session::generateCsrfToken(); | Session::generateCsrfToken(); | ||||||
| 
 | 
 | ||||||
| // Remove the base path from the URL
 | // Remove the base path from the URL
 | ||||||
| if (strpos($path, $app['config']->basePath) === 0) { | if (strpos($path, $app['settings']->basePath) === 0) { | ||||||
|     $path = substr($path, strlen($app['config']->basePath)); |     $path = substr($path, strlen($app['settings']->basePath)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // strip the trailing slash from the resulting route
 | // strip the trailing slash from the resulting route
 | ||||||
| @ -105,7 +105,7 @@ if ($method === 'POST' && $path != 'setup') { | |||||||
|         if (!Session::isValid($_POST['csrf_token'])) { |         if (!Session::isValid($_POST['csrf_token'])) { | ||||||
|             // Invalid session - redirect to /login
 |             // Invalid session - redirect to /login
 | ||||||
|             Log::info('Attempt to POST with invalid session. Redirecting 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; |             exit; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|  | |||||||
| @ -13,16 +13,16 @@ class AdminController extends Controller { | |||||||
|         $data = $this->getAdminData(true); |         $data = $this->getAdminData(true); | ||||||
| 
 | 
 | ||||||
|         // Auto-detect URL and pre-fill if not already configured
 |         // 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(); |             $autodetected = Util::getAutodetectedUrl(); | ||||||
|             $data['autodetectedUrl'] = $autodetected; |             $data['autodetectedUrl'] = $autodetected; | ||||||
| 
 | 
 | ||||||
|             // Pre-fill empty values with auto-detected ones
 |             // Pre-fill empty values with auto-detected ones
 | ||||||
|             if (empty($data['config']->baseUrl)) { |             if (empty($data['settings']->baseUrl)) { | ||||||
|                 $data['config']->baseUrl = $autodetected['baseUrl']; |                 $data['settings']->baseUrl = $autodetected['baseUrl']; | ||||||
|             } |             } | ||||||
|             if (empty($data['config']->basePath)) { |             if (empty($data['settings']->basePath)) { | ||||||
|                 $data['config']->basePath = $autodetected['basePath']; |                 $data['settings']->basePath = $autodetected['basePath']; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -36,7 +36,7 @@ class AdminController extends Controller { | |||||||
| 
 | 
 | ||||||
|         return [ |         return [ | ||||||
|             'user' => $app['user'], |             'user' => $app['user'], | ||||||
|             'config' => $app['config'], |             'settings' => $app['settings'], | ||||||
|             'isSetup' => $isSetup, |             'isSetup' => $isSetup, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| @ -45,7 +45,7 @@ class AdminController extends Controller { | |||||||
|         global $app; |         global $app; | ||||||
| 
 | 
 | ||||||
|         if (!Session::isLoggedIn()){ |         if (!Session::isLoggedIn()){ | ||||||
|             header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'login')); |             header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login')); | ||||||
|             exit; |             exit; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -145,16 +145,16 @@ class AdminController extends Controller { | |||||||
|         if (empty($errors)) { |         if (empty($errors)) { | ||||||
|             try { |             try { | ||||||
|                 // Update site settings
 |                 // Update site settings
 | ||||||
|                 $app['config']->siteTitle = $siteTitle; |                 $app['settings']->siteTitle = $siteTitle; | ||||||
|                 $app['config']->siteDescription = $siteDescription; |                 $app['settings']->siteDescription = $siteDescription; | ||||||
|                 $app['config']->baseUrl = $baseUrl; |                 $app['settings']->baseUrl = $baseUrl; | ||||||
|                 $app['config']->basePath = $basePath; |                 $app['settings']->basePath = $basePath; | ||||||
|                 $app['config']->itemsPerPage = $itemsPerPage; |                 $app['settings']->itemsPerPage = $itemsPerPage; | ||||||
|                 $app['config']->strictAccessibility = $strictAccessibility; |                 $app['settings']->strictAccessibility = $strictAccessibility; | ||||||
|                 $app['config']->logLevel = $logLevel; |                 $app['settings']->logLevel = $logLevel; | ||||||
| 
 | 
 | ||||||
|                 // Save site settings and reload config from database
 |                 // Save site settings and reload config from database
 | ||||||
|                 $app['config'] = $app['config']->save(); |                 $app['settings'] = $app['settings']->save(); | ||||||
|                 Log::info("Site settings updated"); |                 Log::info("Site settings updated"); | ||||||
| 
 | 
 | ||||||
|                 // Update user profile
 |                 // Update user profile
 | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ class AuthController extends Controller { | |||||||
|         $csrf_token = Session::getCsrfToken(); |         $csrf_token = Session::getCsrfToken(); | ||||||
| 
 | 
 | ||||||
|         $vars = [ |         $vars = [ | ||||||
|             'config' => $app['config'], |             'settings' => $app['settings'], | ||||||
|             'csrf_token' => $csrf_token, |             'csrf_token' => $csrf_token, | ||||||
|             'error' => $error, |             'error' => $error, | ||||||
|         ]; |         ]; | ||||||
| @ -34,7 +34,7 @@ class AuthController extends Controller { | |||||||
| 
 | 
 | ||||||
|                     try { |                     try { | ||||||
|                         Session::newLoginSession($user); |                         Session::newLoginSession($user); | ||||||
|                         header('Location: ' . Util::buildRelativeUrl($app['config']->basePath)); |                         header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath)); | ||||||
|                         exit; |                         exit; | ||||||
|                     } catch (Exception $e) { |                     } catch (Exception $e) { | ||||||
|                         Log::error("Failed to create login session for {$username}: " . $e->getMessage()); |                         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']); |         Log::info("Logout from user " . $_SESSION['username']); | ||||||
|         Session::end(); |         Session::end(); | ||||||
| 
 | 
 | ||||||
|         header('Location: ' . Util::buildRelativeUrl($app['config']->basePath)); |         header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath)); | ||||||
|         exit; |         exit; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -17,9 +17,9 @@ class Controller { | |||||||
| 
 | 
 | ||||||
|         // Add custom CSS filename if needed
 |         // Add custom CSS filename if needed
 | ||||||
|         global $app; |         global $app; | ||||||
|         if ($app['config']->cssId) { |         if ($app['settings']->cssId) { | ||||||
|             $cssModel = new CssModel($app['db']); |             $cssModel = new CssModel($app['db']); | ||||||
|             $cssFile = $cssModel->getById($app['config']->cssId); |             $cssFile = $cssModel->getById($app['settings']->cssId); | ||||||
|             $vars['customCssFilename'] = $cssFile['filename'] ?? null; |             $vars['customCssFilename'] = $cssFile['filename'] ?? null; | ||||||
|         } else { |         } else { | ||||||
|             $vars['customCssFilename'] = null; |             $vars['customCssFilename'] = null; | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ class CssController extends Controller { | |||||||
| 
 | 
 | ||||||
|         $vars = [ |         $vars = [ | ||||||
|             'user' => $app['user'], |             'user' => $app['user'], | ||||||
|             'config' => $app['config'], |             'settings' => $app['settings'], | ||||||
|             'customCss' => $customCss, |             'customCss' => $customCss, | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
| @ -114,8 +114,8 @@ class CssController extends Controller { | |||||||
| 
 | 
 | ||||||
|         // Set the theme back to default
 |         // Set the theme back to default
 | ||||||
|         try { |         try { | ||||||
|             $app['config']->cssId = null; |             $app['settings']->cssId = null; | ||||||
|             $app['config'] = $app['config']->save(); |             $app['settings'] = $app['settings']->save(); | ||||||
|             Session::setFlashMessage('success', 'Theme ' . $cssFilename . ' deleted.'); |             Session::setFlashMessage('success', 'Theme ' . $cssFilename . ' deleted.'); | ||||||
|         } catch (Exception $e) { |         } catch (Exception $e) { | ||||||
|             Log::error("Failed to update config after deleting theme: " . $e->getMessage()); |             Log::error("Failed to update config after deleting theme: " . $e->getMessage()); | ||||||
| @ -129,14 +129,14 @@ class CssController extends Controller { | |||||||
|         try { |         try { | ||||||
|             if ($_POST['selectCssFile']){ |             if ($_POST['selectCssFile']){ | ||||||
|                 // Set custom theme
 |                 // Set custom theme
 | ||||||
|                 $app['config']->cssId = $_POST['selectCssFile']; |                 $app['settings']->cssId = $_POST['selectCssFile']; | ||||||
|             } else { |             } else { | ||||||
|                 // Set default theme
 |                 // Set default theme
 | ||||||
|                 $app['config']->cssId = null; |                 $app['settings']->cssId = null; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Update the site theme
 |             // Update the site theme
 | ||||||
|             $app['config'] = $app['config']->save(); |             $app['settings'] = $app['settings']->save(); | ||||||
|             Session::setFlashMessage('success', 'Theme applied.'); |             Session::setFlashMessage('success', 'Theme applied.'); | ||||||
|         } catch (Exception $e) { |         } catch (Exception $e) { | ||||||
|             Log::error("Failed to save theme setting: " . $e->getMessage()); |             Log::error("Failed to save theme setting: " . $e->getMessage()); | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ declare(strict_types=1); | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             $vars = [ |             $vars = [ | ||||||
|                 'config' => $app['config'], |                 'settings' => $app['settings'], | ||||||
|                 'emojiList' => $emojiList, |                 'emojiList' => $emojiList, | ||||||
|             ]; |             ]; | ||||||
| 
 | 
 | ||||||
| @ -39,7 +39,7 @@ declare(strict_types=1); | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'admin/emoji')); |             header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'admin/emoji')); | ||||||
|             exit; |             exit; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,8 +8,8 @@ class FeedController extends Controller { | |||||||
|         global $app; |         global $app; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             $tickModel = new TickModel($app['db'], $app['config']); |             $tickModel = new TickModel($app['db'], $app['settings']); | ||||||
|             $this->ticks = $tickModel->getPage($app['config']->itemsPerPage); |             $this->ticks = $tickModel->getPage($app['settings']->itemsPerPage); | ||||||
|             Log::debug("Loaded " . count($this->ticks) . " ticks for feeds"); |             Log::debug("Loaded " . count($this->ticks) . " ticks for feeds"); | ||||||
|         } catch (Exception $e) { |         } catch (Exception $e) { | ||||||
|             Log::error("Failed to load ticks for feed: " . $e->getMessage()); |             Log::error("Failed to load ticks for feed: " . $e->getMessage()); | ||||||
| @ -21,7 +21,7 @@ class FeedController extends Controller { | |||||||
|     public function rss(){ |     public function rss(){ | ||||||
|         global $app; |         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"); |         Log::debug("Generating RSS feed with " . count($this->ticks) . " ticks"); | ||||||
| 
 | 
 | ||||||
|         header('Content-Type: ' . $generator->getContentType()); |         header('Content-Type: ' . $generator->getContentType()); | ||||||
| @ -31,7 +31,7 @@ class FeedController extends Controller { | |||||||
|     public function atom(){ |     public function atom(){ | ||||||
|         global $app; |         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"); |         Log::debug("Generating Atom feed with " . count($this->ticks) . " ticks"); | ||||||
| 
 | 
 | ||||||
|         header('Content-Type: ' . $generator->getContentType()); |         header('Content-Type: ' . $generator->getContentType()); | ||||||
|  | |||||||
| @ -15,18 +15,18 @@ class HomeController extends Controller { | |||||||
| 
 | 
 | ||||||
|         Log::debug("Loading home page $page"); |         Log::debug("Loading home page $page"); | ||||||
| 
 | 
 | ||||||
|         $tickModel = new TickModel($app['db'], $app['config']); |         $tickModel = new TickModel($app['db'], $app['settings']); | ||||||
|         $limit = $app['config']->itemsPerPage; |         $limit = $app['settings']->itemsPerPage; | ||||||
|         $offset = ($page - 1) * $limit; |         $offset = ($page - 1) * $limit; | ||||||
|         $ticks = $tickModel->getPage($limit, $offset); |         $ticks = $tickModel->getPage($limit, $offset); | ||||||
| 
 | 
 | ||||||
|         $view = new TicksView($app['config'], $ticks, $page); |         $view = new TicksView($app['settings'], $ticks, $page); | ||||||
|         $tickList = $view->getHtml(); |         $tickList = $view->getHtml(); | ||||||
| 
 | 
 | ||||||
|         Log::info("Home page loaded with " . count($ticks) . " ticks"); |         Log::info("Home page loaded with " . count($ticks) . " ticks"); | ||||||
| 
 | 
 | ||||||
|         return [ |         return [ | ||||||
|             'config'     => $app['config'], |             'settings'     => $app['settings'], | ||||||
|             'user'       => $app['user'], |             'user'       => $app['user'], | ||||||
|             'tickList'   => $tickList, |             'tickList'   => $tickList, | ||||||
|         ]; |         ]; | ||||||
| @ -40,7 +40,7 @@ class HomeController extends Controller { | |||||||
|         $result = $this->processTick($_POST); |         $result = $this->processTick($_POST); | ||||||
| 
 | 
 | ||||||
|         // redirect to the index (will show the latest tick if one was sent)
 |         // 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; |         exit; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -63,7 +63,7 @@ class HomeController extends Controller { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             $tickModel = new TickModel($app['db'], $app['config']); |             $tickModel = new TickModel($app['db'], $app['settings']); | ||||||
|             $tickModel->insert($tickContent); |             $tickModel->insert($tickContent); | ||||||
|             Log::info("New tick created: " . substr($tickContent, 0, 50) . (strlen($tickContent) > 50 ? '...' : '')); |             Log::info("New tick created: " . substr($tickContent, 0, 50) . (strlen($tickContent) > 50 ? '...' : '')); | ||||||
|             $result['success'] = true; |             $result['success'] = true; | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ class LogController extends Controller { | |||||||
| 
 | 
 | ||||||
|         // Ensure user is logged in
 |         // Ensure user is logged in
 | ||||||
|         if (!Session::isLoggedIn()) { |         if (!Session::isLoggedIn()) { | ||||||
|             header('Location: ' . Util::buildRelativeUrl($app['config']->basePath, 'login')); |             header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login')); | ||||||
|             exit; |             exit; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -49,7 +49,7 @@ class LogController extends Controller { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return [ |         return [ | ||||||
|             'config' => $app['config'], |             'settings' => $app['settings'], | ||||||
|             'logEntries' => $logEntries, |             'logEntries' => $logEntries, | ||||||
|             'availableRoutes' => $availableRoutes, |             'availableRoutes' => $availableRoutes, | ||||||
|             'availableLevels' => $availableLevels, |             'availableLevels' => $availableLevels, | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ declare(strict_types=1); | |||||||
|             $moodPicker = $view->renderMoodPicker(self::getEmojisWithLabels(), $app['user']->mood); |             $moodPicker = $view->renderMoodPicker(self::getEmojisWithLabels(), $app['user']->mood); | ||||||
| 
 | 
 | ||||||
|             $vars = [ |             $vars = [ | ||||||
|                 'config' => $app['config'], |                 'settings' => $app['settings'], | ||||||
|                 'moodPicker' => $moodPicker, |                 'moodPicker' => $moodPicker, | ||||||
|             ]; |             ]; | ||||||
| 
 | 
 | ||||||
| @ -41,7 +41,7 @@ declare(strict_types=1); | |||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // go back to the index and show the updated mood
 |                 // 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; |                 exit; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ class TickController extends Controller{ | |||||||
|         Log::debug("Fetching tick with ID: {$id}"); |         Log::debug("Fetching tick with ID: {$id}"); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             $tickModel = new TickModel($app['db'], $app['config']); |             $tickModel = new TickModel($app['db'], $app['settings']); | ||||||
|             $vars = $tickModel->get($id); |             $vars = $tickModel->get($id); | ||||||
| 
 | 
 | ||||||
|             if (empty($vars) || !isset($vars['tick'])) { |             if (empty($vars) || !isset($vars['tick'])) { | ||||||
|  | |||||||
| @ -15,10 +15,10 @@ class AtomGenerator extends FeedGenerator { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function buildFeed(): string { |     private function buildFeed(): string { | ||||||
|         Log::debug("Building Atom feed for " . $this->config->siteTitle); |         Log::debug("Building Atom feed for " . $this->settings->siteTitle); | ||||||
|         $feedTitle = Util::escape_xml($this->config->siteTitle . " Atom Feed"); |         $feedTitle = Util::escape_xml($this->settings->siteTitle . " Atom Feed"); | ||||||
|         $siteUrl = Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath)); |         $siteUrl = Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath)); | ||||||
|         $feedUrl = Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath, 'feed/atom')); |         $feedUrl = Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath, 'feed/atom')); | ||||||
|         $updated = date(DATE_ATOM, strtotime($this->ticks[0]['timestamp'] ?? 'now')); |         $updated = date(DATE_ATOM, strtotime($this->ticks[0]['timestamp'] ?? 'now')); | ||||||
| 
 | 
 | ||||||
|         ob_start(); |         ob_start(); | ||||||
| @ -33,7 +33,7 @@ class AtomGenerator extends FeedGenerator { | |||||||
|   <updated><?php echo $updated ?></updated>
 |   <updated><?php echo $updated ?></updated>
 | ||||||
|   <id><?php echo $siteUrl ?></id>
 |   <id><?php echo $siteUrl ?></id>
 | ||||||
|   <author> |   <author> | ||||||
|         <name><?= Util::escape_xml($this->config->siteTitle) ?></name>
 |         <name><?= Util::escape_xml($this->settings->siteTitle) ?></name>
 | ||||||
|   </author> |   </author> | ||||||
| <?php foreach ($this->ticks as $tick): | <?php foreach ($this->ticks as $tick): | ||||||
|     // build the tick entry components
 |     // build the tick entry components
 | ||||||
|  | |||||||
| @ -5,11 +5,11 @@ declare(strict_types=1); | |||||||
| // Specific feeds (RSS, Atom, etc.) will inherit from this.
 | // Specific feeds (RSS, Atom, etc.) will inherit from this.
 | ||||||
| // This will wrap the basic generator functionality.
 | // This will wrap the basic generator functionality.
 | ||||||
| abstract class FeedGenerator { | abstract class FeedGenerator { | ||||||
|     protected $config; |     protected $settings; | ||||||
|     protected $ticks; |     protected $ticks; | ||||||
| 
 | 
 | ||||||
|     public function __construct(ConfigModel $config, array $ticks) { |     public function __construct(SettingsModel $settings, array $ticks) { | ||||||
|         $this->config = $config; |         $this->settings = $settings; | ||||||
|         $this->ticks = $ticks; |         $this->ticks = $ticks; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -17,10 +17,10 @@ abstract class FeedGenerator { | |||||||
|     abstract public function getContentType(): string; |     abstract public function getContentType(): string; | ||||||
| 
 | 
 | ||||||
|     protected function buildTickUrl(int $tickId): 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 { |     protected function getSiteUrl(): string { | ||||||
|         return Util::buildUrl($this->config->baseUrl, $this->config->basePath); |         return Util::buildUrl($this->settings->baseUrl, $this->settings->basePath); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -17,16 +17,16 @@ class RssGenerator extends FeedGenerator { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function buildChannel(): string { |     private function buildChannel(): string { | ||||||
|         Log::debug("Building RSS channel for " . $this->config->siteTitle); |         Log::debug("Building RSS channel for " . $this->settings->siteTitle); | ||||||
|         ob_start(); |         ob_start(); | ||||||
|         ?>
 |         ?>
 | ||||||
| <channel> | <channel> | ||||||
|     <title><?php echo Util::escape_xml($this->config->siteTitle . ' RSS Feed') ?></title>
 |     <title><?php echo Util::escape_xml($this->settings->siteTitle . ' RSS Feed') ?></title>
 | ||||||
|     <link><?php echo Util::escape_xml(Util::buildUrl($this->config->baseUrl, $this->config->basePath))?></link>
 |     <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->config->baseUrl, $this->config->basePath, 'feed/rss'))?>" |     <atom:link href="<?php echo Util::escape_xml(Util::buildUrl($this->settings->baseUrl, $this->settings->basePath, 'feed/rss'))?>" | ||||||
|                rel="self" |                rel="self" | ||||||
|                type="application/rss+xml" /> |                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> |     <language>en-us</language> | ||||||
|     <lastBuildDate><?php echo date(DATE_RSS); ?></lastBuildDate>
 |     <lastBuildDate><?php echo date(DATE_RSS); ?></lastBuildDate>
 | ||||||
| <?php foreach ($this->ticks as $tick): | <?php foreach ($this->ticks as $tick): | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ class Log { | |||||||
| 
 | 
 | ||||||
|     private static function write($level, $message) { |     private static function write($level, $message) { | ||||||
|         global $app; |         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.
 |         // Only log messages if they're at or above the configured log level.
 | ||||||
|         if (self::LEVELS[$level] < $logLevel){ |         if (self::LEVELS[$level] < $logLevel){ | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ class Util { | |||||||
|                 global $app; |                 global $app; | ||||||
|                 $escaped_url = rtrim($matches[1], '.,!?;:)]}>'); |                 $escaped_url = rtrim($matches[1], '.,!?;:)]}>'); | ||||||
|                 $clean_url = html_entity_decode($escaped_url, ENT_QUOTES, 'UTF-8'); |                 $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>'; |                 return '<a' . $tabIndex . ' href="' . $clean_url . '"' . $link_attrs . '>' . $escaped_url . '</a>'; | ||||||
|             }, |             }, | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| <?php | <?php | ||||||
| declare(strict_types=1); | declare(strict_types=1); | ||||||
| 
 | 
 | ||||||
| class ConfigModel { | class SettingsModel { | ||||||
|     // properties and default values
 |     // properties and default values
 | ||||||
|     public string $siteTitle = 'My tkr'; |     public string $siteTitle = 'My tkr'; | ||||||
|     public string $siteDescription = ''; |     public string $siteDescription = ''; | ||||||
| @ -2,7 +2,7 @@ | |||||||
| declare(strict_types=1); | declare(strict_types=1); | ||||||
| 
 | 
 | ||||||
| class TickModel { | 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 { |     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 ?"); |         $stmt = $this->db->prepare("SELECT id, timestamp, tick FROM tick ORDER BY timestamp DESC LIMIT ? OFFSET ?"); | ||||||
| @ -32,7 +32,7 @@ class TickModel { | |||||||
|         return [ |         return [ | ||||||
|             'tickTime' => $row['timestamp'], |             'tickTime' => $row['timestamp'], | ||||||
|             'tick' => $row['tick'], |             'tick' => $row['tick'], | ||||||
|             'config' => $this->config, |             'settings' => $this->settings, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,15 +4,15 @@ declare(strict_types=1); | |||||||
| class TicksView { | class TicksView { | ||||||
|     private $html; |     private $html; | ||||||
| 
 | 
 | ||||||
|     public function __construct(ConfigModel $config, array $ticks, int $page){ |     public function __construct(SettingsModel $settings, array $ticks, int $page){ | ||||||
|         $this->html = $this->render($config, $ticks, $page); |         $this->html = $this->render($settings, $ticks, $page); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function getHtml(): string { |     public function getHtml(): string { | ||||||
|         return $this->html; |         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(); |         ob_start(); | ||||||
|         ?>
 |         ?>
 | ||||||
| 
 | 
 | ||||||
| @ -23,18 +23,18 @@ class TicksView { | |||||||
|                     $relativeTime = Util::relative_time($tick['timestamp']); |                     $relativeTime = Util::relative_time($tick['timestamp']); | ||||||
|                 ?>
 |                 ?>
 | ||||||
|                 <li class="tick" tabindex="0"> |                 <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>
 |                     <span class="tick-text"><?php echo Util::linkify(Util::escape_html($tick['tick'])) ?></span>
 | ||||||
|                 </li> |                 </li> | ||||||
|             <?php endforeach; ?>
 |             <?php endforeach; ?>
 | ||||||
|             </ul> |             </ul> | ||||||
|             <div class="tick-pagination"> |             <div class="tick-pagination"> | ||||||
|             <?php if ($page > 1): ?>
 |             <?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 ?>">« Newer</a> |                    href="?page=<?php echo $page - 1 ?>">« Newer</a> | ||||||
|             <?php endif; ?>
 |             <?php endif; ?>
 | ||||||
|             <?php if (count($ticks) === $config->itemsPerPage): ?>
 |             <?php if (count($ticks) === $settings->itemsPerPage): ?>
 | ||||||
|                 <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                 <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                    href="?page=<?php echo $page + 1 ?>">Older »</a> |                    href="?page=<?php echo $page + 1 ?>">Older »</a> | ||||||
|             <?php endif; ?>
 |             <?php endif; ?>
 | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <?php /** @var bool $isLoggedIn */ ?>
 | <?php /** @var bool $isLoggedIn */ ?>
 | ||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var UserModel $user */ ?>
 | <?php /** @var UserModel $user */ ?>
 | ||||||
| <?php /** @var string $childTemplateFile */ ?>
 | <?php /** @var string $childTemplateFile */ ?>
 | ||||||
| <?php /** @var string $customCssFilename */ ?>
 | <?php /** @var string $customCssFilename */ ?>
 | ||||||
| @ -7,23 +7,23 @@ | |||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
|     <head> |     <head> | ||||||
|         <title><?= $config->siteTitle ?></title>
 |         <title><?= $settings->siteTitle ?></title>
 | ||||||
|         <meta charset="UTF-8"> |         <meta charset="UTF-8"> | ||||||
|         <meta name="viewport" content="width=device-width, initial-scale=1.0"> |         <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|         <link rel="stylesheet" |         <link rel="stylesheet" | ||||||
|               href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'css/default.css')) ?>"> |               href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'css/default.css')) ?>"> | ||||||
| <?php if (!empty($config->cssId)): ?>
 | <?php if (!empty($settings->cssId)): ?>
 | ||||||
|         <link rel="stylesheet" |         <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; ?>
 | <?php endif; ?>
 | ||||||
|         <link rel="alternate" |         <link rel="alternate" | ||||||
|               type="application/rss+xml" |               type="application/rss+xml" | ||||||
|               title="<?php echo Util::escape_html($config->siteTitle) ?> RSS Feed" |               title="<?php echo Util::escape_html($settings->siteTitle) ?> RSS Feed" | ||||||
|               href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/rss/"> |               href="<?php echo Util::escape_html($settings->baseUrl . $settings->basePath)?>feed/rss/"> | ||||||
|         <link rel="alternate" |         <link rel="alternate" | ||||||
|               type="application/atom+xml" |               type="application/atom+xml" | ||||||
|               title="<?php echo Util::escape_html($config->siteTitle) ?> Atom Feed" |               title="<?php echo Util::escape_html($settings->siteTitle) ?> Atom Feed" | ||||||
|               href="<?php echo Util::escape_html($config->baseUrl . $config->basePath)?>feed/atom/"> |               href="<?php echo Util::escape_html($settings->baseUrl . $settings->basePath)?>feed/atom/"> | ||||||
|     </head> |     </head> | ||||||
|     <body> |     <body> | ||||||
| <?php include TEMPLATES_DIR . '/partials/navbar.php'?>
 | <?php include TEMPLATES_DIR . '/partials/navbar.php'?>
 | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var UserModel $user */ ?>
 | <?php /** @var UserModel $user */ ?>
 | ||||||
| <?php /** @var isSetup bool */ ?>
 | <?php /** @var isSetup bool */ ?>
 | ||||||
|         <h1><?php if ($isSetup): ?>Setup<?php else: ?>Admin<?php endif; ?></h1>
 |         <h1><?php if ($isSetup): ?>Setup<?php else: ?>Admin<?php endif; ?></h1>
 | ||||||
|         <main> |         <main> | ||||||
|             <form |             <form | ||||||
|                 action="<?php echo Util::buildRelativeUrl($config->basePath, ($isSetup ? 'setup' : 'admin')) ?>" |                 action="<?php echo Util::buildRelativeUrl($settings->basePath, ($isSetup ? 'setup' : 'admin')) ?>" | ||||||
|                 method="post"> |                 method="post"> | ||||||
|                 <input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>"> |                 <input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>"> | ||||||
|                 <fieldset> |                 <fieldset> | ||||||
| @ -36,43 +36,43 @@ | |||||||
|                         <input type="text" |                         <input type="text" | ||||||
|                             id="site_title" |                             id="site_title" | ||||||
|                             name="site_title" |                             name="site_title" | ||||||
|                             value="<?= Util::escape_html($config->siteTitle) ?>" |                             value="<?= Util::escape_html($settings->siteTitle) ?>" | ||||||
|                             required> |                             required> | ||||||
|                         <label for="site_description">Description <span class=required>*</span></label> |                         <label for="site_description">Description <span class=required>*</span></label> | ||||||
|                         <input type="text" |                         <input type="text" | ||||||
|                             id="site_description" |                             id="site_description" | ||||||
|                             name="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> |                         <label for="base_url">Base URL <span class=required>*</span></label> | ||||||
|                         <input type="text" |                         <input type="text" | ||||||
|                             id="base_url" |                             id="base_url" | ||||||
|                             name="base_url" |                             name="base_url" | ||||||
|                             value="<?= Util::escape_html($config->baseUrl) ?>" |                             value="<?= Util::escape_html($settings->baseUrl) ?>" | ||||||
|                             required> |                             required> | ||||||
|                         <label for="base_path">Base path <span class=required>*</span></label> |                         <label for="base_path">Base path <span class=required>*</span></label> | ||||||
|                         <input type="text" |                         <input type="text" | ||||||
|                             id="base_path" |                             id="base_path" | ||||||
|                             name="base_path" |                             name="base_path" | ||||||
|                             value="<?= Util::escape_html($config->basePath) ?>" |                             value="<?= Util::escape_html($settings->basePath) ?>" | ||||||
|                             required> |                             required> | ||||||
|                         <label for="items_per_page">Ticks per page (max 50) <span class=required>*</span></label> |                         <label for="items_per_page">Ticks per page (max 50) <span class=required>*</span></label> | ||||||
|                         <input type="number" |                         <input type="number" | ||||||
|                             id="items_per_page" |                             id="items_per_page" | ||||||
|                             name="items_per_page" |                             name="items_per_page" | ||||||
|                             value="<?= $config->itemsPerPage ?>" min="1" max="50" |                             value="<?= $settings->itemsPerPage ?>" min="1" max="50" | ||||||
|                             required> |                             required> | ||||||
|                         <label for="strict_accessibility">Strict accessibility</label> |                         <label for="strict_accessibility">Strict accessibility</label> | ||||||
|                         <input type="checkbox" |                         <input type="checkbox" | ||||||
|                                id="strict_accessibility" |                                id="strict_accessibility" | ||||||
|                                name="strict_accessibility" |                                name="strict_accessibility" | ||||||
|                                value="1" |                                value="1" | ||||||
|                                <?php if ($config->strictAccessibility): ?> checked <?php endif; ?>>
 |                                <?php if ($settings->strictAccessibility): ?> checked <?php endif; ?>>
 | ||||||
|                         <label for="strict_accessibility">Log Level</label> |                         <label for="strict_accessibility">Log Level</label> | ||||||
|                         <select id="log_level" name="log_level"> |                         <select id="log_level" name="log_level"> | ||||||
|                             <option value="1" <?= ($config->logLevel ?? 2) == 1 ? 'selected' : '' ?>>DEBUG</option>
 |                             <option value="1" <?= ($settings->logLevel ?? 2) == 1 ? 'selected' : '' ?>>DEBUG</option>
 | ||||||
|                             <option value="2" <?= ($config->logLevel ?? 2) == 2 ? 'selected' : '' ?>>INFO</option>
 |                             <option value="2" <?= ($settings->logLevel ?? 2) == 2 ? 'selected' : '' ?>>INFO</option>
 | ||||||
|                             <option value="3" <?= ($config->logLevel ?? 2) == 3 ? 'selected' : '' ?>>WARNING</option>
 |                             <option value="3" <?= ($settings->logLevel ?? 2) == 3 ? 'selected' : '' ?>>WARNING</option>
 | ||||||
|                             <option value="4" <?= ($config->logLevel ?? 2) == 4 ? 'selected' : '' ?>>ERROR</option>
 |                             <option value="4" <?= ($settings->logLevel ?? 2) == 4 ? 'selected' : '' ?>>ERROR</option>
 | ||||||
|                         </select> |                         </select> | ||||||
|                     </div> |                     </div> | ||||||
|                 </fieldset> |                 </fieldset> | ||||||
|  | |||||||
| @ -1,18 +1,18 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var Array $customCss */ ?>
 | <?php /** @var Array $customCss */ ?>
 | ||||||
|         <h1>CSS Management</h1> |         <h1>CSS Management</h1> | ||||||
|         <main> |         <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']) ?>"> |                 <input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>"> | ||||||
|                 <fieldset> |                 <fieldset> | ||||||
|                     <legend>Manage</legend> |                     <legend>Manage</legend> | ||||||
|                     <div class="fieldset-items"> |                     <div class="fieldset-items"> | ||||||
|                         <label for="selectCssFile">Select CSS File</label> |                         <label for="selectCssFile">Select CSS File</label> | ||||||
|                         <select id="selectCssFile" name="selectCssFile"> |                         <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 foreach ($customCss as $cssFile): ?>
 | ||||||
|     <?php |     <?php | ||||||
|         if ((int) $cssFile['id'] == $config->cssId){ |         if ((int) $cssFile['id'] == $settings->cssId){ | ||||||
|             $cssDescription = $cssFile['description']; |             $cssDescription = $cssFile['description']; | ||||||
|             $selected = "selected"; |             $selected = "selected"; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var array $emojiList */ ?>
 | <?php /** @var array $emojiList */ ?>
 | ||||||
|         <h1>Emoji Management</h1> |         <h1>Emoji Management</h1> | ||||||
|         <main> |         <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']) ?>"> |                 <input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>"> | ||||||
|                 <fieldset> |                 <fieldset> | ||||||
|                     <legend>Add Emoji</legend> |                     <legend>Add Emoji</legend> | ||||||
| @ -24,7 +24,7 @@ | |||||||
|                 </fieldset> |                 </fieldset> | ||||||
|             </form> |             </form> | ||||||
| <?php if (!empty($emojiList)): ?>
 | <?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']) ?>"> |                 <input type="hidden" name="csrf_token" value="<?= Util::escape_html($_SESSION['csrf_token']) ?>"> | ||||||
|                 <fieldset class="delete-emoji-fieldset"> |                 <fieldset class="delete-emoji-fieldset"> | ||||||
|                     <legend>Delete Emoji</legend> |                     <legend>Delete Emoji</legend> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <?php /** @var bool $isLoggedIn */ ?>
 | <?php /** @var bool $isLoggedIn */ ?>
 | ||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var UserModel $user */ ?>
 | <?php /** @var UserModel $user */ ?>
 | ||||||
| <?php /** @var string $tickList */ ?>
 | <?php /** @var string $tickList */ ?>
 | ||||||
|         <div class="home-container"> |         <div class="home-container"> | ||||||
| @ -13,8 +13,8 @@ | |||||||
|                         </span> |                         </span> | ||||||
| <?php if (Session::isLoggedIn()): ?>
 | <?php if (Session::isLoggedIn()): ?>
 | ||||||
|                         <a |                         <a | ||||||
|                             <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                             <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                             href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'mood')) ?>" |                             href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'mood')) ?>" | ||||||
|                             class="change-mood">Change mood</a> |                             class="change-mood">Change mood</a> | ||||||
| <?php endif ?>
 | <?php endif ?>
 | ||||||
|                     </dd> |                     </dd> | ||||||
| @ -48,7 +48,7 @@ | |||||||
| <?php endif; ?>
 | <?php endif; ?>
 | ||||||
|             </aside> |             </aside> | ||||||
|             <main id="ticks"> |             <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 ?>
 |                 <?php echo $tickList ?>
 | ||||||
|             </main> |             </main> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var string $csrf_token */ ?>
 | <?php /** @var string $csrf_token */ ?>
 | ||||||
| <?php /** @var string $error */ ?>
 | <?php /** @var string $error */ ?>
 | ||||||
|     <h2>Login</h2> |     <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"> |         <div class="fieldset-items"> | ||||||
|             <input type="hidden" name="csrf_token" value="<?= Util::escape_html($csrf_token) ?>"> |             <input type="hidden" name="csrf_token" value="<?= Util::escape_html($csrf_token) ?>"> | ||||||
|             <label for="username">Username:</label> |             <label for="username">Username:</label> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /** @var array $logEntries */ ?>
 | <?php /** @var array $logEntries */ ?>
 | ||||||
| <?php /** @var array $availableRoutes */ ?>
 | <?php /** @var array $availableRoutes */ ?>
 | ||||||
| <?php /** @var array $availableLevels */ ?>
 | <?php /** @var array $availableLevels */ ?>
 | ||||||
| @ -8,7 +8,7 @@ | |||||||
|         <main> |         <main> | ||||||
|             <!-- Filters --> |             <!-- Filters --> | ||||||
|             <div class="log-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> |                     <fieldset> | ||||||
|                         <legend>Filter Logs</legend> |                         <legend>Filter Logs</legend> | ||||||
|                         <div class="fieldset-items"> |                         <div class="fieldset-items"> | ||||||
| @ -35,7 +35,7 @@ | |||||||
|                             </select> |                             </select> | ||||||
| 
 | 
 | ||||||
|                             <div></div><button type="submit">Filter</button> |                             <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> |                         </div> | ||||||
|                     </fieldset> |                     </fieldset> | ||||||
|                 </form> |                 </form> | ||||||
|  | |||||||
| @ -1,35 +1,35 @@ | |||||||
| <?php /** @var ConfigModel $config */ ?>
 | <?php /** @var SettingsModel $settings */ ?>
 | ||||||
| <?php /* https://www.w3schools.com/howto/howto_css_dropdown.asp */ ?>
 | <?php /* https://www.w3schools.com/howto/howto_css_dropdown.asp */ ?>
 | ||||||
|         <nav aria-label="Main navigation"> |         <nav aria-label="Main navigation"> | ||||||
|             <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |             <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath)) ?>">home</a> |                href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath)) ?>">home</a> | ||||||
|             <details> |             <details> | ||||||
|                 <summary aria-haspopup="true">feeds</summary> |                 <summary aria-haspopup="true">feeds</summary> | ||||||
|                 <div class="dropdown-items"> |                 <div class="dropdown-items"> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'feed/rss')) ?>">rss</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'feed/rss')) ?>">rss</a> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'feed/atom')) ?>">atom</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'feed/atom')) ?>">atom</a> | ||||||
|                 </div> |                 </div> | ||||||
|             </details> |             </details> | ||||||
| <?php if (!Session::isLoggedIn()): ?>
 | <?php if (!Session::isLoggedIn()): ?>
 | ||||||
|             <a tabindex="0" |             <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: ?>
 | <?php else: ?>
 | ||||||
|             <details> |             <details> | ||||||
|                 <summary aria-haspopup="true">admin</summary> |                 <summary aria-haspopup="true">admin</summary> | ||||||
|                 <div class="dropdown-items"> |                 <div class="dropdown-items"> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin')) ?>">settings</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin')) ?>">settings</a> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/css')) ?>">css</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/css')) ?>">css</a> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/emoji')) ?>">emoji</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/emoji')) ?>">emoji</a> | ||||||
|                     <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |                     <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                        href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'admin/logs')) ?>">logs</a> |                        href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'admin/logs')) ?>">logs</a> | ||||||
|                 </div> |                 </div> | ||||||
|             </details> |             </details> | ||||||
|             <a <?php if($config->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 |             <a <?php if($settings->strictAccessibility): ?>tabindex="0"<?php endif; ?>
 | ||||||
|                href="<?= Util::escape_html(Util::buildRelativeUrl($config->basePath, 'logout')) ?>">logout</a> |                href="<?= Util::escape_html(Util::buildRelativeUrl($settings->basePath, 'logout')) ?>">logout</a> | ||||||
| <?php endif; ?>
 | <?php endif; ?>
 | ||||||
|         </nav> |         </nav> | ||||||
| @ -7,7 +7,7 @@ use PHPUnit\Framework\TestCase; | |||||||
| class AdminControllerTest extends TestCase | class AdminControllerTest extends TestCase | ||||||
| { | { | ||||||
|     private PDO $mockPdo; |     private PDO $mockPdo; | ||||||
|     private ConfigModel $config; |     private SettingsModel $settings; | ||||||
|     private UserModel $user; |     private UserModel $user; | ||||||
| 
 | 
 | ||||||
|     protected function setUp(): void |     protected function setUp(): void | ||||||
| @ -16,12 +16,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $this->mockPdo = $this->createMock(PDO::class); |         $this->mockPdo = $this->createMock(PDO::class); | ||||||
| 
 | 
 | ||||||
|         // Create real config and user objects with mocked PDO
 |         // Create real config and user objects with mocked PDO
 | ||||||
|         $this->config = new ConfigModel($this->mockPdo); |         $this->settings = new SettingsModel($this->mockPdo); | ||||||
|         $this->config->siteTitle = 'Test Site'; |         $this->settings->siteTitle = 'Test Site'; | ||||||
|         $this->config->siteDescription = 'Test Description'; |         $this->settings->siteDescription = 'Test Description'; | ||||||
|         $this->config->baseUrl = 'https://example.com'; |         $this->settings->baseUrl = 'https://example.com'; | ||||||
|         $this->config->basePath = '/tkr'; |         $this->settings->basePath = '/tkr'; | ||||||
|         $this->config->itemsPerPage = 10; |         $this->settings->itemsPerPage = 10; | ||||||
| 
 | 
 | ||||||
|         $this->user = new UserModel($this->mockPdo); |         $this->user = new UserModel($this->mockPdo); | ||||||
|         $this->user->username = 'testuser'; |         $this->user->username = 'testuser'; | ||||||
| @ -32,7 +32,7 @@ class AdminControllerTest extends TestCase | |||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'db' => $this->mockPdo, |             'db' => $this->mockPdo, | ||||||
|             'config' => $this->config, |             'settings' => $this->settings, | ||||||
|             'user' => $this->user, |             'user' => $this->user, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| @ -43,12 +43,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $data = $controller->getAdminData(false); |         $data = $controller->getAdminData(false); | ||||||
| 
 | 
 | ||||||
|         // Should return proper structure
 |         // Should return proper structure
 | ||||||
|         $this->assertArrayHasKey('config', $data); |         $this->assertArrayHasKey('settings', $data); | ||||||
|         $this->assertArrayHasKey('user', $data); |         $this->assertArrayHasKey('user', $data); | ||||||
|         $this->assertArrayHasKey('isSetup', $data); |         $this->assertArrayHasKey('isSetup', $data); | ||||||
| 
 | 
 | ||||||
|         // Should be the injected instances
 |         // Should be the injected instances
 | ||||||
|         $this->assertSame($this->config, $data['config']); |         $this->assertSame($this->settings, $data['settings']); | ||||||
|         $this->assertSame($this->user, $data['user']); |         $this->assertSame($this->user, $data['user']); | ||||||
|         $this->assertFalse($data['isSetup']); |         $this->assertFalse($data['isSetup']); | ||||||
|     } |     } | ||||||
| @ -59,12 +59,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $data = $controller->getAdminData(true); |         $data = $controller->getAdminData(true); | ||||||
| 
 | 
 | ||||||
|         // Should return proper structure
 |         // Should return proper structure
 | ||||||
|         $this->assertArrayHasKey('config', $data); |         $this->assertArrayHasKey('settings', $data); | ||||||
|         $this->assertArrayHasKey('user', $data); |         $this->assertArrayHasKey('user', $data); | ||||||
|         $this->assertArrayHasKey('isSetup', $data); |         $this->assertArrayHasKey('isSetup', $data); | ||||||
| 
 | 
 | ||||||
|         // Should be the injected instances
 |         // Should be the injected instances
 | ||||||
|         $this->assertSame($this->config, $data['config']); |         $this->assertSame($this->settings, $data['settings']); | ||||||
|         $this->assertSame($this->user, $data['user']); |         $this->assertSame($this->user, $data['user']); | ||||||
|         $this->assertTrue($data['isSetup']); |         $this->assertTrue($data['isSetup']); | ||||||
|     } |     } | ||||||
| @ -133,12 +133,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $this->mockPdo->method('query')->willReturn($mockStatement); |         $this->mockPdo->method('query')->willReturn($mockStatement); | ||||||
| 
 | 
 | ||||||
|         // Create models with mocked PDO
 |         // Create models with mocked PDO
 | ||||||
|         $config = new ConfigModel($this->mockPdo); |         $settings = new SettingsModel($this->mockPdo); | ||||||
|         $user = new UserModel($this->mockPdo); |         $user = new UserModel($this->mockPdo); | ||||||
| 
 | 
 | ||||||
|         // Update global $app with test models
 |         // Update global $app with test models
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app['config'] = $config; |         $app['settings'] = $settings; | ||||||
|         $app['user'] = $user; |         $app['user'] = $user; | ||||||
| 
 | 
 | ||||||
|         $controller = new AdminController(); |         $controller = new AdminController(); | ||||||
| @ -195,12 +195,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $this->mockPdo->method('query')->willReturn($mockStatement); |         $this->mockPdo->method('query')->willReturn($mockStatement); | ||||||
| 
 | 
 | ||||||
|         // Create models with mocked PDO
 |         // Create models with mocked PDO
 | ||||||
|         $config = new ConfigModel($this->mockPdo); |         $settings = new SettingsModel($this->mockPdo); | ||||||
|         $user = new UserModel($this->mockPdo); |         $user = new UserModel($this->mockPdo); | ||||||
| 
 | 
 | ||||||
|         // Update global $app with test models
 |         // Update global $app with test models
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app['config'] = $config; |         $app['settings'] = $settings; | ||||||
|         $app['user'] = $user; |         $app['user'] = $user; | ||||||
| 
 | 
 | ||||||
|         $controller = new AdminController(); |         $controller = new AdminController(); | ||||||
| @ -228,12 +228,12 @@ class AdminControllerTest extends TestCase | |||||||
|         $this->mockPdo->method('query') |         $this->mockPdo->method('query') | ||||||
|                      ->willThrowException(new PDOException("Database error")); |                      ->willThrowException(new PDOException("Database error")); | ||||||
| 
 | 
 | ||||||
|         $config = new ConfigModel($this->mockPdo); |         $settings = new SettingsModel($this->mockPdo); | ||||||
|         $user = new UserModel($this->mockPdo); |         $user = new UserModel($this->mockPdo); | ||||||
| 
 | 
 | ||||||
|         // Update global $app with test models
 |         // Update global $app with test models
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app['config'] = $config; |         $app['settings'] = $settings; | ||||||
|         $app['user'] = $user; |         $app['user'] = $user; | ||||||
| 
 | 
 | ||||||
|         $controller = new AdminController(); |         $controller = new AdminController(); | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ class FeedControllerTest extends TestCase | |||||||
| { | { | ||||||
|     private PDO $mockPdo; |     private PDO $mockPdo; | ||||||
|     private PDOStatement $mockStatement; |     private PDOStatement $mockStatement; | ||||||
|     private ConfigModel $mockConfig; |     private SettingsModel $mockConfig; | ||||||
|     private UserModel $mockUser; |     private UserModel $mockUser; | ||||||
|     private string $tempLogDir; |     private string $tempLogDir; | ||||||
| 
 | 
 | ||||||
| @ -23,7 +23,7 @@ class FeedControllerTest extends TestCase | |||||||
|         $this->mockPdo = $this->createMock(PDO::class); |         $this->mockPdo = $this->createMock(PDO::class); | ||||||
| 
 | 
 | ||||||
|         // Mock config with feed-relevant properties
 |         // Mock config with feed-relevant properties
 | ||||||
|         $this->mockConfig = new ConfigModel($this->mockPdo); |         $this->mockConfig = new SettingsModel($this->mockPdo); | ||||||
|         $this->mockConfig->itemsPerPage = 10; |         $this->mockConfig->itemsPerPage = 10; | ||||||
|         $this->mockConfig->basePath = '/tkr'; |         $this->mockConfig->basePath = '/tkr'; | ||||||
|         $this->mockConfig->siteTitle = 'Test Site'; |         $this->mockConfig->siteTitle = 'Test Site'; | ||||||
| @ -38,7 +38,7 @@ class FeedControllerTest extends TestCase | |||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'db' => $this->mockPdo, |             'db' => $this->mockPdo, | ||||||
|             'config' => $this->mockConfig, |             'settings' => $this->mockConfig, | ||||||
|             'user' => $this->mockUser, |             'user' => $this->mockUser, | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ class HomeControllerTest extends TestCase | |||||||
| { | { | ||||||
|     private PDO $mockPdo; |     private PDO $mockPdo; | ||||||
|     private PDOStatement $mockStatement; |     private PDOStatement $mockStatement; | ||||||
|     private ConfigModel $mockConfig; |     private SettingsModel $mockConfig; | ||||||
|     private UserModel $mockUser; |     private UserModel $mockUser; | ||||||
| 
 | 
 | ||||||
|     protected function setUp(): void |     protected function setUp(): void | ||||||
| @ -20,7 +20,7 @@ class HomeControllerTest extends TestCase | |||||||
|         $this->mockPdo = $this->createMock(PDO::class); |         $this->mockPdo = $this->createMock(PDO::class); | ||||||
| 
 | 
 | ||||||
|         // Mock config
 |         // Mock config
 | ||||||
|         $this->mockConfig = new ConfigModel($this->mockPdo); |         $this->mockConfig = new SettingsModel($this->mockPdo); | ||||||
|         $this->mockConfig->itemsPerPage = 10; |         $this->mockConfig->itemsPerPage = 10; | ||||||
|         $this->mockConfig->basePath = '/tkr'; |         $this->mockConfig->basePath = '/tkr'; | ||||||
| 
 | 
 | ||||||
| @ -33,7 +33,7 @@ class HomeControllerTest extends TestCase | |||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'db' => $this->mockPdo, |             'db' => $this->mockPdo, | ||||||
|             'config' => $this->mockConfig, |             'settings' => $this->mockConfig, | ||||||
|             'user' => $this->mockUser, |             'user' => $this->mockUser, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| @ -77,12 +77,12 @@ class HomeControllerTest extends TestCase | |||||||
|         $data = $controller->getHomeData(1); |         $data = $controller->getHomeData(1); | ||||||
| 
 | 
 | ||||||
|         // Should return proper structure
 |         // Should return proper structure
 | ||||||
|         $this->assertArrayHasKey('config', $data); |         $this->assertArrayHasKey('settings', $data); | ||||||
|         $this->assertArrayHasKey('user', $data); |         $this->assertArrayHasKey('user', $data); | ||||||
|         $this->assertArrayHasKey('tickList', $data); |         $this->assertArrayHasKey('tickList', $data); | ||||||
| 
 | 
 | ||||||
|         // Config and user should be the injected instances
 |         // 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']); |         $this->assertSame($this->mockUser, $data['user']); | ||||||
| 
 | 
 | ||||||
|         // Should have tick list HTML (even if empty)
 |         // Should have tick list HTML (even if empty)
 | ||||||
| @ -104,7 +104,7 @@ class HomeControllerTest extends TestCase | |||||||
|         $data = $controller->getHomeData(1); |         $data = $controller->getHomeData(1); | ||||||
| 
 | 
 | ||||||
|         // Should return proper structure
 |         // Should return proper structure
 | ||||||
|         $this->assertArrayHasKey('config', $data); |         $this->assertArrayHasKey('settings', $data); | ||||||
|         $this->assertArrayHasKey('user', $data); |         $this->assertArrayHasKey('user', $data); | ||||||
|         $this->assertArrayHasKey('tickList', $data); |         $this->assertArrayHasKey('tickList', $data); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ class LogControllerTest extends TestCase | |||||||
| 
 | 
 | ||||||
|         // Set up global $app for simplified dependency access
 |         // Set up global $app for simplified dependency access
 | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $mockConfig = new ConfigModel($mockPdo); |         $mockConfig = new SettingsModel($mockPdo); | ||||||
|         $mockConfig->baseUrl = 'https://example.com'; |         $mockConfig->baseUrl = 'https://example.com'; | ||||||
|         $mockConfig->basePath = '/tkr/'; |         $mockConfig->basePath = '/tkr/'; | ||||||
| 
 | 
 | ||||||
| @ -32,7 +32,7 @@ class LogControllerTest extends TestCase | |||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'db' => $mockPdo, |             'db' => $mockPdo, | ||||||
|             'config' => $mockConfig, |             'settings' => $mockConfig, | ||||||
|             'user' => $mockUser, |             'user' => $mockUser, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; | |||||||
| class TickControllerTest extends TestCase | class TickControllerTest extends TestCase | ||||||
| { | { | ||||||
|     private $mockPdo; |     private $mockPdo; | ||||||
|     private $config; |     private $settings; | ||||||
|     private $user; |     private $user; | ||||||
| 
 | 
 | ||||||
|     protected function setUp(): void |     protected function setUp(): void | ||||||
| @ -17,10 +17,10 @@ class TickControllerTest extends TestCase | |||||||
|         // Set up mocks
 |         // Set up mocks
 | ||||||
|         $this->mockPdo = $this->createMock(PDO::class); |         $this->mockPdo = $this->createMock(PDO::class); | ||||||
| 
 | 
 | ||||||
|         $this->config = new ConfigModel($this->mockPdo); |         $this->settings = new SettingsModel($this->mockPdo); | ||||||
|         $this->config->baseUrl = 'https://example.com'; |         $this->settings->baseUrl = 'https://example.com'; | ||||||
|         $this->config->basePath = '/tkr/'; |         $this->settings->basePath = '/tkr/'; | ||||||
|         $this->config->itemsPerPage = 10; |         $this->settings->itemsPerPage = 10; | ||||||
| 
 | 
 | ||||||
|         $this->user = new UserModel($this->mockPdo); |         $this->user = new UserModel($this->mockPdo); | ||||||
| 
 | 
 | ||||||
| @ -28,7 +28,7 @@ class TickControllerTest extends TestCase | |||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'db' => $this->mockPdo, |             'db' => $this->mockPdo, | ||||||
|             'config' => $this->config, |             'settings' => $this->settings, | ||||||
|             'user' => $this->user, |             'user' => $this->user, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -7,12 +7,12 @@ class AtomGeneratorTest extends TestCase | |||||||
| { | { | ||||||
|     private function createMockConfig() { |     private function createMockConfig() { | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $config = new ConfigModel($mockPdo); |         $settings = new SettingsModel($mockPdo); | ||||||
|         $config->siteTitle = 'Test Site'; |         $settings->siteTitle = 'Test Site'; | ||||||
|         $config->siteDescription = 'Test Description'; |         $settings->siteDescription = 'Test Description'; | ||||||
|         $config->baseUrl = 'https://example.com'; |         $settings->baseUrl = 'https://example.com'; | ||||||
|         $config->basePath = '/tkr/'; |         $settings->basePath = '/tkr/'; | ||||||
|         return $config; |         return $settings; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function createSampleTicks() { |     private function createSampleTicks() { | ||||||
| @ -23,10 +23,10 @@ class AtomGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanGenerateValidAtom() { |     public function testCanGenerateValidAtom() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
|         $ticks = $this->createSampleTicks(); |         $ticks = $this->createSampleTicks(); | ||||||
| 
 | 
 | ||||||
|         $generator = new AtomGenerator($config, $ticks); |         $generator = new AtomGenerator($settings, $ticks); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Test XML structure
 |         // Test XML structure
 | ||||||
| @ -58,8 +58,8 @@ class AtomGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanHandleEmptyTickList() { |     public function testCanHandleEmptyTickList() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
|         $generator = new AtomGenerator($config, []); |         $generator = new AtomGenerator($settings, []); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Should still be valid Atom with no entries
 |         // Should still be valid Atom with no entries
 | ||||||
| @ -85,7 +85,7 @@ class AtomGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanHandleSpecialCharactersAndUnicode() { |     public function testCanHandleSpecialCharactersAndUnicode() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
| 
 | 
 | ||||||
|         // Test various challenging characters
 |         // Test various challenging characters
 | ||||||
|         $ticks = [ |         $ticks = [ | ||||||
| @ -111,7 +111,7 @@ class AtomGeneratorTest extends TestCase | |||||||
|             ] |             ] | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         $generator = new AtomGenerator($config, $ticks); |         $generator = new AtomGenerator($settings, $ticks); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Test that emojis are preserved
 |         // Test that emojis are preserved
 | ||||||
|  | |||||||
| @ -7,12 +7,12 @@ class FeedGeneratorTest extends TestCase | |||||||
| { | { | ||||||
|     private function createMockConfig() { |     private function createMockConfig() { | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $config = new ConfigModel($mockPdo); |         $settings = new SettingsModel($mockPdo); | ||||||
|         $config->siteTitle = 'Test Site'; |         $settings->siteTitle = 'Test Site'; | ||||||
|         $config->siteDescription = 'Test Description'; |         $settings->siteDescription = 'Test Description'; | ||||||
|         $config->baseUrl = 'https://example.com'; |         $settings->baseUrl = 'https://example.com'; | ||||||
|         $config->basePath = '/tkr/'; |         $settings->basePath = '/tkr/'; | ||||||
|         return $config; |         return $settings; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function createSampleTicks() { |     private function createSampleTicks() { | ||||||
| @ -22,11 +22,11 @@ class FeedGeneratorTest extends TestCase | |||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function createTestGenerator($config = null, $ticks = null) { |     private function createTestGenerator($settings = null, $ticks = null) { | ||||||
|         $config = $config ?? $this->createMockConfig(); |         $settings = $settings ?? $this->createMockConfig(); | ||||||
|         $ticks = $ticks ?? $this->createSampleTicks(); |         $ticks = $ticks ?? $this->createSampleTicks(); | ||||||
| 
 | 
 | ||||||
|         return new class($config, $ticks) extends FeedGenerator { |         return new class($settings, $ticks) extends FeedGenerator { | ||||||
|             public function generate(): string { |             public function generate(): string { | ||||||
|                 return '<test>content</test>'; |                 return '<test>content</test>'; | ||||||
|             } |             } | ||||||
| @ -69,12 +69,12 @@ class FeedGeneratorTest extends TestCase | |||||||
| 
 | 
 | ||||||
|     public function testUrlMethodsHandleSubdomainConfiguration() { |     public function testUrlMethodsHandleSubdomainConfiguration() { | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $config = new ConfigModel($mockPdo); |         $settings = new SettingsModel($mockPdo); | ||||||
|         $config->siteTitle = 'Test Site'; |         $settings->siteTitle = 'Test Site'; | ||||||
|         $config->baseUrl = 'https://tkr.example.com'; |         $settings->baseUrl = 'https://tkr.example.com'; | ||||||
|         $config->basePath = '/'; |         $settings->basePath = '/'; | ||||||
| 
 | 
 | ||||||
|         $generator = $this->createTestGenerator($config, []); |         $generator = $this->createTestGenerator($settings, []); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals('https://tkr.example.com/', $generator->testGetSiteUrl()); |         $this->assertEquals('https://tkr.example.com/', $generator->testGetSiteUrl()); | ||||||
|         $this->assertEquals('https://tkr.example.com/tick/456', $generator->testBuildTickUrl(456)); |         $this->assertEquals('https://tkr.example.com/tick/456', $generator->testBuildTickUrl(456)); | ||||||
| @ -82,12 +82,12 @@ class FeedGeneratorTest extends TestCase | |||||||
| 
 | 
 | ||||||
|     public function testUrlMethodsHandleEmptyBasePath() { |     public function testUrlMethodsHandleEmptyBasePath() { | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $config = new ConfigModel($mockPdo); |         $settings = new SettingsModel($mockPdo); | ||||||
|         $config->siteTitle = 'Test Site'; |         $settings->siteTitle = 'Test Site'; | ||||||
|         $config->baseUrl = 'https://example.com'; |         $settings->baseUrl = 'https://example.com'; | ||||||
|         $config->basePath = ''; |         $settings->basePath = ''; | ||||||
| 
 | 
 | ||||||
|         $generator = $this->createTestGenerator($config, []); |         $generator = $this->createTestGenerator($settings, []); | ||||||
| 
 | 
 | ||||||
|         $this->assertEquals('https://example.com/', $generator->testGetSiteUrl()); |         $this->assertEquals('https://example.com/', $generator->testGetSiteUrl()); | ||||||
|         $this->assertEquals('https://example.com/tick/789', $generator->testBuildTickUrl(789)); |         $this->assertEquals('https://example.com/tick/789', $generator->testBuildTickUrl(789)); | ||||||
| @ -106,12 +106,12 @@ class FeedGeneratorTest extends TestCase | |||||||
| 
 | 
 | ||||||
|         foreach ($testCases as [$basePath, $expectedSiteUrl, $expectedTickUrl]) { |         foreach ($testCases as [$basePath, $expectedSiteUrl, $expectedTickUrl]) { | ||||||
|             $mockPdo = $this->createMock(PDO::class); |             $mockPdo = $this->createMock(PDO::class); | ||||||
|             $config = new ConfigModel($mockPdo); |             $settings = new SettingsModel($mockPdo); | ||||||
|             $config->siteTitle = 'Test Site'; |             $settings->siteTitle = 'Test Site'; | ||||||
|             $config->baseUrl = 'https://example.com'; |             $settings->baseUrl = 'https://example.com'; | ||||||
|             $config->basePath = $basePath; |             $settings->basePath = $basePath; | ||||||
| 
 | 
 | ||||||
|             $generator = $this->createTestGenerator($config, []); |             $generator = $this->createTestGenerator($settings, []); | ||||||
| 
 | 
 | ||||||
|             $this->assertEquals($expectedSiteUrl, $generator->testGetSiteUrl(), "Failed for basePath: '$basePath'"); |             $this->assertEquals($expectedSiteUrl, $generator->testGetSiteUrl(), "Failed for basePath: '$basePath'"); | ||||||
|             $this->assertEquals($expectedTickUrl, $generator->testBuildTickUrl(123), "Failed for basePath: '$basePath'"); |             $this->assertEquals($expectedTickUrl, $generator->testBuildTickUrl(123), "Failed for basePath: '$basePath'"); | ||||||
|  | |||||||
| @ -7,12 +7,12 @@ class RssGeneratorTest extends TestCase | |||||||
| { | { | ||||||
|     private function createMockConfig() { |     private function createMockConfig() { | ||||||
|         $mockPdo = $this->createMock(PDO::class); |         $mockPdo = $this->createMock(PDO::class); | ||||||
|         $config = new ConfigModel($mockPdo); |         $settings = new SettingsModel($mockPdo); | ||||||
|         $config->siteTitle = 'Test Site'; |         $settings->siteTitle = 'Test Site'; | ||||||
|         $config->siteDescription = 'Test Description'; |         $settings->siteDescription = 'Test Description'; | ||||||
|         $config->baseUrl = 'https://example.com'; |         $settings->baseUrl = 'https://example.com'; | ||||||
|         $config->basePath = '/tkr/'; |         $settings->basePath = '/tkr/'; | ||||||
|         return $config; |         return $settings; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function createSampleTicks() { |     private function createSampleTicks() { | ||||||
| @ -23,10 +23,10 @@ class RssGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanGenerateValidRss() { |     public function testCanGenerateValidRss() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
|         $ticks = $this->createSampleTicks(); |         $ticks = $this->createSampleTicks(); | ||||||
| 
 | 
 | ||||||
|         $generator = new RssGenerator($config, $ticks); |         $generator = new RssGenerator($settings, $ticks); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Test XML structure
 |         // Test XML structure
 | ||||||
| @ -56,8 +56,8 @@ class RssGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanHandleEmptyTickList() { |     public function testCanHandleEmptyTickList() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
|         $generator = new RssGenerator($config, []); |         $generator = new RssGenerator($settings, []); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Should still be valid RSS with no items
 |         // Should still be valid RSS with no items
 | ||||||
| @ -81,7 +81,7 @@ class RssGeneratorTest extends TestCase | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testCanHandleSpecialCharactersAndUnicode() { |     public function testCanHandleSpecialCharactersAndUnicode() { | ||||||
|         $config = $this->createMockConfig(); |         $settings = $this->createMockConfig(); | ||||||
| 
 | 
 | ||||||
|         // Test various challenging characters
 |         // Test various challenging characters
 | ||||||
|         $ticks = [ |         $ticks = [ | ||||||
| @ -107,7 +107,7 @@ class RssGeneratorTest extends TestCase | |||||||
|             ] |             ] | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         $generator = new RssGenerator($config, $ticks); |         $generator = new RssGenerator($settings, $ticks); | ||||||
|         $xml = $generator->generate(); |         $xml = $generator->generate(); | ||||||
| 
 | 
 | ||||||
|         // Test that emojis are preserved
 |         // Test that emojis are preserved
 | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ class LogTest extends TestCase | |||||||
|     private function setLogLevel(int $level): void |     private function setLogLevel(int $level): void | ||||||
|     { |     { | ||||||
|         global $app; |         global $app; | ||||||
|         $app = ['config' => (object)['logLevel' => $level]]; |         $app = ['settings' => (object)['logLevel' => $level]]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function assertLogContains(string $message): void |     private function assertLogContains(string $message): void | ||||||
| @ -188,7 +188,7 @@ class LogTest extends TestCase | |||||||
|     { |     { | ||||||
|         // Set up config without logLevel property (simulates missing config value)
 |         // Set up config without logLevel property (simulates missing config value)
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app = ['config' => (object)[]]; |         $app = ['settings' => (object)[]]; | ||||||
| 
 | 
 | ||||||
|         // Should not throw errors and should default to INFO level
 |         // Should not throw errors and should default to INFO level
 | ||||||
|         Log::debug('Debug message');  // Should be filtered out (default INFO level = 2)
 |         Log::debug('Debug message');  // Should be filtered out (default INFO level = 2)
 | ||||||
|  | |||||||
| @ -151,7 +151,7 @@ final class UtilTest extends TestCase | |||||||
|         // Set up global $app with config
 |         // Set up global $app with config
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'config' => (object)['strictAccessibility' => $strictAccessibility] |             'settings' => (object)['strictAccessibility' => $strictAccessibility] | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         $result = Util::linkify($input); |         $result = Util::linkify($input); | ||||||
| @ -162,7 +162,7 @@ final class UtilTest extends TestCase | |||||||
|         // Test linkify without new window
 |         // Test linkify without new window
 | ||||||
|         global $app; |         global $app; | ||||||
|         $app = [ |         $app = [ | ||||||
|             'config' => (object)['strictAccessibility' => false] |             'settings' => (object)['strictAccessibility' => false] | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         $input = 'Visit https://example.com'; |         $input = 'Visit https://example.com'; | ||||||
|  | |||||||
| @ -175,11 +175,11 @@ try { | |||||||
|     echo "💾 Saving configuration...\n"; |     echo "💾 Saving configuration...\n"; | ||||||
| 
 | 
 | ||||||
|     // Create/update settings
 |     // Create/update settings
 | ||||||
|     $configModel = new ConfigModel($db); |     $settingsModel = new SettingsModel($db); | ||||||
|     $configModel->siteTitle = $siteTitle; |     $settingsModel->siteTitle = $siteTitle; | ||||||
|     $configModel->baseUrl = $baseUrl; |     $settingsModel->baseUrl = $baseUrl; | ||||||
|     $configModel->basePath = $basePath; |     $settingsModel->basePath = $basePath; | ||||||
|     $config = $configModel->save(); |     $settings = $settingsModel->save(); | ||||||
| 
 | 
 | ||||||
|     // Create admin user
 |     // Create admin user
 | ||||||
|     $userModel = new UserModel($db); |     $userModel = new UserModel($db); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user