myaac/system/router.php
slawkens f3745a2752
Feature/new router (#165)
* Remove unneeded escape

* Fix guild back buttons (change logo & motd)

* small adjustment in news.php

* Fix create character when admin (any case is allowed now)

* Fix forum table style (boards & thread view)

* Small improvement to plugins.enabled check

* [WIP] nikic/fast-route implementation

I will describe it more in Pull Request

* Optimisations & fixes.

* Fix path - should not be absolute

* Add PLUGINS to Twig path

* Don't hide "Install Plugin" Box by default

* Update package-lock.json

* nothing important, just early exit & fixes

Fix creature display

* fix premium_ends_at for tfs 1.3+

* Move pages

* Move pages tbc

* $db->select: make $where parameter optional, allows to get all records

* Add some error box to error

* fix parse error

* Rewriting the router v2

To be more flexible

* small fixes

* fix & add admin icons

* Move mass_* pages to correct folder

* fix logout hook 2

* Delete accountmanagement.php

* This code wasn't used

* Add missing var

* Add redirect_from && redirect_to to router options

+ Also add * for all methods shortcut

* Remove comments

Not allowed in normal json

* Allow admin pages included into plugins dir

* block access to some files

* Fix admin logout

* Fix #178

* feature: mail confirmed reward

Suggested by @EPuncker

# Conflicts:
#	system/hooks.php

* remove misleading comment

* adjust required version according to composer.json

* fix duplicated word

* Adjustments & fixed to mass actions

* Add password confirm, and change text type to password

* Add list of Open Source Software MyAAC is using

* Fix signature

* Show First, Second instead of numbers

* fix base dir detection

* fix double ACTION define + undefined URI in template

* new function> escapeHtml + fix css in admin menus

* fix changelog add

* fix news adding, rename const to NEWS_*

* Add verify to pages, add messages, limits, fix add

* fix "Please fill all input"

* add required input to admin pages

* shorten some expressions with ??

* shorten code + fix conversion (int)

* Move account_types to config, account.web_flags to common.php

* Update example.json

* feature: router aliases

* shorten some code + const convert

* remove wrong char

* fix signature on custom basedir

* fix: mass teleport position validation (#214)

* fix: mass teleport position validation

* fix: max position

* Fix execute in CLI

* fix warning in reload cache in dev mode

* Configurable admin panel folder

* feature: plugin require more options with comma

* $config_account_salt -> USE_ACCOUNT_SALT

* fix forum show_thread

* Update show_thread.php

---------

Co-authored-by: Gabriel Pedro <gpedro@users.noreply.github.com>
2023-02-07 11:41:05 +01:00

304 lines
6.8 KiB
PHP

<?php
/**
* Router
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2021 MyAAC
* @link https://my-aac.org
*/
if(!isset($content[0]))
$content = '';
// check if site has been closed
$load_it = true;
$site_closed = false;
if(fetchDatabaseConfig('site_closed', $site_closed)) {
$site_closed = ($site_closed == 1);
if($site_closed) {
if(!admin())
{
$title = getDatabaseConfig('site_closed_title');
$content .= '<p class="note">' . getDatabaseConfig('site_closed_message') . '</p><br/>';
$load_it = false;
}
if(!$logged)
{
ob_start();
require SYSTEM . 'pages/account/manage.php';
$content .= ob_get_contents();
ob_end_clean();
$load_it = false;
}
}
}
define('SITE_CLOSED', $site_closed);
// Strip query string (?foo=bar) and decode URI
/** @var string $uri */
if (false !== $pos = strpos($uri, '?')) {
if ($pos !== 1) {
$uri = substr($uri, 0, $pos);
}
else {
$uri = str_replace_first('?', '', $uri);
}
}
$uri = rawurldecode($uri);
if (BASE_DIR !== '') {
$tmp = str_replace_first('/', '', BASE_DIR);
$uri = str_replace_first($tmp . '/', '', $uri);
}
define('URI', $uri);
/** @var boolean $load_it */
if(!$load_it) {
// ignore warnings in some functions/plugins
// page is not loaded anyways
define('PAGE', '');
return;
}
/** @var string $content */
if(SITE_CLOSED && admin())
$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>';
$ignore = false;
/** @var boolean $logged */
/** @var OTS_Account $account_logged */
$logged_access = 1;
if($logged && $account_logged && $account_logged->isLoaded()) {
$logged_access = $account_logged->getAccess();
}
/**
* Routes loading
*/
$dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) {
$routes = require SYSTEM . 'routes.php';
$isAlreadyDefined = [];
$routesTmp = [];
foreach(getDatabasePages() as $page) {
$isAlreadyDefined[$page] = true;
$routesTmp[] = ['*', $page, '__database__/' . $page, true];
}
Plugins::clearWarnings();
foreach (Plugins::getRoutes() as $route) {
if(!isset($isAlreadyDefined[$route[1]])) {
$isAlreadyDefined[$route[1]] = true;
$routesTmp[] = [$route[0], $route[1], $route[2]];
}
}
foreach ($routes as $route) {
if(!isset($isAlreadyDefined[$route[1]])) {
if (strpos($route[2], '__redirect__') === false && strpos($route[2], '__database__') === false) {
$routesTmp[] = [$route[0], $route[1], 'system/pages/' . $route[2]];
}
else {
$routesTmp[] = [$route[0], $route[1], $route[2]];
}
}
}
//var_dump($routesTmp);
foreach ($routesTmp as $route) {
if ($route[0] === '*') {
$route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];
}
$aliases = [
[':int', ':string', ':alphanum'],
[':\d+', ':[A-Za-z0-9-_%+\']+}', ':[A-Za-z0-9]+'],
];
// apply aliases
$route[1] = str_replace($aliases[0], $aliases[1], $route[1]);
$r->addRoute($route[0], $route[1], $route[2]);
}
if (config('env') === 'dev') {
foreach(Plugins::getWarnings() as $warning) {
log_append('router.log', $warning);
}
}
},
[
'cacheFile' => CACHE . 'route.cache',
'cacheDisabled' => config('env') === 'dev',
]
);
// Fetch method and URI
$httpMethod = $_SERVER['REQUEST_METHOD'];
$found = true;
// old support for pages like /?subtopic=accountmanagement
$page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? '');
if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) {
if (config('backward_support')) {
require SYSTEM . 'compat/pages.php';
}
$file = loadPageFromFileSystem($page, $found);
if(!$found) {
$file = false;
}
}
else {
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
// ... 404 Not Found
//var_dump('not found');
/**
* Fallback to load page from templates/ or system/pages/ directory
*/
$page = $uri;
if (preg_match('/^[A-z0-9\/\-]+$/', $page)) {
$file = loadPageFromFileSystem($page, $found);
} else {
$found = false;
}
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
// ... 405 Method Not Allowed
$page = '405';
$allowedMethods = $routeInfo[1];
$file = SYSTEM . 'pages/405.php';
break;
case FastRoute\Dispatcher::FOUND:
$path = $routeInfo[1];
$vars = $routeInfo[2];
$_REQUEST = array_merge($_REQUEST, $vars);
$_GET = array_merge($_GET, $vars);
if (strpos($path, '__database__/') !== false) {
$pageName = str_replace('__database__/', '', $path);
$success = false;
$tmp_content = getCustomPage($pageName, $success);
if ($success) {
$content .= $tmp_content;
if (hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) {
$pageInfo = getCustomPageInfo($pageName);
$content = $twig->render('admin.pages.links.html.twig', array(
'page' => array('id' => $pageInfo !== null ? $pageInfo['id'] : 0, 'hidden' => $pageInfo !== null ? $pageInfo['hidden'] : '0')
)) . $content;
}
$page = $pageName;
$file = false;
}
} else if (strpos($path, '__redirect__/') !== false) {
$path = str_replace('__redirect__/', '', $path);
header('Location: ' . BASE_URL . $path);
exit;
} else {
// parse for define PAGE
$tmp = BASE_DIR;
$uri = $_SERVER['REQUEST_URI'];
if (!empty($tmp)) {
$uri = str_replace(BASE_DIR . '/', '', $uri);
}
if (false !== $pos = strpos($uri, '?')) {
$uri = substr($uri, 0, $pos);
}
if (0 === strpos($uri, '/')) {
$uri = str_replace_first('/', '', $uri);
}
$page = $uri;
$file = BASE . $path;
}
unset($tmp, $uri);
break;
}
}
if (!$found) {
$page = '404';
$file = SYSTEM . 'pages/404.php';
}
define('PAGE', $page);
ob_start();
if($hooks->trigger(HOOK_BEFORE_PAGE)) {
if(!$ignore && $file !== false)
require $file;
}
unset($file);
if(config('backward_support') && isset($main_content[0]))
$content .= $main_content;
$content .= ob_get_contents();
ob_end_clean();
$hooks->trigger(HOOK_AFTER_PAGE);
if(!isset($title)) {
$title = ucfirst($page);
}
if(config('backward_support')) {
$main_content = $content;
$topic = $title;
}
unset($page);
function getDatabasePages() {
global $db;
$pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages');
$ret = [];
if ($pages->rowCount() < 1) {
return $ret;
}
foreach($pages->fetchAll() as $page) {
$ret [] = $page['name'];
}
return $ret;
}
function loadPageFromFileSystem($page, &$found) {
$file = SYSTEM . 'pages/' . $page . '.php';
if (!is_file($file)) {
// feature: convert camelCase to snake_case
// so instead of forum/move_thread
// we can write: forum/moveThread
$file = SYSTEM . 'pages/' . camelCaseToUnderscore($page) . '.php';
if (!is_file($file)) {
// feature: load pages from templates/ dir
global $template_path;
$file = $template_path . '/pages/' . $page . '.php';
if (!is_file($file)) {
$found = false;
}
}
}
return $file;
}