From 60f64e21d8a2e5b7fcd48ca5049d1e933e60eea3 Mon Sep 17 00:00:00 2001 From: slawkens Date: Mon, 28 Nov 2022 21:37:25 +0100 Subject: [PATCH] Rewriting the router v2 To be more flexible --- admin/index.php | 3 - index.php | 51 +-- plugins/example.json | 8 +- system/compat/pages.php | 4 + system/functions.php | 26 +- system/init.php | 4 + .../{newsarchive.php => news/archive.php} | 3 +- system/router.php | 320 +++++++++++------- system/routes.php | 10 +- system/templates/online.html.twig | 8 +- templates/tibiacom/index.php | 2 +- 11 files changed, 245 insertions(+), 194 deletions(-) rename system/pages/{newsarchive.php => news/archive.php} (86%) diff --git a/admin/index.php b/admin/index.php index bfc42cf8..7ef1c31f 100644 --- a/admin/index.php +++ b/admin/index.php @@ -52,9 +52,6 @@ if(!$logged || !admin()) { $page = 'login'; } -// more pages have action, lets define it here -$action = $_REQUEST['action'] ?? ''; - // include our page $file = BASE . 'admin/pages/' . $page . '.php'; if(!@file_exists($file)) { diff --git a/index.php b/index.php index 234e98dc..693d90d7 100644 --- a/index.php +++ b/index.php @@ -28,19 +28,13 @@ require_once 'common.php'; require_once SYSTEM . 'functions.php'; $uri = $_SERVER['REQUEST_URI']; - -$tmp = BASE_DIR; -if(!empty($tmp)) - $uri = str_replace(BASE_DIR . '/', '', $uri); -else - $uri = str_replace_first('/', '', $uri); - -$uri = str_replace_first('index.php', '', $uri); -if(($pos = strpos($uri, '?') === 0) || $pos == 9) { - $uri = str_replace_first('?', '', $uri); +if(false !== strpos($uri, 'index.php')) { + $uri = str_replace_first('/index.php', '', $uri); } -define('URI', $uri); +if(0 === strpos($uri, '/')) { + $uri = str_replace_first('/', '', $uri); +} if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) { $tmp = explode('.', $uri); @@ -51,7 +45,7 @@ if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) { exit(); } -if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|php|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { +if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { http_response_code(404); exit; } @@ -98,6 +92,8 @@ require_once SYSTEM . 'status.php'; $twig->addGlobal('config', $config); $twig->addGlobal('status', $status); +require_once SYSTEM . 'router.php'; + require SYSTEM . 'migrate.php'; $hooks->trigger(HOOK_STARTUP); @@ -146,35 +142,6 @@ if($config['visitors_counter']) $visitors = new Visitors($config['visitors_counter_ttl']); } -// page content loading -if(!isset($content[0])) - $content = ''; -$load_it = true; - -// check if site has been closed -$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 .= '

' . getDatabaseConfig('site_closed_message') . '


