Compare commits
5 Commits
bccd48014b
...
ed0baf18d3
Author | SHA1 | Date |
---|---|---|
|
ed0baf18d3 | |
|
8628985361 | |
|
facddb0d6d | |
|
9797caa58e | |
|
9c896d9e0e |
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class HookDispatcher
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Stores all registered hooks and their callbacks.
|
||||||
|
* @var array<string, array<callable>>
|
||||||
|
*/
|
||||||
|
private static array $hooks = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a callback for a given hook.
|
||||||
|
*/
|
||||||
|
public static function register(string $hook, callable $callback): void
|
||||||
|
{
|
||||||
|
if (!isset(self::$hooks[$hook])) {
|
||||||
|
self::$hooks[$hook] = [];
|
||||||
|
}
|
||||||
|
self::$hooks[$hook][] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch all callbacks for the specified hook.
|
||||||
|
*/
|
||||||
|
public static function dispatch(string $hook, array $context = []): void
|
||||||
|
{
|
||||||
|
if (!empty(self::$hooks[$hook])) {
|
||||||
|
foreach (self::$hooks[$hook] as $callback) {
|
||||||
|
call_user_func($callback, $context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply filters for a hook key, passing a value through all callbacks.
|
||||||
|
* Each callback should accept the value and return a modified value.
|
||||||
|
*
|
||||||
|
* @param string $hook
|
||||||
|
* @param mixed $value
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public static function applyFilters(string $hook, $value)
|
||||||
|
{
|
||||||
|
if (!empty(self::$hooks[$hook])) {
|
||||||
|
foreach (self::$hooks[$hook] as $callback) {
|
||||||
|
$value = call_user_func($callback, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class PluginManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Loads all enabled plugins from the given directory.
|
||||||
|
*
|
||||||
|
* @param string $pluginsDir
|
||||||
|
* @return array<string, array{path: string, meta: array}>
|
||||||
|
*/
|
||||||
|
public static function load(string $pluginsDir): array
|
||||||
|
{
|
||||||
|
$enabled = [];
|
||||||
|
foreach (glob($pluginsDir . '*', GLOB_ONLYDIR) as $pluginPath) {
|
||||||
|
$manifest = $pluginPath . '/plugin.json';
|
||||||
|
if (!file_exists($manifest)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$meta = json_decode(file_get_contents($manifest), true);
|
||||||
|
if (empty($meta['enabled'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = basename($pluginPath);
|
||||||
|
$enabled[$name] = [
|
||||||
|
'path' => $pluginPath,
|
||||||
|
'meta' => $meta,
|
||||||
|
];
|
||||||
|
$bootstrap = $pluginPath . '/bootstrap.php';
|
||||||
|
if (file_exists($bootstrap)) {
|
||||||
|
include_once $bootstrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $enabled;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use Session;
|
||||||
|
use Feedback;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
/**
|
||||||
|
* Check session validity and handle redirection for protected pages.
|
||||||
|
* Returns current username if session is valid, null otherwise.
|
||||||
|
*/
|
||||||
|
public static function checkAuth(array $config, string $app_root, array $public_pages, string $page): ?string {
|
||||||
|
$validSession = Session::isValidSession();
|
||||||
|
if ($validSession) {
|
||||||
|
return Session::getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($page, $public_pages, true)) {
|
||||||
|
// flash session timeout if needed
|
||||||
|
if (isset($_SESSION['LAST_ACTIVITY']) && !isset($_SESSION['session_timeout_shown'])) {
|
||||||
|
Feedback::flash('LOGIN', 'SESSION_TIMEOUT');
|
||||||
|
$_SESSION['session_timeout_shown'] = true;
|
||||||
|
}
|
||||||
|
// preserve flash messages
|
||||||
|
$flash_messages = $_SESSION['flash_messages'] ?? [];
|
||||||
|
Session::cleanup($config);
|
||||||
|
$_SESSION['flash_messages'] = $flash_messages;
|
||||||
|
|
||||||
|
// build login URL
|
||||||
|
$loginUrl = $app_root . '?page=login';
|
||||||
|
$trimmed = trim($page, '/?');
|
||||||
|
if (!in_array($trimmed, INVALID_REDIRECT_PAGES, true)) {
|
||||||
|
$loginUrl .= '&redirect=' . urlencode($_SERVER['REQUEST_URI']);
|
||||||
|
}
|
||||||
|
header('Location: ' . $loginUrl);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,43 +12,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Preparing plugins and hooks
|
// Preparing plugins and hooks
|
||||||
$GLOBALS['plugin_hooks'] = [];
|
// Initialize HookDispatcher and plugin system
|
||||||
$enabled_plugins = [];
|
require_once __DIR__ . '/../app/core/HookDispatcher.php';
|
||||||
|
require_once __DIR__ . '/../app/core/PluginManager.php';
|
||||||
|
use App\Core\HookDispatcher;
|
||||||
|
use App\Core\PluginManager;
|
||||||
|
|
||||||
// Plugin discovery
|
// Hook registration and dispatch helpers
|
||||||
|
function register_hook(string $hook, callable $callback): void {
|
||||||
|
HookDispatcher::register($hook, $callback);
|
||||||
|
}
|
||||||
|
function do_hook(string $hook, array $context = []): void {
|
||||||
|
HookDispatcher::dispatch($hook, $context);
|
||||||
|
}
|
||||||
|
function filter_public_pages(array $pages): array {
|
||||||
|
return HookDispatcher::applyFilters('filter_public_pages', $pages);
|
||||||
|
}
|
||||||
|
function filter_allowed_urls(array $urls): array {
|
||||||
|
return HookDispatcher::applyFilters('filter_allowed_urls', $urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load enabled plugins
|
||||||
$plugins_dir = dirname(__DIR__) . '/plugins/';
|
$plugins_dir = dirname(__DIR__) . '/plugins/';
|
||||||
foreach (glob($plugins_dir . '*', GLOB_ONLYDIR) as $plugin_path) {
|
$enabled_plugins = PluginManager::load($plugins_dir);
|
||||||
$manifest = $plugin_path . '/plugin.json';
|
|
||||||
if (file_exists($manifest)) {
|
|
||||||
$meta = json_decode(file_get_contents($manifest), true);
|
|
||||||
if (!empty($meta['enabled'])) {
|
|
||||||
$plugin_name = basename($plugin_path);
|
|
||||||
$enabled_plugins[$plugin_name] = [
|
|
||||||
'path' => $plugin_path,
|
|
||||||
'meta' => $meta
|
|
||||||
];
|
|
||||||
// Autoload plugin bootstrap if exists
|
|
||||||
$bootstrap = $plugin_path . '/bootstrap.php';
|
|
||||||
if (file_exists($bootstrap)) {
|
|
||||||
include_once $bootstrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$GLOBALS['enabled_plugins'] = $enabled_plugins;
|
$GLOBALS['enabled_plugins'] = $enabled_plugins;
|
||||||
|
|
||||||
// Simple hook system
|
|
||||||
function register_hook($hook, $callback) {
|
|
||||||
$GLOBALS['plugin_hooks'][$hook][] = $callback;
|
|
||||||
}
|
|
||||||
function do_hook($hook, $context = []) {
|
|
||||||
if (!empty($GLOBALS['plugin_hooks'][$hook])) {
|
|
||||||
foreach ($GLOBALS['plugin_hooks'][$hook] as $callback) {
|
|
||||||
call_user_func($callback, $context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define CSRF token include path globally
|
// Define CSRF token include path globally
|
||||||
if (!defined('CSRF_TOKEN_INCLUDE')) {
|
if (!defined('CSRF_TOKEN_INCLUDE')) {
|
||||||
define('CSRF_TOKEN_INCLUDE', dirname(__DIR__) . '/app/includes/csrf_token.php');
|
define('CSRF_TOKEN_INCLUDE', dirname(__DIR__) . '/app/includes/csrf_token.php');
|
||||||
|
@ -121,14 +109,6 @@ $allowed_urls = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// Let plugins filter/extend allowed_urls
|
// Let plugins filter/extend allowed_urls
|
||||||
function filter_allowed_urls($urls) {
|
|
||||||
if (!empty($GLOBALS['plugin_hooks']['filter_allowed_urls'])) {
|
|
||||||
foreach ($GLOBALS['plugin_hooks']['filter_allowed_urls'] as $callback) {
|
|
||||||
$urls = call_user_func($callback, $urls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $urls;
|
|
||||||
}
|
|
||||||
$allowed_urls = filter_allowed_urls($allowed_urls);
|
$allowed_urls = filter_allowed_urls($allowed_urls);
|
||||||
|
|
||||||
// cnfig file
|
// cnfig file
|
||||||
|
@ -161,41 +141,11 @@ $app_root = $config['folder'];
|
||||||
$public_pages = ['login', 'help', 'about'];
|
$public_pages = ['login', 'help', 'about'];
|
||||||
|
|
||||||
// Let plugins filter/extend public_pages
|
// Let plugins filter/extend public_pages
|
||||||
function filter_public_pages($pages) {
|
|
||||||
if (!empty($GLOBALS['plugin_hooks']['filter_public_pages'])) {
|
|
||||||
foreach ($GLOBALS['plugin_hooks']['filter_public_pages'] as $callback) {
|
|
||||||
$pages = call_user_func($callback, $pages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $pages;
|
|
||||||
}
|
|
||||||
$public_pages = filter_public_pages($public_pages);
|
$public_pages = filter_public_pages($public_pages);
|
||||||
|
|
||||||
// Check session and redirect if needed
|
// Dispatch routing and auth
|
||||||
$currentUser = null;
|
require_once __DIR__ . '/../app/core/Router.php';
|
||||||
if ($validSession) {
|
$currentUser = \App\Core\Router::checkAuth($config, $app_root, $public_pages, $page);
|
||||||
// Session is OK
|
|
||||||
$currentUser = Session::getUsername();
|
|
||||||
} else if (!in_array($page, $public_pages)) {
|
|
||||||
// Session expired/invalid, page needs login
|
|
||||||
if (isset($_SESSION['LAST_ACTIVITY']) && !isset($_SESSION['session_timeout_shown'])) {
|
|
||||||
// Only show session timeout message if there was an active session
|
|
||||||
// and we haven't shown it yet
|
|
||||||
Feedback::flash('LOGIN', 'SESSION_TIMEOUT');
|
|
||||||
$_SESSION['session_timeout_shown'] = true;
|
|
||||||
// Cleanup session but keep flash messages
|
|
||||||
$flash_messages = $_SESSION['flash_messages'] ?? [];
|
|
||||||
Session::cleanup($config);
|
|
||||||
$_SESSION['flash_messages'] = $flash_messages;
|
|
||||||
}
|
|
||||||
$loginUrl = $app_root . '?page=login';
|
|
||||||
$trimmed = trim($page, '/?');
|
|
||||||
if (!in_array($trimmed, INVALID_REDIRECT_PAGES, true)) {
|
|
||||||
$loginUrl .= '&redirect=' . urlencode($_SERVER['REQUEST_URI']);
|
|
||||||
}
|
|
||||||
header('Location: ' . $loginUrl);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect to db of Jilo Web
|
// connect to db of Jilo Web
|
||||||
require '../app/classes/database.php';
|
require '../app/classes/database.php';
|
||||||
|
|
Loading…
Reference in New Issue