Added in plugin routing. Sitemap + robotstxt done

This commit is contained in:
Belle Aerni 2023-11-10 05:29:13 -08:00
parent 5a1e17beea
commit f10bd2cbc3
12 changed files with 105 additions and 612 deletions

View file

@ -52,7 +52,7 @@ class AntCMS
$themeConfig = Self::getThemeConfig();
if (!$content || !is_array($content)) {
$this->renderException("404");
return $this->renderException("404");
}
$pageTemplate = $this->getPageLayout(null, $page, $content['template']);
@ -98,9 +98,8 @@ class AntCMS
* @param string $exceptionCode The exception code to be displayed on the error page
* @param int $httpCode The HTTP response code to return, 404 by default.
* @param string $exceptionString An optional parameter to define a custom string to be displayed along side the exception.
* @return never
*/
public function renderException(string $exceptionCode, int $httpCode = 404, string $exceptionString = 'That request caused an exception to be thrown.')
public function renderException(string $exceptionCode, int $httpCode = 404, string $exceptionString = 'That request caused an exception to be thrown.'): Response
{
$exceptionString .= " (Code {$exceptionCode})";
$pageTemplate = self::getPageLayout();
@ -116,9 +115,9 @@ class AntCMS
$pageTemplate = str_replace('{{ AntCMSBody | raw }} ', $params['AntCMSBody'], $pageTemplate);
}
http_response_code($httpCode);
echo $pageTemplate;
exit;
$response = $this->getResponse()->withStatus($httpCode);
$response->getBody()->write($pageTemplate);
return $response;
}
/**
@ -248,9 +247,9 @@ class AntCMS
public function serveContent(): Response
{
$path = $this->request->getUri();
$path = $this->request->getUri()->getPath();
if (!file_exists($path)) {
$this->renderException('404');
return $this->renderException('404');
} else {
$response = $this->response->withHeader('Content-Type', mime_content_type($path) . '; charset=UTF-8');
$response->getBody()->write(file_get_contents($path));

View file

@ -2,15 +2,23 @@
namespace AntCMS;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use \Slim\App;
abstract class AntPlugin
{
/**
* @param array<string> $route
* @return mixed
*/
public function handlePluginRoute(array $route)
protected ?Request $request;
protected ?Response $response;
public function setRequest(?Request $request)
{
die("Plugin did not define a handlePluginRoute function");
$this->request = $request;
}
public function SetResponse(?Response $response)
{
$this->response = $response;
}
/** @return string */

View file