'; - $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); - // backward support for gesior if($config['backward_support']) { define('INITIALIZED', true); @@ -212,8 +179,6 @@ if($config['backward_support']) { $config['status']['serverStatus_' . $key] = $value; } -require SYSTEM . 'router.php'; - /** * @var OTS_Account $account_logged */ diff --git a/plugins/example.json b/plugins/example.json index 63b27e1b..f19280d2 100644 --- a/plugins/example.json +++ b/plugins/example.json @@ -40,11 +40,9 @@ "method": "GET", "priority": "130" }, - "Second Route": { - "pattern": "/YourSecondRoute", - "file": "plugins/your-plugin/your-awesome-page-two.php", - "method": "GET,POST", - "priority": "120" + "Redirect Example": { + "pattern": "/redirectExample", + "file": "account/manage" } } } diff --git a/system/compat/pages.php b/system/compat/pages.php index 9c174945..3f5d0a0a 100644 --- a/system/compat/pages.php +++ b/system/compat/pages.php @@ -30,6 +30,10 @@ switch($page) $page = 'news'; break; + case 'newsarchive': + $page = 'news/archive'; + break; + case 'tibiarules': $page = 'rules'; break; diff --git a/system/functions.php b/system/functions.php index 0811d054..747ab513 100644 --- a/system/functions.php +++ b/system/functions.php @@ -62,20 +62,20 @@ function getFullLink($page, $name, $blank = false) { function getLink($page, $action = null) { global $config; - return BASE_URL . ($config['friendly_urls'] ? '' : '?') . $page . ($action ? '/' . $action : ''); + return BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . $page . ($action ? '/' . $action : ''); } function internalLayoutLink($page, $action = null) {return getLink($page, $action);} function getForumThreadLink($thread_id, $page = NULL) { global $config; - return BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'forum/thread/' . (int)$thread_id . (isset($page) ? '/' . $page : ''); + return BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'forum/thread/' . (int)$thread_id . (isset($page) ? '/' . $page : ''); } function getForumBoardLink($board_id, $page = NULL) { global $config; - return BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'forum/board/' . (int)$board_id . (isset($page) ? '/' . $page : ''); + return BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'forum/board/' . (int)$board_id . (isset($page) ? '/' . $page : ''); } function getPlayerLink($name, $generate = true) @@ -90,7 +90,7 @@ function getPlayerLink($name, $generate = true) $name = $player->getName(); } - $url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'characters/' . urlencode($name); + $url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'characters/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -100,7 +100,7 @@ function getMonsterLink($name, $generate = true) { global $config; - $url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'creatures/' . urlencode($name); + $url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'creatures/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -118,7 +118,7 @@ function getHouseLink($name, $generate = true) $name = $house->fetchColumn(); } - $url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'houses/' . urlencode($name); + $url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'houses/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -136,7 +136,7 @@ function getGuildLink($name, $generate = true) $name = $guild->fetchColumn(); } - $url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'guilds/' . urlencode($name); + $url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'guilds/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -268,6 +268,13 @@ function getForumBoards() return array(); } +// TODO: +// convert forum threads links from just forum/ID +// INTO: forum/thread-name-id, like in XenForo +//function convertForumThreadTitle($title) { +// return str_replace(' ', '-', strtolower($title)); +//} + /** * Retrieves data from myaac database config. * @@ -1493,6 +1500,11 @@ function getAccountLoginByLabel() return $ret; } +function camelCaseToUnderscore($input) +{ + return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_'); +} + // validator functions require_once LIBS . 'validator.php'; require_once SYSTEM . 'compat/base.php'; diff --git a/system/init.php b/system/init.php index d4c623bf..46ebe5ab 100644 --- a/system/init.php +++ b/system/init.php @@ -34,6 +34,10 @@ $cache = Cache::getInstance(); // twig require_once SYSTEM . 'twig.php'; +// action, used by many pages +$action = $_REQUEST['action'] ?? ''; +define('ACTION', $action); + // trim values we receive if(isset($_POST)) { diff --git a/system/pages/newsarchive.php b/system/pages/news/archive.php similarity index 86% rename from system/pages/newsarchive.php rename to system/pages/news/archive.php index aefa0fa9..fbcbe2fd 100644 --- a/system/pages/newsarchive.php +++ b/system/pages/news/archive.php @@ -9,5 +9,4 @@ * @link https://my-aac.org */ $_GET['archive'] = true; -require 'news.php'; -?> +require __DIR__ . '/../news.php'; diff --git a/system/router.php b/system/router.php index c2c8e268..9adee97b 100644 --- a/system/router.php +++ b/system/router.php @@ -8,6 +8,35 @@ * @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 .= '

' . getDatabaseConfig('site_closed_message') . '


'; + $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); + +/** @var boolean $load_it */ if(!$load_it) { // ignore warnings in some functions/plugins // page is not loaded anyways @@ -17,145 +46,153 @@ if(!$load_it) { return; } +/** @var string $content */ if(SITE_CLOSED && admin()) $content .= '

Site is under maintenance (closed mode). Only privileged users can see it.

