diff --git a/.htaccess b/.htaccess index 80d47f3..9295fae 100644 --- a/.htaccess +++ b/.htaccess @@ -37,6 +37,7 @@ RewriteRule ^(.*)/$ /$1 [L,R=301] # Handle Template Files + RewriteRule ^theme/([^/]+)/(.*)$ index.php?theme=$1&file=$2 [QSA,L] RewriteRule ^(css|js|fonts|images|assets)/(.*)$ default/$1/$2 [QSA,L] # Handle Front Controller diff --git a/classes/Accounting/Auth.class.php b/classes/Accounting/Auth.class.php index a62a3dc..8ead923 100644 --- a/classes/Accounting/Auth.class.php +++ b/classes/Accounting/Auth.class.php @@ -6,7 +6,9 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Accounting; + class Auth { public static function isLoggedIn() : bool { return AuthFactory::getInstance()->isLoggedIn(); @@ -60,5 +62,4 @@ public static function getPermissions() : array { return AuthFactory::getInstance()->getPermissions(); } } - - ?> \ No newline at end of file +?> \ No newline at end of file diff --git a/classes/Accounting/AuthFactory.class.php b/classes/Accounting/AuthFactory.class.php index 7236082..52e92e3 100644 --- a/classes/Accounting/AuthFactory.class.php +++ b/classes/Accounting/AuthFactory.class.php @@ -6,6 +6,7 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Accounting; use fruithost\Localization\I18N; @@ -236,5 +237,4 @@ public function getPermissions() : array { return $this->permissions; } } - - ?> \ No newline at end of file +?> \ No newline at end of file diff --git a/classes/Accounting/Session.class.php b/classes/Accounting/Session.class.php index 0101b54..273d288 100644 --- a/classes/Accounting/Session.class.php +++ b/classes/Accounting/Session.class.php @@ -8,7 +8,7 @@ */ namespace fruithost\Accounting; - + class Session { public static function init() : void { if(!isset($_SESSION)) { diff --git a/classes/Accounting/User.class.php b/classes/Accounting/User.class.php index 4051b0e..ccd709c 100644 --- a/classes/Accounting/User.class.php +++ b/classes/Accounting/User.class.php @@ -6,6 +6,7 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Accounting; use fruithost\Security\Encryption; @@ -209,5 +210,4 @@ public function getPermissions() : array { return $this->permissions; } } - - ?> \ No newline at end of file +?> \ No newline at end of file diff --git a/classes/Hardware/NetworkAddress.class.php b/classes/Hardware/NetworkAddress.class.php index 274d6ec..2c0f47b 100644 --- a/classes/Hardware/NetworkAddress.class.php +++ b/classes/Hardware/NetworkAddress.class.php @@ -1,4 +1,12 @@ getRepositorysByID($ids); } - } \ No newline at end of file + } +?> \ No newline at end of file diff --git a/classes/Installer/InstallerFactory.class.php b/classes/Installer/InstallerFactory.class.php index 1d13478..be82de3 100644 --- a/classes/Installer/InstallerFactory.class.php +++ b/classes/Installer/InstallerFactory.class.php @@ -6,7 +6,9 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Installer; + class InstallerFactory { private static ?InstallerFactory $instance = null; @@ -21,4 +23,5 @@ public static function getInstance() : InstallerFactory { } public function getList() {} - } \ No newline at end of file + } +?> \ No newline at end of file diff --git a/classes/Installer/Repository.class.php b/classes/Installer/Repository.class.php index 3a0748f..997d5d9 100644 --- a/classes/Installer/Repository.class.php +++ b/classes/Installer/Repository.class.php @@ -6,6 +6,7 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Installer; use fruithost\Storage\Database; @@ -171,4 +172,5 @@ public function getFile($repository, $file) : string | int | null { return $content; } - } \ No newline at end of file + } +?> \ No newline at end of file diff --git a/classes/Localization/I18N.class.php b/classes/Localization/I18N.class.php index 4712e27..8bacc02 100644 --- a/classes/Localization/I18N.class.php +++ b/classes/Localization/I18N.class.php @@ -8,6 +8,7 @@ */ namespace fruithost\Localization; + use fruithost\Accounting\Auth; use fruithost\Network\Request; use fruithost\Storage\Database; diff --git a/classes/Modules/Module.class.php b/classes/Modules/Module.class.php index e41623a..acb3394 100644 --- a/classes/Modules/Module.class.php +++ b/classes/Modules/Module.class.php @@ -17,7 +17,7 @@ class Module { private ?ModuleInfo $info = null; private ?ModuleInterface $instance = null; private bool $enabled = false; - private bool$locked = false; + private bool $locked = false; public function __construct(string $path) { $this->path = $path; diff --git a/classes/Modules/ModuleError.class.php b/classes/Modules/ModuleError.class.php index b2e5964..adbd0a4 100644 --- a/classes/Modules/ModuleError.class.php +++ b/classes/Modules/ModuleError.class.php @@ -1,4 +1,12 @@ headers['Content-Type'] = $type; } public function addHeader(string $name, string $value) : void { diff --git a/classes/Services/PHP.class.php b/classes/Services/PHP.class.php index b7eeaac..48542bb 100644 --- a/classes/Services/PHP.class.php +++ b/classes/Services/PHP.class.php @@ -6,6 +6,7 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\Services; use fruithost\Accounting\Auth; @@ -43,11 +44,16 @@ public function __construct() { } catch(\Exception $e) { $this->debug[] = $e->getMessage(); } + if(!empty($ini)) { if(isset($ini['www'])) { if(isset($ini['www']['listen'])) { $this->socket = $ini['www']['listen']; } + } else if(isset($ini['panel'])) { + if(isset($ini['panel']['listen'])) { + $this->socket = str_replace('$pool', 'panel', $ini['panel']['listen']); + } } } @@ -65,6 +71,10 @@ public function setPath(string $path) : void { } public function getHeader() : ?string { + if(empty($this->content)) { + return null; + } + return explode("\r\n\r\n", $this->content)[0]; } @@ -274,4 +284,4 @@ public function highlight(array | string $data) : array | string { I18N::__('yes') I18N::__('no value') */ - ?> +?> diff --git a/classes/System/Core.class.php b/classes/System/Core.class.php index 9f10ef0..d66bc70 100644 --- a/classes/System/Core.class.php +++ b/classes/System/Core.class.php @@ -6,6 +6,7 @@ * @version 1.0.0 * @license MIT */ + namespace fruithost\System; use fruithost\Accounting\Auth; @@ -22,6 +23,7 @@ use fruithost\Storage\PropertiesCache; use fruithost\Templating\Template; use fruithost\UI\Icon; + use fruithost\System\Utils; use Parsedown\Parsedown; class Core extends Loader { @@ -59,41 +61,65 @@ public function getHooks() : ?Hooks { } protected function initRoutes() : void { + $this->router->addRoute('^/theme/([^/]+)/(.*)$', function(?string $theme = null, ?string $file = null) { + $path = sprintf('%1$s%2$sthemes%2$s%3$s%2$s%4$s', dirname(PATH), DS, $theme, $file); + + // @ToDo Is Secure? + if(file_exists($path)) { + Response::setContentType(Utils::getMimeType($path)); + Response::header(); + readfile($path); + exit(); + } + + $this->template->display('error/404'); + }); + $this->router->addRoute('/', function() { if(Auth::isLoggedIn()) { Response::redirect('/overview'); } + Response::redirect('/login'); }); + $this->router->addRoute('/logout', function() { if(Auth::isLoggedIn()) { Auth::logout(); } + Response::redirect('/'); }); + $this->router->addRoute('/overview', function() { if(!Auth::isLoggedIn()) { Response::redirect('/'); } + $this->template->display('overview'); }); + $this->router->addRoute('^/settings(?:/([a-zA-Z0-9\-_]+))?$', function(?string $tab = null) { if(!Auth::isLoggedIn()) { Response::redirect('/'); } + $this->template->display('settings', [ 'tab' => $tab, 'languages' => I18N::getLanguages(), 'timezones' => json_decode(file_get_contents(dirname(PATH).'/config/timezones.json')) ]); }); + $this->router->addRoute('^/account(?:/([a-zA-Z0-9\-_]+))?$', function(?string $tab = null) { if(!Auth::isLoggedIn()) { Response::redirect('/'); } + $data = Database::single('SELECT * FROM `'.DATABASE_PREFIX.'users_data` WHERE `user_id`=:user_id LIMIT 1', [ 'user_id' => Auth::getID() ]); + if($data !== false) { foreach($data as $index => $entry) { if(in_array($index, [ @@ -105,11 +131,13 @@ protected function initRoutes() : void { $data->{$index} = Encryption::decrypt($entry, ENCRYPTION_SALT); } } + $this->template->display('account', [ 'tab' => $tab, 'data' => $data ]); }); + $this->router->addRoute('^/app/([a-zA-Z0-9\-_]+)/(.*)$', function(?string $module = null, ?string $file = null) { if(empty($module)) { $this->template->display('error/module', array_merge($this->template->getAssigns(), [ @@ -118,46 +146,58 @@ protected function initRoutes() : void { return; } + $module = $this->getModules()->getModule($module); + if(empty($module)) { header('HTTP/1.1 403 Forbidden'); require_once(dirname(PATH).'/placeholder/errors/403.html'); exit(); } + if(!method_exists($module->getInstance(), 'frame')) { header('HTTP/1.1 403 Forbidden'); require_once(dirname(PATH).'/placeholder/errors/403.html'); exit(); } + if(!file_exists(sprintf('%s/www/', $module->getPath()))) { header('HTTP/1.1 403 Forbidden'); require_once(dirname(PATH).'/placeholder/errors/403.html'); exit(); } + $file = sprintf('%s/www/%s', $module->getPath(), $file); + if(!file_exists($file)) { header('HTTP/1.1 403 Forbidden'); require_once(dirname(PATH).'/placeholder/errors/403.html'); exit(); } + Response::setContentType(mime_content_type($file)); Response::header(); readfile($file); exit(); }); + $this->router->addRoute('^/module(?:(?:/([a-zA-Z0-9\-_]+)?)(?:/([a-zA-Z0-9\-_]+)(?:/([a-zA-Z0-9\-_]+))?)?)?$', function(?string $module = null, ?string $submodule = null, ?string $action = null) { if(!Auth::isLoggedIn()) { Response::redirect('/'); } + if(Session::has('success')) { $this->template->assign('success', Session::get('success')); Session::remove('success'); } + if(Session::has('error')) { $this->template->assign('error', Session::get('error')); Session::remove('error'); } + $errors = []; + foreach(($this->getModules()->getList()) as $m) { try { if($m != null && $m->getInstance() != null && method_exists($m->getInstance(), 'preLoad')) { @@ -171,35 +211,41 @@ protected function initRoutes() : void { ]; } } + if(count($errors) > 0) { $message = I18N::get('Following Modules have some Errors:'); $message .= '
'; + foreach($errors as $entry) { $message .= $entry->name; } + $this->template->assign('error', $message); } + if(empty($module)) { $this->template->display('error/module', array_merge($this->template->getAssigns(), [ 'module' => $module, 'submodule' => $submodule, 'action' => $action ])); - return; } + $module = $this->getModules()->getModule($module); + if(empty($module)) { $this->template->display('error/module', array_merge($this->template->getAssigns(), [ 'module' => $module, 'submodule' => $submodule, 'action' => $action ])); - return; } + $permissions = $module->getInfo()->getPermissions(); $visible = true; + if(!empty($permissions)) { $visible = false; foreach($permissions as $permission) { @@ -208,6 +254,7 @@ protected function initRoutes() : void { } } } + if(!$visible) { $this->template->display('error/permissions', array_merge($this->template->getAssigns(), [ 'module' => $module, @@ -217,9 +264,11 @@ protected function initRoutes() : void { return; } + if(method_exists($module->getInstance(), 'load')) { $module->getInstance()->load($submodule, $action); } + if(isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && method_exists($module->getInstance(), 'onPOST')) { $module->getInstance()->onPOST(array_merge([ 'module' => $module, @@ -227,6 +276,7 @@ protected function initRoutes() : void { 'action' => $action ], $_POST)); } + if(!method_exists($module->getInstance(), 'content') && !method_exists($module->getInstance(), 'frame')) { $this->template->display('error/module_empty', array_merge($this->template->getAssigns(), [ 'module' => $module, @@ -236,42 +286,52 @@ protected function initRoutes() : void { return; } + $this->template->display('module', array_merge($this->template->getAssigns(), [ 'module' => $module, 'submodule' => $submodule, 'action' => $action ])); }); + $this->router->addRoute('^/lost-password(?:/([a-zA-Z0-9\-_]+))?$', function(?string $token = null) { if(Auth::isLoggedIn()) { Response::redirect('/'); } + $this->template->display('lost-password', [ 'token' => $token ]); }); + $this->router->addRoute('/login', function() { if(Auth::isLoggedIn()) { Response::redirect('/'); } + $this->template->display('login'); }); + $this->router->addRoute('/ajax', function() { if(!Auth::isLoggedIn()) { header('HTTP/1.1 403 Forbidden'); require_once(dirname(PATH).'/placeholder/errors/403.html'); exit(); } + if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') { header('HTTP/1.1 405 Method Not Allowed'); require_once(dirname(PATH).'/placeholder/errors/405.html'); exit(); } + $this->getHooks()->runAction('ajax'); $this->router->run(true); + // Fire modals if(!empty($_POST['modal'])) { $modals = $this->getHooks()->applyFilter('modals', []); + if(count($modals) == 0) { print 'No registred modals.'; } else { @@ -279,9 +339,11 @@ protected function initRoutes() : void { if($modal->getName() === $_POST['modal']) { $callback = $modal->getCallback('save'); $data = []; + foreach($_POST as $name => $value) { if(is_array($value)) { $data[trim($name)] = []; + foreach($value as $key => $entry) { $data[trim($name)][trim($key)] = trim($entry); } @@ -289,31 +351,76 @@ protected function initRoutes() : void { $data[trim($name)] = trim($value); } } + $result = call_user_func_array($callback, [ $data ]); + if(is_bool($result)) { print json_encode($result); return; } + print $result; } } } - /* Module Info */ + + /* Modules */ + } else if(!empty($_POST['search'])) { + $query = $_POST['search']; + $results = []; + $repositorys = Installer::getRepositorys(); + + foreach($repositorys as $entry) { + $repository = Installer::getRepository($entry->id); + $content = Installer::getFile($repository, 'modules.list'); + if(!empty($content)) { + $modules = explode(PHP_EOL, $content.PHP_EOL); + foreach($modules as $name) { + if(empty($name)) { + continue; + } + // @ToDo Cache + $name = trim($name); + $info = Installer::getFile($repository, sprintf('%s/module.package', $name)); + $info = json_decode($info); + if(!empty($info)) { + $info->installed = $this->getModules()->hasModule($name, true); + } + $info->icon_raw = $info->icon; + if(preg_match('/^(http|https|data):/', $info->icon)) { + $info->icon = sprintf('', $info->icon); + } else if(str_starts_with($info->icon, '/')) { + $info->icon = sprintf('', $info->icon); + } else { + $info->icon = sprintf('', $info->icon); + } + if($query === 'null' || (!empty($query) && preg_match('/'.$query.'/Uis', $info->name) || preg_match('/'.$query.'/Uis', $info->description))) { + $results[$name] = $info; + } + } + } + } + + print json_encode($results); + + /* Module Info */ } else if(!empty($_POST['module'])) { if(!Auth::hasPermission('MODULES::INSTALL')) { print 'NO_PERMISSIONS'; - return; } + $repositorys = Installer::getRepositorys(); + if(count($repositorys) === 0) { print 'NO_REPOSITORYS'; - return; } + $found = null; $repo = null; + foreach($repositorys as $repository) { $content = Installer::getFile($repository, 'modules.list'); if(!($content == Repository::FORBIDDEN || empty($content))) { @@ -330,20 +437,24 @@ protected function initRoutes() : void { } } } + if(empty($found)) { print 'MODULE_NOT_FOUND'; - return; } + $info = Installer::getFile($repo, sprintf('%s/module.package?time=%d', $found, time())); + if(empty($info)) { print 'MODULE_EMPTY'; return; } + $info = json_decode($info); $screenshots = []; $info->icon_raw = $info->icon; + /* Icon */ if(preg_match('/^(http|https|data):/', $info->icon)) { $info->icon = sprintf('', $info->icon); @@ -352,26 +463,31 @@ protected function initRoutes() : void { } else { $info->icon = sprintf('', $info->icon); } + /* Readme */ $readme = Installer::getFile($repo, sprintf('%s/README.md?time=%d', $found, time())); if(empty($readme)) { $readme = ''; } + /* Screenshots */ if(isset($info->screenshots)) { // Fix Github Repos if(preg_match('/github\.com\/([^\/]+)\/(.*)(?:\/)?$/Uis', $repo->url, $matches)) { $repo->url = sprintf('https://raw.githubusercontent.com/%s/%s/master', $matches[1], rtrim($matches[2], '/')); } + foreach($info->screenshots as $screenshot) { $screenshots[] = sprintf('%s/%s/%s?raw=true', $repo->url, $found, $screenshot->file); } } + /* Changelog */ $changelog = Installer::getFile($repo, sprintf('%s/CHANGELOG.md?time=%d', $found, time())); if(empty($changelog)) { $changelog = ''; } + /* Features */ // @ToDo Analyze module files for these informations... $features = [ @@ -439,5 +555,4 @@ public function setSettings(string $name, mixed $value = null) : void { $this->cache->set('settings', $name, $value); } } - - ?> \ No newline at end of file +?> \ No newline at end of file diff --git a/classes/System/CoreAdmin.class.php b/classes/System/CoreAdmin.class.php index 45fc70b..7bd54f9 100644 --- a/classes/System/CoreAdmin.class.php +++ b/classes/System/CoreAdmin.class.php @@ -47,7 +47,7 @@ public function init() : void { 'tab' => $tab, 'action' => $action, 'admin' => true - ]) == false) { + ]) === false) { $this->getTemplate()->display('error/404'); } }); @@ -60,7 +60,7 @@ public function init() : void { if($this->getTemplate()->display('server' . (!empty($destination) ? sprintf('/%s', $destination) : ''), [ 'tab' => $tab, 'action' => $action - ]) == false) { + ]) === false) { $this->getTemplate()->display('error/404'); } }); diff --git a/classes/System/Update.class.php b/classes/System/Update.class.php index 326f185..f3ea50b 100644 --- a/classes/System/Update.class.php +++ b/classes/System/Update.class.php @@ -8,9 +8,6 @@ */ namespace fruithost\System; - - use fruithost\Storage\Database; - use fruithost\System\Core; class Update { protected static Core $core; @@ -45,7 +42,7 @@ public static function run() { 'host' => $_SERVER['SERVER_NAME'], 'ip' => $_SERVER['SERVER_ADDR'], 'admin' => $_SERVER['SERVER_ADMIN'], - 'ssl' => isset($_SERVER['HTTPS']) ? ($_SERVER['HTTPS'] == 'on') : false + 'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ]); /* Refresh License */ @@ -73,7 +70,7 @@ protected static function get($action, $data) { if($json != NULL) { $response = $json; } - } catch(Exception $e) { + } catch(\Exception $e) { } @@ -93,7 +90,7 @@ public static function getLicense() : string { 'host' => $_SERVER['SERVER_NAME'], 'ip' => $_SERVER['SERVER_ADDR'], 'admin' => $_SERVER['SERVER_ADMIN'], - 'ssl' => isset($_SERVER['HTTPS']) ? ($_SERVER['HTTPS'] == 'on') : false + 'ssl' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ]); self::$core->setSettings('UPDATE_LICENSE', $result->license); diff --git a/classes/System/Utils.class.php b/classes/System/Utils.class.php index 7cb64a1..38ee429 100644 --- a/classes/System/Utils.class.php +++ b/classes/System/Utils.class.php @@ -12,6 +12,33 @@ use fruithost\Localization\I18N; class Utils { + public static function getMimeType($file) : string { + $mime = mime_content_type($file); + $extension = pathinfo($file, PATHINFO_EXTENSION); + $additional = [ + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'css' => 'text/css' + ]; + + /* Set MIME-Type when not given */ + if(!$mime) { + if(array_key_exists($extension, $additional)) { + return $additional[$extension]; + } + } + + /* Override misconfigured MIME-Types */ + if(array_key_exists($extension, $additional)) { + if($mime !== $additional[$extension]) { + return $additional[$extension]; + } + } + + return $mime; + } + public static function randomString(int $length = 10) : string { $characters = 'abcdefghijklmonpqrstuvwxyz-_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $charactersLength = strlen($characters); diff --git a/classes/Templating/Template.class.php b/classes/Templating/Template.class.php index 7c43ebe..156e40f 100644 --- a/classes/Templating/Template.class.php +++ b/classes/Templating/Template.class.php @@ -27,16 +27,16 @@ class Template extends TemplateDefaults { private ?string $path = null; public function __construct(Core $core) { - $this->path = PATH; + $this->path = dirname(PATH); $this->core = $core; $this->files = new TemplateFiles(); $this->navigation = new TemplateNavigation($this->core); - $this->theme = $this->core->getHooks()->applyFilter('theme_name', 'default'); + $this->theme = $this->core->getHooks()->applyFilter('theme_name', $this->getDefaultTheme()); $this->assigns = [ 'project_name' => $this->core->getSettings('PROJECT_NAME', 'fruithost'), 'project_copyright' => $this->core->getSettings('PROJECT_COPYRIGHT', true) ]; - + // gzip, deflate, br, zstd if(isset($_SERVER['HTTP_ACCEPT_ENCODING']) && substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) { @ob_start('ob_gzhandler'); @@ -55,11 +55,8 @@ public function __construct(Core $core) { $this->files->addStylesheet('bootstrap', $this->url('css/bootstrap/bootstrap.min.css'), '5.3.2'); $this->files->addStylesheet('bootstrap-icons', $this->url('fonts/bootstrap-icons/bootstrap-icons.css'), '1.11.1', [ 'bootstrap' ]); $this->files->addStylesheet('cascadia-mono', $this->url('fonts/cascadia-mono/cascadia-mono.css'), '2111.01', [ 'bootstrap' ]); - $this->files->addStylesheet('global', $this->url('css/global.css'), '1.0.0', [ 'bootstrap' ]); - $this->files->addJavascript('bootstrap', $this->url('js/bootstrap/bootstrap.bundle.min.js'), '5.3.2', [], TemplateFiles::FOOTER); - $this->files->addJavascript('global', $this->url('js/global.js'), '1.0.0', [ 'bootstrap' ], TemplateFiles::FOOTER); - $this->files->addJavascript('ajax', $this->url('js/ajax.js'), '1.0.0', [ 'bootstrap' ], TemplateFiles::FOOTER); - + $this->files->addJavascript('bootstrap', $this->url('js/bootstrap/bootstrap.bundle.min.js'), '5.3.2', [], TemplateFiles::FOOTER); + $this->navigation->addCategory('account', I18N::get('Account')); $this->navigation->addCategory('database', I18N::get('Databases')); $this->navigation->addCategory('domain', I18N::get('Domains')); @@ -72,11 +69,26 @@ public function __construct(Core $core) { $this->navigation->addCategory('server', I18N::get('Server')); $this->assign('topbar', $this->navigation->getCategory('account')); - $this->assign('navigation', $this->navigation); + $this->assign('navigation', $this->navigation); - $this->core->getHooks()->runAction('template_init'); + $this->core->getHooks()->runAction('template_init', $this); } - + + public function getDefaultTheme() { + $theme = 'default'; + + if(isset($_GET['theme'])) { + $theme = $_GET['theme']; + } + + // @ToDo Check if Theme-Directory exists + if($theme != 'default') { + + } + + return $theme; + } + public function getCore() : Core { return $this->core; } @@ -93,6 +105,23 @@ public function getAssigns() : array { return $this->assigns; } + public function resolveThemePath($file, $handler = false) : string { + $entry = 'default'; + + if($handler) { + $entry = 'handler'; + $theme = sprintf('%1$s%2$sthemes%2$shandler%2$s%4$s%2$s%3$s.php', $this->path, DS, $file, $this->theme); + } else { + $theme = sprintf('%1$s%2$sthemes%2$s%4$s%2$s%3$s.php', $this->path, DS, $file, $this->theme); + } + + if(!file_exists($theme)) { + $theme = sprintf('%1$s%4$s%2$s%3$s.php', PATH, DS, $file, $entry); + } + + return $theme; + } + public function setPath($path) { $this->path = $path; } @@ -122,7 +151,16 @@ public function assign(string $name, mixed $value) : void { public function display(string $file, array $arguments = [], bool $basedir = true, bool $once = true) : ?bool { $template = $this; - + $functions = $this->resolveThemePath('functions'); + + if(file_exists($functions)) { + if($once) { + require_once($functions); + } else { + require($functions); + } + } + $this->core->getHooks()->runAction('html_render'); foreach($arguments AS $name => $value) { @@ -130,13 +168,13 @@ public function display(string $file, array $arguments = [], bool $basedir = tru } if($basedir) { - $path = sprintf('%1$s%2$sthemes%2$s%4$s%2$s%3$s.php', $this->path, DS, $file, $this->theme); + $path = $this->resolveThemePath($file); } else { - $path = $file; + $path = $file; } - $handler = sprintf('%1$shandler%2$s%3$s.php', $this->path, DS, $file); - + $handler = $this->resolveThemePath($file, true); + foreach($this->assigns AS $name => $value) { ${$name} = $value; } @@ -165,27 +203,6 @@ public function display(string $file, array $arguments = [], bool $basedir = tru } return true; - } else { - if(!Auth::isLoggedIn()) { - $this->getFiles()->addStylesheet('login', $this->url('css/login.css'), '2.0.0', [ 'bootstrap' ]); - $this->getFiles()->addJavascript('login', $this->url('js/login.js'), '1.0.0', [ 'bootstrap' ], TemplateFiles::FOOTER); - } else { - $this->getFiles()->addStylesheet('style', $this->url('css/style.css'), '2.0.0', [ 'bootstrap' ]); - $this->getFiles()->addJavascript('ui', $this->url('js/ui.js'), '2.0.0', [ 'bootstrap' ], TemplateFiles::FOOTER); - $this->getFiles()->addJavascript('codemirror', $this->url('js/codemirror/build/bundle.min.js'), '6.0.0', [ 'ui' ], TemplateFiles::FOOTER); - } - - $path = sprintf('%1$s%2$sdefault%2$s%3$s.php', $this->path, DS, $file); - - if(file_exists($path) && is_readable($path)) { - if($once) { - require_once($path); - } else { - require($path); - } - - return true; - } } return false; @@ -193,27 +210,20 @@ public function display(string $file, array $arguments = [], bool $basedir = tru public function header() : void { $template = $this; - $path = sprintf('%1$s%2$sthemes%2$s%4$s%2$s%3$s.php', $this->path, DS, 'header', $this->theme); - + $path = $this->resolveThemePath('header'); + foreach($this->assigns AS $name => $value) { ${$name} = $value; } - if(file_exists($path)) { @require_once($path); - } else { - $path = sprintf('%1$s%2$sdefault%2$s%3$s.php', $this->path, DS, 'header'); - - if(file_exists($path)) { - @require_once($path); - } } } public function footer() : void { $template = $this; - $path = sprintf('%1$s%2$sthemes%2$s%4$s%2$s%3$s.php', $this->path, DS, 'footer', $this->theme); + $path = $this->resolveThemePath('footer'); foreach($this->assigns AS $name => $value) { ${$name} = $value; @@ -221,12 +231,6 @@ public function footer() : void { if(file_exists($path)) { require_once($path); - } else { - $path = sprintf('%1$s%2$sdefault%2$s%3$s.php', $this->path, DS, 'footer'); - - if(file_exists($path)) { - @require_once($path); - } } } diff --git a/classes/Templating/TemplateDefaults.class.php b/classes/Templating/TemplateDefaults.class.php index e43aa4f..4829429 100644 --- a/classes/Templating/TemplateDefaults.class.php +++ b/classes/Templating/TemplateDefaults.class.php @@ -17,7 +17,6 @@ public function head_robots() : void { } public function head_description() : void { - $description = I18N::get('fruithost is an OpenSource Hosting-Panel for some Linux Distributions (later maybe also for Windows). With fruithost you get a real alternative to confixx/Plesk, ISPConfig, froxlor and other derivatives.'); $description .= I18N::get('Whether private or professional, with fruithost you get a clean and simple solution to manage your servers.'); $description .= I18N::get('Security comes first at fruithost - that\'s why we only use up-to-date software and do without miserable old solutions. If you have an MIT license, you can use fruithost for any scenario!'); diff --git a/classes/Templating/TemplateNavigationCategory.class.php b/classes/Templating/TemplateNavigationCategory.class.php index 1048a62..4636df8 100644 --- a/classes/Templating/TemplateNavigationCategory.class.php +++ b/classes/Templating/TemplateNavigationCategory.class.php @@ -198,5 +198,4 @@ public function getEntries() : array { return $results; } } - - ?> \ No newline at end of file +?> \ No newline at end of file diff --git a/default/account.php b/default/account.php index 69d7a1d..ef8b6b6 100644 --- a/default/account.php +++ b/default/account.php @@ -1,4 +1,12 @@ header(); if(!Auth::hasPermission('MODULES::*')) { ?> - + footer(); exit(); } + if(Auth::hasPermission('MODULES::HANDLE') && isset($_GET['settings'])) { ?> -
+ @@ -83,11 +87,9 @@ class="btn btn-sm btn-outline-success">