@ -3,15 +3,16 @@
namespace AntCMS;
use AntCMS\AntTools;
use Slim\App;
class AntPluginLoader
{
/** @return array<mixed> */
public function loadPlugins()
{
$plugins = array();
$plugins = [];
$files = AntTools::getFileList(antPluginPath, null, true);
$files = AntTools::getFileList(antPluginPath, 'php', true);
foreach ($files as $file) {
if (str_ends_with($file, "Plugin.php")) {
@ -23,4 +24,18 @@ class AntPluginLoader
return $plugins;
}
public function registerPluginRoutes(App $app)
{
$files = scandir(antPluginPath);
foreach ($files as $file) {
$fqcn = '\\Plugins\\' . $file . '\\Controller';
if (class_exists($fqcn)) {
$controler = new $fqcn;
if (method_exists($controler, 'registerRoutes')) {
$controler->registerRoutes($app);
}
}
}
}
}

View file

@ -1,106 +0,0 @@
<?php
namespace AntCMS;
class AntRouting
{
private string $baseUrl;
private string $requestUri;
private array $uriExploded;
private array $indexes = ['/', '/index.php', '/index.html', '', 'index.php', 'index.html'];
/**
* @param string $baseUrl The base site URL. Ex: domain.com
* @param string $requestUri The current request URI. Ex: /page/example
*/
public function __construct(string $baseUrl, string $requestUri)
{
$this->baseUrl = $baseUrl;
$this->requestUri = $requestUri;
$this->setExplodedUri($requestUri);
}
/**
* @param string $requestUri The current request URI. Ex: /page/example
*/
public function setRequestUri(string $requestUri): void
{
$this->$requestUri = $requestUri;
$this->setExplodedUri($requestUri);
}
/**
* Used to add to the start of the request URI. Primarially used for plugin routing.
* For example: this is used internally to rewrite /profile/edit to /plugin/profile/edit
*
* @param string $append What to append to the start of the request URI.
*/
public function requestUriUnshift(string $append): void
{
array_unshift($this->uriExploded, $append);
$this->requestUri = implode('/', $this->uriExploded);
}
/**
* Used to check if the current request URI matches a specified route.
* Supports using '*' as a wild-card. Ex: '/admin/*' will match '/admin/somthing' and '/admin'
*
* @param string $uri The Route to compare against the current URI.
*/
public function checkMatch(string $uri): bool
{
$matching = explode('/', $uri);
if (empty($matching[0])) {
array_shift($matching);
}
if (count($matching) < count($this->uriExploded) && end($matching) !== '*') {
return false;
}
foreach ($this->uriExploded as $index => $value) {
if (isset($matching[$index]) && $matching[$index] !== '*' && $matching[$index] !== $value) {
return false;
}
}
return true;
}
/**
* Attempts to detect what plugin is associated with the current URI and then routes to the matching one.
*/
public function routeToPlugin(): void
{
$pluginName = $this->uriExploded[1];
$pluginLoader = new AntPluginLoader();
$plugins = $pluginLoader->loadPlugins();
//Drop the first two elements of the array so the remaining segments are specific to the plugin.
array_splice($this->uriExploded, 0, 2);
foreach ($plugins as $plugin) {
if (strtolower($plugin->getName()) === strtolower($pluginName)) {
$plugin->handlePluginRoute($this->uriExploded);
exit;
}
}
// plugin not found
header("HTTP/1.0 404 Not Found");
echo ("Error 404");
exit;
}
private function setExplodedUri(string $uri): void
{
$exploaded = explode('/', $uri);
if (empty($exploaded[0])) {
array_shift($exploaded);
}
$this->uriExploded = $exploaded;
}
}

View file

@ -1,328 +0,0 @@
<?php
use AntCMS\AntCMS;
use AntCMS\AntPlugin;
use AntCMS\AntConfig;
use AntCMS\AntPages;
use AntCMS\AntYaml;
use AntCMS\AntAuth;
use AntCMS\AntTools;
use AntCMS\AntTwig;
use AntCMS\AntUsers;
class AdminPlugin extends AntPlugin
{
protected $auth;
protected $antCMS;
protected $AntTwig;
public function getName(): string
{
return 'Admin';
}
/**
* @param array<string> $route
* @return void
*/
public function handlePluginRoute(array $route)
{
$currentStep = $route[0] ?? 'none';
$this->auth = new AntAuth;
$this->auth->checkAuth();
$this->antCMS = new AntCMS;
$this->AntTwig = new AntTwig();
array_shift($route);
switch ($currentStep) {
case 'config':
$this->configureAntCMS($route);
case 'pages':
$this->managePages($route);
case 'users':
$this->userManagement($route);
default:
$params = [
'AntCMSTitle' => 'AntCMS Admin Dashboard',
'AntCMSDescription' => 'The AntCMS admin dashboard',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
'user' => AntUsers::getUserPublicalKeys($this->auth->getUsername()),
];
echo $this->AntTwig->renderWithSubLayout('admin_landing', $params);
break;
}
}
/**
* @param array<string> $route
* @return never
*/
private function configureAntCMS(array $route)
{
if ($this->auth->getRole() != 'admin') {
$this->antCMS->renderException("You are not permitted to visit this page.");
}
$currentConfig = AntConfig::currentConfig();
$currentConfigFile = file_get_contents(antConfigFile);
$params = array(
'AntCMSTitle' => 'AntCMS Configuration',
'AntCMSDescription' => 'The AntCMS configuration screen',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
);
switch ($route[0] ?? 'none') {
case 'edit':
$params['AntCMSActionURL'] = '//' . $currentConfig['baseURL'] . 'admin/config/save';
$params['AntCMSTextAreaContent'] = htmlspecialchars($currentConfigFile);
echo $this->AntTwig->renderWithSubLayout('textareaEdit', $params);
break;
case 'save':
if (!$_POST['textarea']) {
AntCMS::redirectWithoutRequest('/admin/config');
}
$yaml = AntYaml::parseYaml($_POST['textarea']);
if (is_array($yaml)) {
AntYaml::saveFile(antConfigFile, $yaml);
}
AntCMS::redirectWithoutRequest('/admin/config');
break;
default:
foreach ($currentConfig as $key => $value) {
if (is_array($value)) {
foreach ($value as $subkey => $subvalue) {
if (is_bool($subvalue)) {
$currentConfig[$key][$subkey] = ($subvalue) ? 'true' : 'false';
}
if (is_array($subvalue)) {
$currentConfig[$key][$subkey] = implode(', ', $subvalue);
}
}
} else if (is_bool($value)) {
$currentConfig[$key] = ($value) ? 'true' : 'false';
}
}
$params['currentConfig'] = $currentConfig;
echo $this->AntTwig->renderWithSubLayout('admin_config', $params);
break;
}
exit;
}
/**
* @param array<string> $route
* @return never
*/
private function managePages(array $route)
{
$pages = AntPages::getPages();
$params = array(
'AntCMSTitle' => 'AntCMS Page Management',
'AntCMSDescription' => 'The AntCMS page management screen',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
);
switch ($route[0] ?? 'none') {
case 'regenerate':
AntPages::generatePages();
AntCMS::redirectWithoutRequest('/admin/pages');
exit;
case 'edit':
if (!isset($_POST['newpage'])) {
array_shift($route);
$pagePath = AntTools::convertFunctionaltoFullpath(implode('/', $route));
$page = file_get_contents($pagePath);
//Finally, we strip off the antContentPath for compatibility with the save function.
$pagePath = str_replace(antContentPath, '', $pagePath);
} else {
$pagePath = '/' . $_POST['newpage'];
if (!str_ends_with($pagePath, ".md")) {
$pagePath .= '.md';
}
$pagePath = AntTools::repairFilePath($pagePath);
$name = $this->auth->getName();
$page = "--AntCMS--\nTitle: New Page Title\nAuthor: $name\nDescription: Description of this page.\nKeywords: Keywords\n--AntCMS--\n";
}
$params['AntCMSActionURL'] = '//' . AntConfig::currentConfig('baseURL') . "admin/pages/save/{$pagePath}";
$params['AntCMSTextAreaContent'] = $page;
echo $this->AntTwig->renderWithSubLayout('markdownEdit', $params);
break;
case 'save':
array_shift($route);
$pagePath = AntTools::repairFilePath(antContentPath . '/' . implode('/', $route));
if (!isset($_POST['textarea'])) {
AntCMS::redirectWithoutRequest('/admin/pages');
}
file_put_contents($pagePath, $_POST['textarea']);
AntCMS::redirectWithoutRequest('/admin/pages');
exit;
case 'create':
$params['BaseURL'] = AntConfig::currentConfig('baseURL');
echo $this->AntTwig->renderWithSubLayout('admin_newPage', $params);
break;
case 'delete':
array_shift($route);
$pagePath = AntTools::convertFunctionaltoFullpath(implode('/', $route));
// Find the key associated with the functional page path, then remove it from our temp pages array
foreach ($pages as $key => $page) {
if ($page['fullPagePath'] == $pagePath) {
unset($pages[$key]);
}
}
// If we were able to delete the page, update the pages list with the updated pages array.
if (file_exists($pagePath) && unlink($pagePath)) {
AntYaml::saveFile(antPagesList, $pages);
}
AntCMS::redirectWithoutRequest('/admin/pages');
break;
case 'togglevisibility':
array_shift($route);
$pagePath = AntTools::convertFunctionaltoFullpath(implode('/', $route));
foreach ($pages as $key => $page) {
if ($page['fullPagePath'] == $pagePath) {
$pages[$key]['showInNav'] = !$page['showInNav'];
}
}
AntYaml::saveFile(antPagesList, $pages);
AntCMS::redirectWithoutRequest('/admin/pages');
break;
default:
foreach ($pages as $key => $page) {
$pages[$key]['editurl'] = '//' . AntTools::repairURL(AntConfig::currentConfig('baseURL') . "/admin/pages/edit/" . $page['functionalPagePath']);
$pages[$key]['deleteurl'] = '//' . AntTools::repairURL(AntConfig::currentConfig('baseURL') . "/admin/pages/delete/" . $page['functionalPagePath']);
$pages[$key]['togglevisibility'] = '//' . AntTools::repairURL(AntConfig::currentConfig('baseURL') . "/admin/pages/togglevisibility/" . $page['functionalPagePath']);
$pages[$key]['isvisable'] = $this->boolToWord($page['showInNav']);
}
$params = [
'AntCMSTitle' => 'AntCMS Admin Dashboard',
'AntCMSDescription' => 'The AntCMS admin dashboard',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
'pages' => $pages,
];
echo $this->AntTwig->renderWithSubLayout('admin_managePages', $params);
break;
}
exit;
}
private function userManagement(array $route)
{
if ($this->auth->getRole() != 'admin') {
$this->antCMS->renderException("You are not permitted to visit this page.");
}
$params = array(
'AntCMSTitle' => 'AntCMS User Management',
'AntCMSDescription' => 'The AntCMS user management screen',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
);
switch ($route[0] ?? 'none') {
case 'add':
echo $this->AntTwig->renderWithSubLayout('admin_userAdd', $params);
break;
case 'edit':
$user = AntUsers::getUserPublicalKeys($route[1]);
if (!$user) {
AntCMS::redirectWithoutRequest('/admin/users');
}
$user['username'] = $route[1];
$params['user'] = $user;
echo $this->AntTwig->renderWithSubLayout('admin_userEdit', $params);
break;
case 'resetpassword':
$user = AntUsers::getUserPublicalKeys($route[1]);
if (!$user) {
AntCMS::redirectWithoutRequest('/admin/users');
}
$user['username'] = $route[1];
$params['user'] = $user;
echo $this->AntTwig->renderWithSubLayout('admin_userResetPassword', $params);
break;
case 'save':
$data['username'] = $_POST['username'] ?? null;
$data['name'] = $_POST['display-name'] ?? null;
$data['role'] = $_POST['role'] ?? null;
$data['password'] = $_POST['password'] ?? null;
foreach ($data as $key => $value) {
if (is_null($value)) {
unset($data[$key]);
}
}
AntUsers::updateUser($_POST['originalusername'], $data);
AntCMS::redirectWithoutRequest('/admin/users');
break;
case 'savenew':
AntUsers::addUser($_POST);
AntCMS::redirectWithoutRequest('/admin/users');
break;
default:
$users = AntUsers::getUsers();
foreach ($users as $key => $user) {
unset($users[$key]['password']);
$users[$key]['username'] = $key;
}
$params['users'] = $users;
echo $this->AntTwig->renderWithSubLayout('admin_users', $params);
break;
}
exit;
}
/**
* @return string
*/
private function boolToWord(bool $value)
{
return $value ? 'true' : 'false';
}
}

View file

@ -1,119 +0,0 @@
<?php
use AntCMS\AntPlugin;
use AntCMS\AntAuth;
use AntCMS\AntCMS;
use AntCMS\AntTwig;
use AntCMS\AntUsers;
class ProfilePlugin extends AntPlugin
{
protected $antAuth;
protected $antTwig;
public function handlePluginRoute(array $route)
{
$this->antAuth = new AntAuth;
$this->antTwig = new AntTwig;
$currentStep = $route[0] ?? 'none';
$params = [
'AntCMSTitle' => 'AntCMS Profile Management',
'AntCMSDescription' => 'AntCMS Profile Management',
'AntCMSAuthor' => 'AntCMS',
'AntCMSKeywords' => '',
];
switch ($currentStep) {
case 'firsttime':
if (file_exists(antUsersList)) {
AntCMS::redirectWithoutRequest('/admin');
}
echo $this->antTwig->renderWithSubLayout('profile_firstTime', $params);
break;
case 'submitfirst':
if (file_exists(antUsersList)) {
AntCMS::redirectWithoutRequest('/admin');
}
if (isset($_POST['username']) && isset($_POST['password']) && isset($_POST['display-name'])) {
$data = [
'username' => $_POST['username'],
'password' => $_POST['password'],
'name' => $_POST['display-name'],
];
AntUsers::setupFirstUser($data);
AntCMS::redirectWithoutRequest('/admin');
} else {
AntCMS::redirectWithoutRequest('/profile/firsttime');
}
break;
case 'edit':
$this->antAuth->checkAuth();
$user = AntUsers::getUserPublicalKeys($this->antAuth->getUsername());
if (!$user) {
AntCMS::redirectWithoutRequest('/profile');
}
$user['username'] = $this->antAuth->getUsername();
$params['user'] = $user;
echo $this->antTwig->renderWithSubLayout('profile_edit', $params);
break;
case 'resetpassword':
$this->antAuth->checkAuth();
$user = AntUsers::getUserPublicalKeys($this->antAuth->getUsername());
if (!$user) {
AntCMS::redirectWithoutRequest('/profile');
}
$user['username'] = $this->antAuth->getUsername();
$params['user'] = $user;
echo $this->antTwig->renderWithSubLayout('profile_resetPassword', $params);
break;
case 'save':
$this->antAuth->checkAuth();
$data['username'] = $_POST['username'] ?? null;
$data['name'] = $_POST['display-name'] ?? null;
$data['password'] = $_POST['password'] ?? null;
foreach ($data as $key => $value) {
if (is_null($value)) {
unset($data[$key]);
}
}
AntUsers::updateUser($this->antAuth->getUsername(), $data);
AntCMS::redirectWithoutRequest('/profile');
break;
case 'logout':
$this->antAuth->invalidateSession();
if (!$this->antAuth->isAuthenticated()) {
echo "You have been logged out.";
} else {
echo "There was an error logging you out.";
}
exit;
default:
$this->antAuth->checkAuth();
$params['user'] = AntUsers::getUserPublicalKeys($this->antAuth->getUsername());
echo $this->antTwig->renderWithSubLayout('profile_landing', $params);
}
exit;
}
public function getName(): string
{
return 'Profile';
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Plugins\Robotstxt;
use \Slim\App;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class Controller
{
public function registerRoutes(App $app)
{
$app->get('/robots.txt', function (Request $request, Response $response) {
$sitemap = new Robotstxt();
$sitemap->setRequest($request);
$sitemap->SetResponse($response);
return $sitemap->returnRobotstxt();
});
}
}

View file

@ -1,12 +1,14 @@
<?php
namespace Plugins\Robotstxt;
use AntCMS\AntPlugin;
use AntCMS\AntConfig;
use AntCMS\AntTools;
class RobotstxtPlugin extends AntPlugin
class Robotstxt extends AntPlugin
{
public function handlePluginRoute(array $route)
public function returnRobotstxt()
{
$protocol = AntConfig::currentConfig('forceHTTPS') ? 'https' : 'http';
$baseURL = AntConfig::currentConfig('baseURL');
@ -16,9 +18,10 @@ class RobotstxtPlugin extends AntPlugin
$robotstxt .= 'Disallow: /admin/' . "\n";
$robotstxt .= 'Disallow: /profile/' . "\n";
$robotstxt .= 'Sitemap: ' . $protocol . '://' . AntTools::repairURL($baseURL . '/sitemap.xml' . "\n");
header("Content-Type: text/plain");
echo $robotstxt;
exit;
$response = $this->response->withHeader('Content-Type', 'Content-Type: text/plain');
$response->getBody()->write($robotstxt);
return $response;
}
public function getName(): string

View file

@ -0,0 +1,20 @@
<?php
namespace Plugins\Sitemap;
use \Slim\App;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class Controller
{
public function registerRoutes(App $app)
{
$app->get('/sitemap.xml', function (Request $request, Response $response) {
$sitemap = new Sitemap();
$sitemap->setRequest($request);
$sitemap->SetResponse($response);
return $sitemap->returnSitemap();
});
}
}

View file

@ -1,13 +1,15 @@
<?php
use AntCMS\AntPlugin;
namespace Plugins\Sitemap;
use AntCMS\AntPages;
use AntCMS\AntConfig;
use AntCMS\AntTools;
use Psr\Http\Message\ResponseInterface as Response;
class SitemapPlugin extends AntPlugin
class Sitemap extends \AntCMS\AntPlugin
{
public function handlePluginRoute(array $route)
public function returnSitemap(): Response
{
$protocol = AntConfig::currentConfig('forceHTTPS') ? 'https' : 'http';
$baseURL = AntConfig::currentConfig('baseURL');
@ -15,7 +17,7 @@ class SitemapPlugin extends AntPlugin
$pages = AntPages::getPages();
if (extension_loaded('dom')) {
$domDocument = new DOMDocument('1.0', 'UTF-8');
$domDocument = new \DOMDocument('1.0', 'UTF-8');
$domDocument->formatOutput = true;
$domElement = $domDocument->createElement('urlset');
@ -40,11 +42,13 @@ class SitemapPlugin extends AntPlugin
$domElement->appendChild($element);
}
header('Content-Type: application/xml');
echo $domDocument->saveXML();
exit;
$response = $this->response->withHeader('Content-Type', 'Content-Type: application/xml');
$response->getBody()->write($domDocument->saveXML());
return $response;
} else {
die("AntCMS is unable to generate a sitemap without having the DOM extension loadded in PHP.");
$response = $this->response;
$response->getBody()->write("AntCMS is unable to generate a sitemap without having the DOM extension loadded in PHP.");
return $response;
}
}

View file

@ -17,5 +17,7 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . 'Vendor' . DIRECTORY_SEPARATOR . 'a
$classMapPath = AntCachePath . DIRECTORY_SEPARATOR . 'classMap.php';
$loader = new \AntCMS\AntLoader(['path' => $classMapPath]);
$loader->addNamespace('AntCMS\\', __DIR__ . DIRECTORY_SEPARATOR . 'AntCMS');
$loader->addNamespace('Plugins\\', __DIR__ . DIRECTORY_SEPARATOR . 'Plugins');
$loader->checkClassMap();
$loader->register();

View file

@ -2,6 +2,7 @@
use AntCMS\AntCMS;
use AntCMS\AntConfig;
use AntCMS\AntPluginLoader;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
@ -20,9 +21,7 @@ if (!file_exists(antPagesList)) {
\AntCMS\AntPages::generatePages();
}
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$baseUrl = AntConfig::currentConfig('baseURL');
$antRouting = new \AntCMS\AntRouting($baseUrl, $requestUri);
$antCMS = new AntCMS();
$app = AppFactory::create();
$app->addRoutingMiddleware();
@ -40,9 +39,9 @@ if (AntConfig::currentConfig('cacheMode') !== 'none') {
$routeCollector->setCacheFile(AntCachePath . DIRECTORY_SEPARATOR . 'routes.cache');
}
$routeCollector = $app->getRouteCollector();
$antCMS = new AntCMS($app);
// Register plugin routes first so they get priority
$pluginLoader = new AntPluginLoader;
$pluginLoader->registerPluginRoutes($app);
$app->get('/themes/{theme}/assets', function (Request $request, Response $response) use ($antCMS) {
$antCMS->setRequest($request);
@ -56,30 +55,6 @@ $app->get('/.well-known/acme-challenge/{path:.*}', function (Request $request, R
return $antCMS->serveContent();
});
/**
* TODO: Make these non-static and not hard-coded.
* This is also still relying on the custom routing implementation I am working to remove
*/
if ($antRouting->checkMatch('/sitemap.xml')) {
$antRouting->setRequestUri('/plugin/sitemap');
}
if ($antRouting->checkMatch('/robots.txt')) {
$antRouting->setRequestUri('/plugin/robotstxt');
}
if ($antRouting->checkMatch('/admin/*')) {
$antRouting->requestUriUnshift('plugin');
}
if ($antRouting->checkMatch('/profile/*')) {
$antRouting->requestUriUnshift('plugin');
}
if ($antRouting->checkMatch('/plugin/*')) {
$antRouting->routeToPlugin();
}
$app->get('/', function (Request $request, Response $response) use ($antCMS) {
if (!file_exists(antUsersList)) {
AntCMS::redirectWithoutRequest('/profile/firsttime');