'; $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(); } -$success = false; -$tmp_content = getCustomPage($uri, $success); -if($success) { - $content .= $tmp_content; - if(hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) { - $pageInfo = getCustomPageInfo($uri); - $content = $twig->render('admin.pages.links.html.twig', array( - 'page' => array('id' => $pageInfo !== null ? $pageInfo['id'] : 0, 'hidden' => $pageInfo !== null ? $pageInfo['hidden'] : '0') - )) . $content; +/** + * Routes loading + */ +$dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) { + $routes = require SYSTEM . 'routes.php'; + + $duplicates = []; + + $routesTmp = []; + foreach(getDatabasePages() as $page) { + $duplicates[$page] = true; + $routesTmp[] = [['GET', 'POST'], $page, 'database/' . $page, true]; } - $page = $uri; -} else { - // 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'; + Plugins::clearWarnings(); + foreach (Plugins::getRoutes() as $route) { + if(!isset($duplicates[$route[1]])) { + $duplicates[$route[1]] = true; + $routesTmp[] = [$route[0], $route[1], $route[2]]; + } + } + + foreach ($routes as $route) { + if(!isset($duplicates[$route[1]])) { + $routesTmp[] = [$route[0], $route[1], 'system/pages/' . $route[2]]; + } + } + + foreach ($routesTmp as $route) { + if (strpos($route[2], '.php') === false && !isset($route[3])) { + $route[2] = str_replace('system/pages/', '', 'redirect/' . $route[2]); } - $file = SYSTEM . 'pages/' . $page . '.php'; - if (!is_file($file)) { - $page = '404'; - $file = SYSTEM . 'pages/404.php'; + $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']; + +// 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 { - $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) { - $routes = require SYSTEM . 'routes.php'; + $uri = str_replace_first('?', '', $uri); + } +} - $duplicates = []; - Plugins::clearWarnings(); - foreach (Plugins::getRoutes() as $route) { - $duplicates[$route[1]] = true; - $r->addRoute($route[0], '/' . $route[1], $route[2]); +$uri = rawurldecode($uri); +define('URI', $uri); + +$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; } - foreach ($routes as $route) { - if(!isset($duplicates[$route[1]])) { - $r->addRoute($route[0], '/' . $route[1], 'system/pages/' . $route[2]); - } - } + break; - if (config('env') === 'dev') { - foreach(Plugins::getWarnings() as $warning) { - log_append('router.log', $warning); - } - } - }, - [ - 'cacheFile' => CACHE . 'route.cache', - 'cacheDisabled' => config('env') === 'dev', - ] - ); + case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + // ... 405 Method Not Allowed + $page = '405'; + $allowedMethods = $routeInfo[1]; + $file = SYSTEM . 'pages/405.php'; + break; - // Fetch method and URI from somewhere - $httpMethod = $_SERVER['REQUEST_METHOD']; - $uri = $_SERVER['REQUEST_URI']; + case FastRoute\Dispatcher::FOUND: + $path = $routeInfo[1]; + $vars = $routeInfo[2]; - // Strip query string (?foo=bar) and decode URI - if (false !== $pos = strpos($uri, '?')) { - if ($pos !== 1) { - $uri = substr($uri, 0, $pos); - } - else { - $uri = str_replace_first('?', '', $uri); - } - } - $uri = rawurldecode($uri); + $_REQUEST = array_merge($_REQUEST, $vars); + $_GET = array_merge($_GET, $vars); - $routeInfo = $dispatcher->dispatch($httpMethod, $uri); - switch ($routeInfo[0]) { - case FastRoute\Dispatcher::NOT_FOUND: - // ... 404 Not Found - $tmp = URI; - $found = true; + if (strpos($path, 'database/') !== false) { + //var_dump($path); + $pageName = str_replace('database/', '', $path); - $page = $tmp; - if (preg_match('/^[A-z0-9\/\-]+$/', $tmp)) { - global $template_path; - $file = $template_path . '/pages/' . $tmp . '.php'; - if (!is_file($file)) { - $file = SYSTEM . 'pages/' . $tmp . '.php'; - if (!is_file($file)) { - $found = false; - } - } - } - else { - $tmp_ = BASE_DIR; - $uri = $_SERVER['REQUEST_URI']; - if (!empty($tmp)) { - $uri = str_replace(BASE_DIR . '/', '', $uri); + $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; } - if (false !== $pos = strpos($uri, '?')) { - $tmp = substr($uri, 0, $pos); - } - - if (empty($tmp)) { - $page = 'news'; - $file = SYSTEM . 'pages/news.php'; - } - else { - $found = false; - } + $page = $pageName; + $file = false; } - - if (!$found) { - $page = '404'; - $file = SYSTEM . 'pages/404.php'; - } - - 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); - + } 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']; @@ -172,24 +209,23 @@ if($success) { $page = $uri; $file = BASE . $path; + } - unset($tmp, $uri); - break; - } + unset($tmp, $uri); + break; } } -define('PAGE', $page); -if(config('backward_support')) { - $subtopic = $page; +if (!$found) { + $page = '404'; + $file = SYSTEM . 'pages/404.php'; } -$action = isset($_REQUEST['action']) ? strtolower($_REQUEST['action']) : ''; -define('ACTION', $action); +define('PAGE', $page); ob_start(); if($hooks->trigger(HOOK_BEFORE_PAGE)) { - if(!$ignore) + if(!$ignore && $file !== false) require $file; } @@ -202,13 +238,49 @@ $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; - if(!isset($title)) { - $title = ucfirst($page); - } - $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; +} diff --git a/system/routes.php b/system/routes.php index aa42dbf0..8085b734 100644 --- a/system/routes.php +++ b/system/routes.php @@ -9,9 +9,11 @@ */ return [ + ['GET', '', 'news'], // redirect empty URL to news + ['GET', 'news/archive/{id:[0-9]+}[/]', 'news/archive.php'], + [['GET', 'POST'], 'account/base[/]', '404.php'], // this is to block account/base.php [['GET', 'POST'], 'account/password[/]', 'account/change_password.php'], - [['GET', 'POST'], 'account/register[/]', 'account/register.php'], [['GET', 'POST'], 'account/register/new[/]', 'account/register_new.php'], [['GET', 'POST'], 'account/email[/]', 'account/change_email.php'], [['GET', 'POST'], 'account/info[/]', 'account/change_info.php'], @@ -22,7 +24,7 @@ return [ [['GET', 'POST'], 'account/character/comment[/{name:[A-Za-z0-9-_%+\']+}]', 'account/change_comment.php'], ['GET', 'account/confirm_email/{hash:[A-Za-z0-9-_]+}[/]', 'account/confirm_email.php'], - ['GET', 'bans[/{page:\d+}]', 'bans.php'], + ['GET', 'bans/{page:\d+}[/]', 'bans.php'], [['GET', 'POST'], 'characters[/{name:[A-Za-z0-9-_%+\']+}]', 'characters.php'], ['GET', 'changelog[/{page:\d+}]', 'changelog.php'], ['GET', 'creatures[/{name:[A-Za-z0-9-_%+\']+}]', 'creatures.php'], @@ -45,9 +47,7 @@ return [ ['GET', 'highscores/{list:[A-Za-z0-9-_]+}/{vocation:[A-Za-z0-9-_]+}[/]', 'highscores.php'], ['GET', 'highscores/{list:[A-Za-z0-9-_]+}[/]', 'highscores.php'], - ['GET', '', 'news.php'], - [['GET', 'POST'], 'news/archive[/]', 'newsarchive.php'], - ['GET', 'news/archive/{id:[0-9]+}[/]', 'newsarchive.php'], + ['GET', 'online/{order:[A-Za-z0-9-_]+}[/]', 'online.php'], /* '/^gifts\/history\/?$/' => array('subtopic' => 'gifts', 'action' => 'show_history'), '/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'), diff --git a/system/templates/online.html.twig b/system/templates/online.html.twig index 04e0b651..b7c9d916 100644 --- a/system/templates/online.html.twig +++ b/system/templates/online.html.twig @@ -85,14 +85,14 @@ {% if config.account_country %} - + {% endif %} {% if config.online_outfit %} {% endif %} - - - + + + {% set i = 0 %} {% for player in players %} diff --git a/templates/tibiacom/index.php b/templates/tibiacom/index.php index 46cafb26..8ebbd5b6 100644 --- a/templates/tibiacom/index.php +++ b/templates/tibiacom/index.php @@ -388,7 +388,7 @@ foreach($config['menu_categories'] as $id => $cat) { -
+
##OutfitNameLevelVocationNameLevelVocation