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::*')) {
?>
-