Imports classes in core code with "use" instead of full references.

main
Yasen Pramatarov 2026-05-04 12:25:03 +03:00
parent cfed37ef8f
commit 896249f833
15 changed files with 130 additions and 85 deletions

View File

@ -5,6 +5,8 @@
* Used when code does require_once '../app/classes/log.php'. * Used when code does require_once '../app/classes/log.php'.
*/ */
use App\Core\NullLogger;
// If there is already a Log plugin loaded // If there is already a Log plugin loaded
if (class_exists('Log')) { if (class_exists('Log')) {
return; return;
@ -24,7 +26,7 @@ class Log {
if (isset($logObject) && method_exists($logObject, 'insertLog')) { if (isset($logObject) && method_exists($logObject, 'insertLog')) {
$this->logger = $logObject; $this->logger = $logObject;
} else { } else {
$this->logger = new \App\Core\NullLogger(); $this->logger = new NullLogger();
} }
} }

View File

@ -2,6 +2,8 @@
namespace App\Core; namespace App\Core;
use App\App;
class PluginManager class PluginManager
{ {
/** @var array<string, array{path: string, meta: array}> */ /** @var array<string, array{path: string, meta: array}> */
@ -168,7 +170,7 @@ class PluginManager
} }
// Use App API to get database connection // Use App API to get database connection
$db = \App\App::db(); $db = App::db();
$pdo = ($db instanceof \PDO) ? $db : $db->getConnection(); $pdo = ($db instanceof \PDO) ? $db : $db->getConnection();
try { try {
@ -214,7 +216,7 @@ class PluginManager
} }
// Use App API to get database connection // Use App API to get database connection
$db = \App\App::db(); $db = App::db();
// If database unavailable, fallback to manifest // If database unavailable, fallback to manifest
if (!$db) { if (!$db) {
@ -283,7 +285,7 @@ class PluginManager
return false; return false;
} }
$db = \App\App::db(); $db = App::db();
if (!$db) { if (!$db) {
app_log('error', 'PluginManager::purge: Database connection not available', ['scope' => 'plugin']); app_log('error', 'PluginManager::purge: Database connection not available', ['scope' => 'plugin']);
return false; return false;

View File

@ -1,5 +1,7 @@
<?php <?php
use App\Core\NullLogger;
/** /**
* Returns a logger instance: plugin Log if available, otherwise NullLogger. * Returns a logger instance: plugin Log if available, otherwise NullLogger.
* *
@ -11,7 +13,7 @@ function getLoggerInstance($database) {
return new Log($database); return new Log($database);
} }
require_once __DIR__ . '/../core/NullLogger.php'; require_once __DIR__ . '/../core/NullLogger.php';
return new \App\Core\NullLogger(); return new NullLogger();
} }
if (!function_exists('app_log')) { if (!function_exists('app_log')) {
@ -29,7 +31,7 @@ if (!function_exists('app_log')) {
static $fallbackLogger = null; static $fallbackLogger = null;
if ($fallbackLogger === null) { if ($fallbackLogger === null) {
require_once __DIR__ . '/../core/NullLogger.php'; require_once __DIR__ . '/../core/NullLogger.php';
$fallbackLogger = new \App\Core\NullLogger(); $fallbackLogger = new NullLogger();
} }
$fallbackLogger->log($level, $message, $context); $fallbackLogger->log($level, $message, $context);

View File

@ -1,5 +1,11 @@
<?php <?php
use App\App;
use App\Core\HookDispatcher;
use App\Core\Maintenance;
use App\Core\MigrationRunner;
use App\Core\PluginManager;
/* /*
* Admin control center * Admin control center
* *
@ -111,7 +117,7 @@ foreach ($sectionRegistry as $key => $meta) {
]; ];
} }
$sectionStatePayload = \App\Core\HookDispatcher::applyFilters('admin.sections.state', [ $sectionStatePayload = HookDispatcher::applyFilters('admin.sections.state', [
'sections' => $sectionRegistry, 'sections' => $sectionRegistry,
'state' => [], 'state' => [],
'db' => $db ?? null, 'db' => $db ?? null,
@ -125,9 +131,9 @@ if (is_array($sectionStatePayload)) {
// Get plugin catalog and list of loaded plugins // Get plugin catalog and list of loaded plugins
// with their dependencies // with their dependencies
$pluginCatalog = \App\Core\PluginManager::getCatalog(); $pluginCatalog = PluginManager::getCatalog();
$pluginLoadedMap = \App\Core\PluginManager::getLoaded(); $pluginLoadedMap = PluginManager::getLoaded();
$pluginDependencyErrors = \App\Core\PluginManager::getDependencyErrors(); $pluginDependencyErrors = PluginManager::getDependencyErrors();
$normalizeDependencies = static function ($meta): array { $normalizeDependencies = static function ($meta): array {
$deps = $meta['dependencies'] ?? []; $deps = $meta['dependencies'] ?? [];
@ -154,14 +160,14 @@ $pluginAdminMap = [];
foreach ($pluginCatalog as $slug => $info) { foreach ($pluginCatalog as $slug => $info) {
$meta = $info['meta'] ?? []; $meta = $info['meta'] ?? [];
$name = trim((string)($meta['name'] ?? $slug)); $name = trim((string)($meta['name'] ?? $slug));
$enabled = \App\Core\PluginManager::isEnabled($slug); // Use database setting $enabled = PluginManager::isEnabled($slug); // Use database setting
$dependencies = $normalizeDependencies($meta); $dependencies = $normalizeDependencies($meta);
$dependents = array_values($pluginDependentsIndex[$slug] ?? []); $dependents = array_values($pluginDependentsIndex[$slug] ?? []);
$enabledDependents = array_values(array_filter($dependents, static function($depSlug) { $enabledDependents = array_values(array_filter($dependents, static function($depSlug) {
return \App\Core\PluginManager::isEnabled($depSlug); // Use database setting return PluginManager::isEnabled($depSlug); // Use database setting
})); }));
$missingDependencies = array_values(array_filter($dependencies, static function($depSlug) use ($pluginCatalog) { $missingDependencies = array_values(array_filter($dependencies, static function($depSlug) use ($pluginCatalog) {
return !isset($pluginCatalog[$depSlug]) || !\App\Core\PluginManager::isEnabled($depSlug); // Use database setting return !isset($pluginCatalog[$depSlug]) || !PluginManager::isEnabled($depSlug); // Use database setting
})); }));
// Check for migration files and existing tables // Check for migration files and existing tables
@ -170,7 +176,7 @@ foreach ($pluginCatalog as $slug => $info) {
$existingTables = []; $existingTables = [];
if ($hasMigration) { if ($hasMigration) {
$db = \App\App::db(); $db = App::db();
if ($db instanceof PDO) { if ($db instanceof PDO) {
$stmt = $db->query("SHOW TABLES"); $stmt = $db->query("SHOW TABLES");
$allTables = $stmt->fetchAll(PDO::FETCH_COLUMN, 0); $allTables = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
@ -260,7 +266,7 @@ if ($postAction === 'read_migration') {
// Hooks actions for plugins // Hooks actions for plugins
if ($action !== '' && $action !== 'read_migration') { if ($action !== '' && $action !== 'read_migration') {
$customActionPayload = \App\Core\HookDispatcher::applyFilters('admin.actions.handle', [ $customActionPayload = HookDispatcher::applyFilters('admin.actions.handle', [
'handled' => false, 'handled' => false,
'action' => $action, 'action' => $action,
'request_method' => $_SERVER['REQUEST_METHOD'] ?? 'GET', 'request_method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
@ -295,18 +301,18 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
// Maintenance actions // Maintenance actions
if ($postAction === 'maintenance_on') { if ($postAction === 'maintenance_on') {
$msg = trim($_POST['maintenance_message'] ?? ''); $msg = trim($_POST['maintenance_message'] ?? '');
\App\Core\Maintenance::enable($msg); Maintenance::enable($msg);
Feedback::flash('NOTICE', 'DEFAULT', 'Maintenance mode enabled.', true); Feedback::flash('NOTICE', 'DEFAULT', 'Maintenance mode enabled.', true);
} elseif ($postAction === 'maintenance_off') { } elseif ($postAction === 'maintenance_off') {
\App\Core\Maintenance::disable(); Maintenance::disable();
Feedback::flash('NOTICE', 'DEFAULT', 'Maintenance mode disabled.', true); Feedback::flash('NOTICE', 'DEFAULT', 'Maintenance mode disabled.', true);
// DB migrations actions // DB migrations actions
} elseif ($postAction === 'migrate_up') { } elseif ($postAction === 'migrate_up') {
$runner = new \App\Core\MigrationRunner($db, $migrationsDir); $runner = new MigrationRunner($db, $migrationsDir);
$applied = $runner->applyPendingMigrations(); $applied = $runner->applyPendingMigrations();
Feedback::flash('NOTICE', 'DEFAULT', empty($applied) ? 'No pending migrations.' : 'Applied migrations: ' . implode(', ', $applied), true); Feedback::flash('NOTICE', 'DEFAULT', empty($applied) ? 'No pending migrations.' : 'Applied migrations: ' . implode(', ', $applied), true);
} elseif ($postAction === 'migrate_apply_one') { } elseif ($postAction === 'migrate_apply_one') {
$runner = new \App\Core\MigrationRunner($db, $migrationsDir); $runner = new MigrationRunner($db, $migrationsDir);
$migrationName = trim($_POST['migration_name'] ?? ''); $migrationName = trim($_POST['migration_name'] ?? '');
$applied = $migrationName !== '' ? $runner->applyMigrationByName($migrationName) : []; $applied = $migrationName !== '' ? $runner->applyMigrationByName($migrationName) : [];
if (empty($applied)) { if (empty($applied)) {
@ -342,11 +348,11 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
$reason = 'Enable required plugins first: ' . implode(', ', $pluginMeta['missing_dependencies']); $reason = 'Enable required plugins first: ' . implode(', ', $pluginMeta['missing_dependencies']);
} }
Feedback::flash('ERROR', 'DEFAULT', $reason, false); Feedback::flash('ERROR', 'DEFAULT', $reason, false);
} elseif (!\App\Core\PluginManager::setEnabled($slug, true)) { } elseif (!PluginManager::setEnabled($slug, true)) {
Feedback::flash('ERROR', 'DEFAULT', 'Failed to enable plugin. Check database connection and error logs.', false); Feedback::flash('ERROR', 'DEFAULT', 'Failed to enable plugin. Check database connection and error logs.', false);
} else { } else {
// Automatically install plugin tables when enabling // Automatically install plugin tables when enabling
$installResult = \App\Core\PluginManager::install($slug); $installResult = PluginManager::install($slug);
if ($installResult) { if ($installResult) {
Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" enabled and installed successfully.', $pluginMeta['name']), true); Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" enabled and installed successfully.', $pluginMeta['name']), true);
} else { } else {
@ -357,7 +363,7 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
if (!$pluginMeta['can_disable']) { if (!$pluginMeta['can_disable']) {
$reason = 'Disable dependent plugins first: ' . implode(', ', $pluginMeta['enabled_dependents']); $reason = 'Disable dependent plugins first: ' . implode(', ', $pluginMeta['enabled_dependents']);
Feedback::flash('ERROR', 'DEFAULT', $reason, false); Feedback::flash('ERROR', 'DEFAULT', $reason, false);
} elseif (!\App\Core\PluginManager::setEnabled($slug, false)) { } elseif (!PluginManager::setEnabled($slug, false)) {
Feedback::flash('ERROR', 'DEFAULT', 'Failed to disable plugin. Check database connection and error logs.', false); Feedback::flash('ERROR', 'DEFAULT', 'Failed to disable plugin. Check database connection and error logs.', false);
} else { } else {
Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" disabled.', $pluginMeta['name']), true); Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" disabled.', $pluginMeta['name']), true);
@ -370,7 +376,7 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
if ($slug === '' || !isset($pluginAdminMap[$slug])) { if ($slug === '' || !isset($pluginAdminMap[$slug])) {
Feedback::flash('ERROR', 'DEFAULT', 'Unknown plugin specified.', false); Feedback::flash('ERROR', 'DEFAULT', 'Unknown plugin specified.', false);
} else { } else {
if (\App\Core\PluginManager::install($slug)) { if (PluginManager::install($slug)) {
Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" installed successfully.', $pluginAdminMap[$slug]['name']), true); Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" installed successfully.', $pluginAdminMap[$slug]['name']), true);
} else { } else {
Feedback::flash('ERROR', 'DEFAULT', 'Plugin installation failed. Check migration files.', false); Feedback::flash('ERROR', 'DEFAULT', 'Plugin installation failed. Check migration files.', false);
@ -382,7 +388,7 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
if ($slug === '' || !isset($pluginAdminMap[$slug])) { if ($slug === '' || !isset($pluginAdminMap[$slug])) {
Feedback::flash('ERROR', 'DEFAULT', 'Unknown plugin specified.', false); Feedback::flash('ERROR', 'DEFAULT', 'Unknown plugin specified.', false);
} else { } else {
if (\App\Core\PluginManager::purge($slug)) { if (PluginManager::purge($slug)) {
Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" purged successfully. All data and tables removed.', $pluginAdminMap[$slug]['name']), true); Feedback::flash('NOTICE', 'DEFAULT', sprintf('Plugin "%s" purged successfully. All data and tables removed.', $pluginAdminMap[$slug]['name']), true);
} else { } else {
Feedback::flash('ERROR', 'DEFAULT', 'Plugin purge failed. Check database permissions.', false); Feedback::flash('ERROR', 'DEFAULT', 'Plugin purge failed. Check database permissions.', false);
@ -471,8 +477,8 @@ if ($postAction !== '' && $postAction !== 'read_migration') {
exit; exit;
} }
$maintenance_enabled = \App\Core\Maintenance::isEnabled(); $maintenance_enabled = Maintenance::isEnabled();
$maintenance_message = \App\Core\Maintenance::getMessage(); $maintenance_message = Maintenance::getMessage();
$pending = []; $pending = [];
$applied = []; $applied = [];
@ -492,7 +498,7 @@ if (isset($_SESSION['migration_modal_open'])) {
} }
try { try {
$runner = new \App\Core\MigrationRunner($db, $migrationsDir); $runner = new MigrationRunner($db, $migrationsDir);
$pending = $runner->listPendingMigrations(); $pending = $runner->listPendingMigrations();
$applied = $runner->listAppliedMigrations(); $applied = $runner->listAppliedMigrations();
@ -602,7 +608,7 @@ if ($queryAction === 'plugin_check_page' && isset($_GET['plugin'])) {
]; ];
// Check database tables // Check database tables
$db = \App\App::db(); $db = App::db();
$pluginOwnedTables = []; $pluginOwnedTables = [];
$pluginReferencedTables = []; $pluginReferencedTables = [];
if ($db && method_exists($db, 'getConnection')) { if ($db && method_exists($db, 'getConnection')) {
@ -685,7 +691,7 @@ if ($queryAction === 'plugin_check_page' && isset($_GET['plugin'])) {
exit; exit;
} }
$overviewPillsPayload = \App\Core\HookDispatcher::applyFilters('admin.overview.pills', [ $overviewPillsPayload = HookDispatcher::applyFilters('admin.overview.pills', [
'pills' => [], 'pills' => [],
'sections' => $sectionRegistry, 'sections' => $sectionRegistry,
'section_state' => $sectionState, 'section_state' => $sectionState,
@ -697,7 +703,7 @@ if (is_array($overviewPillsPayload)) {
$adminOverviewPills = $overviewPillsPayload['pills'] ?? (is_array($overviewPillsPayload) ? $overviewPillsPayload : []); $adminOverviewPills = $overviewPillsPayload['pills'] ?? (is_array($overviewPillsPayload) ? $overviewPillsPayload : []);
} }
$overviewStatusesPayload = \App\Core\HookDispatcher::applyFilters('admin.overview.statuses', [ $overviewStatusesPayload = HookDispatcher::applyFilters('admin.overview.statuses', [
'statuses' => [], 'statuses' => [],
'sections' => $sectionRegistry, 'sections' => $sectionRegistry,
'section_state' => $sectionState, 'section_state' => $sectionState,
@ -709,7 +715,7 @@ if (is_array($overviewStatusesPayload)) {
$adminOverviewStatuses = $overviewStatusesPayload['statuses'] ?? (is_array($overviewStatusesPayload) ? $overviewStatusesPayload : []); $adminOverviewStatuses = $overviewStatusesPayload['statuses'] ?? (is_array($overviewStatusesPayload) ? $overviewStatusesPayload : []);
} }
$adminTabDotsPayload = \App\Core\HookDispatcher::applyFilters('admin.tabs.dot_indicators', [ $adminTabDotsPayload = HookDispatcher::applyFilters('admin.tabs.dot_indicators', [
'dots' => [], 'dots' => [],
'sections' => $sectionRegistry, 'sections' => $sectionRegistry,
'section_state' => $sectionState, 'section_state' => $sectionState,

View File

@ -12,6 +12,8 @@
* - `edit`: Edit user profile details, rights, or avatar. * - `edit`: Edit user profile details, rights, or avatar.
*/ */
use App\Core\HookDispatcher;
require_once '../app/helpers/url_canonicalizer.php'; require_once '../app/helpers/url_canonicalizer.php';
$action = $_REQUEST['action'] ?? ''; $action = $_REQUEST['action'] ?? '';
@ -43,8 +45,8 @@ $profileHooksContext = [
'user' => $userDetails[0] ?? null, 'user' => $userDetails[0] ?? null,
]; ];
if (class_exists('\\App\\Core\\HookDispatcher')) { if (class_exists(HookDispatcher::class)) {
$profileHooksContext = \App\Core\HookDispatcher::applyFilters('profile.context', $profileHooksContext); $profileHooksContext = HookDispatcher::applyFilters('profile.context', $profileHooksContext);
} }
// plugins can add additional panels to the profile page // plugins can add additional panels to the profile page

View File

@ -1,4 +1,7 @@
<?php <?php
use App\Helpers\Theme;
/** /**
* Theme Management Controller * Theme Management Controller
* *
@ -57,7 +60,7 @@ if (isset($_GET['switch_to'])) {
exit(); exit();
} }
if (\App\Helpers\Theme::setCurrentTheme($themeName)) { if (Theme::setCurrentTheme($themeName)) {
// Set success message // Set success message
Feedback::flash('THEME', 'THEME_CHANGED'); Feedback::flash('THEME', 'THEME_CHANGED');
} else { } else {
@ -72,13 +75,13 @@ if (isset($_GET['switch_to'])) {
} }
// Get available themes and current theme for the view // Get available themes and current theme for the view
$themes = \App\Helpers\Theme::getAvailableThemes(); $themes = Theme::getAvailableThemes();
$currentTheme = \App\Helpers\Theme::getCurrentThemeName(); $currentTheme = Theme::getCurrentThemeName();
// Prepare theme data with screenshot URLs and metadata for the view // Prepare theme data with screenshot URLs and metadata for the view
$themeData = []; $themeData = [];
foreach ($themes as $id => $name) { foreach ($themes as $id => $name) {
$meta = \App\Helpers\Theme::getThemeMetadata($id); $meta = Theme::getThemeMetadata($id);
$themeData[$id] = [ $themeData[$id] = [
'name' => $meta['name'] ?? $name, 'name' => $meta['name'] ?? $name,
'description' => $meta['description'] ?? '', 'description' => $meta['description'] ?? '',
@ -89,7 +92,7 @@ foreach ($themes as $id => $name) {
'path' => $meta['path'] ?? '', 'path' => $meta['path'] ?? '',
'last_modified' => $meta['last_modified'] ?? null, 'last_modified' => $meta['last_modified'] ?? null,
'file_count' => $meta['file_count'] ?? null, 'file_count' => $meta['file_count'] ?? null,
'screenshotUrl' => \App\Helpers\Theme::getAssetUrl($id, 'screenshot.png'), 'screenshotUrl' => Theme::getAssetUrl($id, 'screenshot.png'),
'isActive' => $id === $currentTheme 'isActive' => $id === $currentTheme
]; ];
} }

View File

@ -14,9 +14,9 @@
/** @var array $adminOverviewPills */ /** @var array $adminOverviewPills */
/** @var array $adminOverviewStatuses */ /** @var array $adminOverviewStatuses */
/** @var array $sectionState */ /** @var array $sectionState */
?>
<?php use App\App;
$preselectModalId = null; $preselectModalId = null;
if (!empty($modal_to_open)) { if (!empty($modal_to_open)) {
$preselectModalId = 'migrationModal' . md5($modal_to_open); $preselectModalId = 'migrationModal' . md5($modal_to_open);
@ -642,7 +642,7 @@ endif; ?>
]; ];
// Check database tables // Check database tables
$db = \App\App::db(); $db = App::db();
$pluginOwnedTables = []; $pluginOwnedTables = [];
$pluginReferencedTables = []; $pluginReferencedTables = [];
if ($db && method_exists($db, 'getConnection')) { if ($db && method_exists($db, 'getConnection')) {

View File

@ -2,6 +2,9 @@
/** /**
* Maintenance mode page * Maintenance mode page
*/ */
use App\Core\Maintenance;
?> ?>
<div class="container mt-5"> <div class="container mt-5">
<div class="row justify-content-center"> <div class="row justify-content-center">
@ -12,7 +15,7 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<p class="lead">The site is temporarily unavailable due to maintenance.</p> <p class="lead">The site is temporarily unavailable due to maintenance.</p>
<?php $mm = \App\Core\Maintenance::getMessage(); ?> <?php $mm = Maintenance::getMessage(); ?>
<?php if ($mm): ?> <?php if ($mm): ?>
<p class="mb-0"><em><?= htmlspecialchars($mm) ?></em></p> <p class="mb-0"><em><?= htmlspecialchars($mm) ?></em></p>
<?php else: ?> <?php else: ?>

View File

@ -1,5 +1,8 @@
<?php <?php
$navMainDotsPayload = \App\Core\HookDispatcher::applyFilters('nav.main.dot_indicators', [
use App\Core\HookDispatcher;
$navMainDotsPayload = HookDispatcher::applyFilters('nav.main.dot_indicators', [
'dots' => [], 'dots' => [],
'app_root' => $app_root, 'app_root' => $app_root,
'user_id' => $userId ?? 0, 'user_id' => $userId ?? 0,
@ -17,7 +20,7 @@ if (!empty($navMainDots) && is_array($navMainDots)) {
}); });
} }
$navSettingsDotsPayload = \App\Core\HookDispatcher::applyFilters('nav.settings.dot_indicators', [ $navSettingsDotsPayload = HookDispatcher::applyFilters('nav.settings.dot_indicators', [
'dots' => [], 'dots' => [],
'app_root' => $app_root, 'app_root' => $app_root,
'user_id' => $userId ?? 0, 'user_id' => $userId ?? 0,
@ -28,7 +31,7 @@ if (is_array($navSettingsDotsPayload)) {
$navSettingsDots = $navSettingsDotsPayload['dots'] ?? (is_array($navSettingsDotsPayload) ? $navSettingsDotsPayload : []); $navSettingsDots = $navSettingsDotsPayload['dots'] ?? (is_array($navSettingsDotsPayload) ? $navSettingsDotsPayload : []);
} }
$navAccountDotsPayload = \App\Core\HookDispatcher::applyFilters('nav.account.dot_indicators', [ $navAccountDotsPayload = HookDispatcher::applyFilters('nav.account.dot_indicators', [
'dots' => [], 'dots' => [],
'app_root' => $app_root, 'app_root' => $app_root,
'user_id' => $userId ?? 0, 'user_id' => $userId ?? 0,

View File

@ -9,8 +9,9 @@
* - screenshotUrl: URL to the screenshot (or null if not available) * - screenshotUrl: URL to the screenshot (or null if not available)
* - isActive: Whether this is the current theme * - isActive: Whether this is the current theme
*/ */
?>
<?php use App\App;
$activeThemeName = 'Default'; $activeThemeName = 'Default';
foreach ($themes as $themeData) { foreach ($themes as $themeData) {
if (!empty($themeData['isActive'])) { if (!empty($themeData['isActive'])) {
@ -18,7 +19,7 @@ foreach ($themes as $themeData) {
break; break;
} }
} }
$userTimezone = \App\App::get('user_timezone') ?: 'UTC'; $userTimezone = App::get('user_timezone') ?: 'UTC';
$totalThemes = count($themes); $totalThemes = count($themes);
?> ?>

View File

@ -11,6 +11,21 @@
* Version: 0.4.1 * Version: 0.4.1
*/ */
// Load application classes
use App\App;
use App\Core\ConfigLoader;
use App\Core\DatabaseConnector;
use App\Core\HookDispatcher;
use App\Core\LogThrottler;
use App\Core\Maintenance;
use App\Core\MiddlewarePipeline;
use App\Core\MigrationRunner;
use App\Core\NullLogger;
use App\Core\PluginManager;
use App\Core\PluginRouteRegistry;
use App\Core\Router;
use App\Helpers\Theme;
// error reporting, comment out in production // error reporting, comment out in production
//ini_set('display_errors', 1); //ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1); //ini_set('display_startup_errors', 1);
@ -23,9 +38,6 @@ define('APP_PATH', __DIR__ . '/../app/');
require_once APP_PATH . 'core/ConfigLoader.php'; require_once APP_PATH . 'core/ConfigLoader.php';
require_once APP_PATH . 'core/App.php'; require_once APP_PATH . 'core/App.php';
require_once APP_PATH . 'core/PluginRouteRegistry.php'; require_once APP_PATH . 'core/PluginRouteRegistry.php';
use App\Core\ConfigLoader;
use App\App;
use App\Core\PluginRouteRegistry;
// Load the core datetime helper for all user-facing dates/times // Load the core datetime helper for all user-facing dates/times
require_once APP_PATH . 'helpers/datetime.php'; require_once APP_PATH . 'helpers/datetime.php';
@ -57,8 +69,6 @@ App::set('app_root', $app_root);
// Initialize HookDispatcher and plugin system // Initialize HookDispatcher and plugin system
require_once APP_PATH . 'core/HookDispatcher.php'; require_once APP_PATH . 'core/HookDispatcher.php';
require_once APP_PATH . 'core/PluginManager.php'; require_once APP_PATH . 'core/PluginManager.php';
use App\Core\HookDispatcher;
use App\Core\PluginManager;
// Global allowed URLs registration // Global allowed URLs registration
register_hook('filter_allowed_urls', function($urls) { register_hook('filter_allowed_urls', function($urls) {
@ -106,7 +116,6 @@ require_once APP_PATH . 'classes/session.php';
// Initialize themes system after session is started // Initialize themes system after session is started
require_once APP_PATH . 'helpers/theme.php'; require_once APP_PATH . 'helpers/theme.php';
use app\Helpers\Theme;
Session::startSession(); Session::startSession();
@ -119,7 +128,7 @@ if (!isset($page)) {
// Middleware pipeline for security, sanitization & CSRF // Middleware pipeline for security, sanitization & CSRF
require_once APP_PATH . 'core/MiddlewarePipeline.php'; require_once APP_PATH . 'core/MiddlewarePipeline.php';
$pipeline = new \App\Core\MiddlewarePipeline(); $pipeline = new MiddlewarePipeline();
App::set('middleware.pipeline', $pipeline); App::set('middleware.pipeline', $pipeline);
$pipeline->add(function() { $pipeline->add(function() {
// Apply security headers // Apply security headers
@ -143,7 +152,6 @@ require APP_PATH . 'includes/errors.php';
// Connect to DB via DatabaseConnector (before loading plugins so their hooks are available) // Connect to DB via DatabaseConnector (before loading plugins so their hooks are available)
require_once APP_PATH . 'core/DatabaseConnector.php'; require_once APP_PATH . 'core/DatabaseConnector.php';
use App\Core\DatabaseConnector;
$db = DatabaseConnector::connect($config); $db = DatabaseConnector::connect($config);
App::set('db', $db); App::set('db', $db);
@ -178,7 +186,6 @@ $allowed_urls = PluginRouteRegistry::injectAllowedPages($allowed_urls);
// Dispatch routing and auth (after plugins added public/allowed entries) // Dispatch routing and auth (after plugins added public/allowed entries)
require_once APP_PATH . 'core/Router.php'; require_once APP_PATH . 'core/Router.php';
use App\Core\Router;
$currentUser = Router::checkAuth($config, $app_root, $public_pages, $page); $currentUser = Router::checkAuth($config, $app_root, $public_pages, $page);
if ($currentUser === null && $validSession) { if ($currentUser === null && $validSession) {
$currentUser = Session::getUsername(); $currentUser = Session::getUsername();
@ -186,11 +193,9 @@ if ($currentUser === null && $validSession) {
// Initialize Log throttler // Initialize Log throttler
require_once APP_PATH . 'core/LogThrottler.php'; require_once APP_PATH . 'core/LogThrottler.php';
use App\Core\LogThrottler;
// Logging: default to NullLogger, plugin can override // Logging: default to NullLogger, plugin can override
require_once APP_PATH . 'core/NullLogger.php'; require_once APP_PATH . 'core/NullLogger.php';
use App\Core\NullLogger;
$logObject = new NullLogger(); $logObject = new NullLogger();
App::set('logger', $logObject); App::set('logger', $logObject);
@ -218,7 +223,7 @@ try {
$migrationsDir = APP_PATH . '../doc/database/migrations'; $migrationsDir = APP_PATH . '../doc/database/migrations';
if (is_dir($migrationsDir) && $userId !== null && $page !== 'login') { if (is_dir($migrationsDir) && $userId !== null && $page !== 'login') {
require_once APP_PATH . 'core/MigrationRunner.php'; require_once APP_PATH . 'core/MigrationRunner.php';
$runner = new \App\Core\MigrationRunner($db, $migrationsDir); $runner = new MigrationRunner($db, $migrationsDir);
if ($runner->hasPendingMigrations()) { if ($runner->hasPendingMigrations()) {
$pending = $runner->listPendingMigrations(); $pending = $runner->listPendingMigrations();
$msg = 'Database schema is out of date. There are pending migrations. Run "<code>php scripts/migrate.php up</code>" or use the <a href="?page=admin&section=migrations">Admin center</a>'; $msg = 'Database schema is out of date. There are pending migrations. Run "<code>php scripts/migrate.php up</code>" or use the <a href="?page=admin&section=migrations">Admin center</a>';
@ -276,7 +281,7 @@ if (!$pipeline->run()) {
// Maintenance mode: show maintenance page to non-superusers // Maintenance mode: show maintenance page to non-superusers
try { try {
require_once APP_PATH . 'core/Maintenance.php'; require_once APP_PATH . 'core/Maintenance.php';
if (\App\Core\Maintenance::isEnabled()) { if (Maintenance::isEnabled()) {
$isSuperuser = false; $isSuperuser = false;
if ($validSession && isset($userId) && isset($userObject) && method_exists($userObject, 'hasRight')) { if ($validSession && isset($userId) && isset($userObject) && method_exists($userObject, 'hasRight')) {
// user 1 is always superuser per implementation, but also check explicit right // user 1 is always superuser per implementation, but also check explicit right
@ -287,15 +292,15 @@ try {
// Advise clients to retry after 10 minutes (600 seconds; configure here) // Advise clients to retry after 10 minutes (600 seconds; configure here)
header('Retry-After: 600'); header('Retry-After: 600');
// Show themed maintenance page // Show themed maintenance page
\App\Helpers\Theme::include('page-header'); Theme::include('page-header');
\App\Helpers\Theme::include('page-menu'); Theme::include('page-menu');
include APP_PATH . 'templates/maintenance.php'; include APP_PATH . 'templates/maintenance.php';
\App\Helpers\Theme::include('page-footer'); Theme::include('page-footer');
ob_end_flush(); ob_end_flush();
exit; exit;
} else { } else {
// Superusers bypass maintenance; show a small banner // Superusers bypass maintenance; show a small banner
$maintMsg = \App\Core\Maintenance::getMessage(); $maintMsg = Maintenance::getMessage();
$custom = 'Maintenance mode is enabled.'; $custom = 'Maintenance mode is enabled.';
if (!empty($maintMsg)) { if (!empty($maintMsg)) {
$custom .= ' <em>' . htmlspecialchars($maintMsg) . '</em>'; $custom .= ' <em>' . htmlspecialchars($maintMsg) . '</em>';
@ -314,7 +319,7 @@ if ($validSession && isset($userId) && isset($userObject) && is_object($userObje
try { try {
$dbTheme = $userObject->getUserTheme((int)$userId); $dbTheme = $userObject->getUserTheme((int)$userId);
if ($dbTheme) { if ($dbTheme) {
\App\Helpers\Theme::setCurrentTheme($dbTheme, false); Theme::setCurrentTheme($dbTheme, false);
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
// Non-fatal if theme load fails // Non-fatal if theme load fails
@ -357,10 +362,10 @@ if ($page == 'logout') {
Feedback::flash('LOGIN', 'LOGOUT_SUCCESS'); Feedback::flash('LOGIN', 'LOGOUT_SUCCESS');
// Use theme helper to include templates // Use theme helper to include templates
\App\Helpers\Theme::include('page-header'); Theme::include('page-header');
\App\Helpers\Theme::include('page-menu'); Theme::include('page-menu');
include APP_PATH . 'pages/login.php'; include APP_PATH . 'pages/login.php';
\App\Helpers\Theme::include('page-footer'); Theme::include('page-footer');
} else { } else {
// if user is logged in, we need user details and rights // if user is logged in, we need user details and rights
@ -444,36 +449,36 @@ if ($page == 'logout') {
ob_end_flush(); ob_end_flush();
exit; exit;
} else { } else {
\App\Helpers\Theme::include('page-header'); Theme::include('page-header');
\App\Helpers\Theme::include('page-menu'); Theme::include('page-menu');
if ($validSession) { if ($validSession) {
\App\Helpers\Theme::include('page-sidebar'); Theme::include('page-sidebar');
} }
PluginRouteRegistry::dispatch($page, $routeContext); PluginRouteRegistry::dispatch($page, $routeContext);
\App\Helpers\Theme::include('page-footer'); Theme::include('page-footer');
} }
} elseif (in_array($page, $allowed_urls)) { } elseif (in_array($page, $allowed_urls)) {
// The page is from a core controller // The page is from a core controller
\App\Helpers\Theme::include('page-header'); Theme::include('page-header');
\App\Helpers\Theme::include('page-menu'); Theme::include('page-menu');
if ($validSession) { if ($validSession) {
\App\Helpers\Theme::include('page-sidebar'); Theme::include('page-sidebar');
} }
if (file_exists(APP_PATH . "pages/{$page}.php")) { if (file_exists(APP_PATH . "pages/{$page}.php")) {
include APP_PATH . "pages/{$page}.php"; include APP_PATH . "pages/{$page}.php";
} else { } else {
include APP_PATH . 'templates/error-notfound.php'; include APP_PATH . 'templates/error-notfound.php';
} }
\App\Helpers\Theme::include('page-footer'); Theme::include('page-footer');
} else { } else {
// The page is not in allowed URLs // The page is not in allowed URLs
\App\Helpers\Theme::include('page-header'); Theme::include('page-header');
\App\Helpers\Theme::include('page-menu'); Theme::include('page-menu');
if ($validSession) { if ($validSession) {
\App\Helpers\Theme::include('page-sidebar'); Theme::include('page-sidebar');
} }
include APP_PATH . 'templates/error-notfound.php'; include APP_PATH . 'templates/error-notfound.php';
\App\Helpers\Theme::include('page-footer'); Theme::include('page-footer');
} }
} }

View File

@ -1,3 +1,7 @@
<?php
use App\Helpers\Theme;
?>
<!-- Modern Theme Footer --> <!-- Modern Theme Footer -->
<footer class="footer mt-5 py-3 bg-light"> <footer class="footer mt-5 py-3 bg-light">
<div class="container"> <div class="container">
@ -17,7 +21,7 @@
</footer> </footer>
<!-- Theme-specific JavaScript --> <!-- Theme-specific JavaScript -->
<script src="<?= \App\Helpers\Theme::asset('js/theme.js') ?>"></script> <script src="<?= Theme::asset('js/theme.js') ?>"></script>
<!-- Global site scripts --> <!-- Global site scripts -->
<script src="<?= htmlspecialchars($app_root) ?>static/js/messages.js"></script> <script src="<?= htmlspecialchars($app_root) ?>static/js/messages.js"></script>

View File

@ -1,3 +1,7 @@
<?php
use App\Helpers\Theme;
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -15,7 +19,7 @@
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css"> <link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css">
<!-- Theme-specific CSS --> <!-- Theme-specific CSS -->
<link rel="stylesheet" type="text/css" href="<?= \App\Helpers\Theme::asset('css/theme.css') ?>"> <link rel="stylesheet" type="text/css" href="<?= Theme::asset('css/theme.css') ?>">
<!-- jQuery --> <!-- jQuery -->
<script src="<?= htmlspecialchars($app_root) ?>static/libs/jquery/jquery.min.js"></script> <script src="<?= htmlspecialchars($app_root) ?>static/libs/jquery/jquery.min.js"></script>

View File

@ -1,3 +1,7 @@
<?php
use App\Helpers\Theme;
?>
<!-- Retro Theme Footer --> <!-- Retro Theme Footer -->
<footer class="footer mt-5 py-3 bg-light"> <footer class="footer mt-5 py-3 bg-light">
<div class="container"> <div class="container">
@ -17,7 +21,7 @@
</footer> </footer>
<!-- Theme-specific JavaScript --> <!-- Theme-specific JavaScript -->
<script src="<?= \App\Helpers\Theme::asset('js/theme.js') ?>"></script> <script src="<?= Theme::asset('js/theme.js') ?>"></script>
<!-- Global site scripts --> <!-- Global site scripts -->
<script src="<?= htmlspecialchars($app_root) ?>static/js/messages.js"></script> <script src="<?= htmlspecialchars($app_root) ?>static/js/messages.js"></script>

View File

@ -1,3 +1,7 @@
<?php
use App\Helpers\Theme;
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -15,7 +19,7 @@
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css"> <link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css">
<!-- Theme-specific CSS --> <!-- Theme-specific CSS -->
<link rel="stylesheet" type="text/css" href="<?= \App\Helpers\Theme::asset('css/theme.css') ?>"> <link rel="stylesheet" type="text/css" href="<?= Theme::asset('css/theme.css') ?>">
<!-- jQuery --> <!-- jQuery -->
<script src="<?= htmlspecialchars($app_root) ?>static/libs/jquery/jquery.min.js"></script> <script src="<?= htmlspecialchars($app_root) ?>static/libs/jquery/jquery.min.js"></script>