diff --git a/.htaccess b/.htaccess index ac1bc2f..33564f1 100644 --- a/.htaccess +++ b/.htaccess @@ -1,14 +1,16 @@ php_flag display_errors on Options -Indexes +DirectorySlash Off + RewriteEngine On RewriteRule ^api(/.*)?$ /index.php?api=$1 [L,QSA] - - RewriteEngine On - DirectorySlash Off - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d [OR] - RewriteCond %{REQUEST_URI} "(\.idea|\.git|src|test|core)(/.*)?" - RewriteRule ^(.*)$ /index.php?site=$1 [L,QSA] - \ No newline at end of file +RewriteEngine On +RewriteOptions AllowNoSlash +RewriteRule ^((\.idea|\.git|src|test|core|static)(/.*)?)$ /index.php?site=$1 [L,QSA] + +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ /index.php?site=$1 [L,QSA] \ No newline at end of file diff --git a/core/Configuration/CreateDatabase.class.php b/core/Configuration/CreateDatabase.class.php index 6eeb3df..d32d9bc 100755 --- a/core/Configuration/CreateDatabase.class.php +++ b/core/Configuration/CreateDatabase.class.php @@ -133,7 +133,7 @@ class CreateDatabase { ->addRow("^/register(/)?$", "dynamic", "\\Documents\\Account", "\\Views\\Account\\Register") ->addRow("^/confirmEmail(/)?$", "dynamic", "\\Documents\\Account", "\\Views\\Account\\ConfirmEmail") ->addRow("^/acceptInvite(/)?$", "dynamic", "\\Documents\\Account", "\\Views\\Account\\AcceptInvite") - ->addRow("^/$", "dynamic", "\\Documents\\Welcome", NULL); + ->addRow("^/$", "static", "/static/welcome.html", NULL); return $queries; } diff --git a/core/Documents/Admin.class.php b/core/Documents/Admin.class.php index 9389ccf..f0bbc29 100644 --- a/core/Documents/Admin.class.php +++ b/core/Documents/Admin.class.php @@ -29,12 +29,7 @@ namespace Documents\Admin { } protected function initSources() { -// $this->loadJQuery(); $this->loadFontawesome(); -// $this->addJS(Script::CORE); -// $this->addCSS(Link::CORE); -// $this->addJS(Script::ADMIN); -// $this->addCSS(Link::ADMIN); } protected function initMetas() { diff --git a/core/Documents/Document404.class.php b/core/Documents/Document404.class.php index b93523e..fa97af3 100644 --- a/core/Documents/Document404.class.php +++ b/core/Documents/Document404.class.php @@ -17,6 +17,7 @@ namespace Documents\Document404 { use Elements\Body; use Elements\Head; + use Elements\SimpleBody; use Views\View404; class Head404 extends Head { @@ -47,7 +48,7 @@ namespace Documents\Document404 { } } - class Body404 extends Body { + class Body404 extends SimpleBody { public function __construct($document) { parent::__construct($document); @@ -57,10 +58,8 @@ namespace Documents\Document404 { http_response_code(404); } - public function getCode() { - $html = parent::getCode(); - $html .= "" . (new View404($this->getDocument())) . ""; - return $html; + protected function getContent() { + return $this->load(View404::class); } } } diff --git a/core/Documents/Welcome.class.php b/core/Documents/Welcome.class.php deleted file mode 100644 index b573e06..0000000 --- a/core/Documents/Welcome.class.php +++ /dev/null @@ -1,78 +0,0 @@ -loadBootstrap(); - } - - protected function initMetas() { - return array( - array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), - array('name' => 'format-detection', 'content' => 'telephone=yes'), - array('charset' => 'utf-8'), - array("http-equiv" => 'expires', 'content' => '0'), - array("name" => 'robots', 'content' => 'noarchive'), - ); - } - - protected function initRawFields() { - return array(); - } - - protected function initTitle() { - return "Welcome"; - } - } - - class WelcomeBody extends SimpleBody { - - public function __construct($document) { - parent::__construct($document); - } - - protected function getContent() { - return - "
-
-
-
-

Congratulations!

-

Your Web-Base Installation is now ready to use!

-
-

- You can now login into your Administrator Dashboard to adjust your settings - and add routes & pages. - You can add new documents and views by adding classes in the corresponding - directories and link to them, by creating rules in the Administrator Dashboard. -

-
-
-
-
"; - } - } -} \ No newline at end of file diff --git a/core/Elements/Head.class.php b/core/Elements/Head.class.php index ac071ab..a69928d 100644 --- a/core/Elements/Head.class.php +++ b/core/Elements/Head.class.php @@ -15,6 +15,7 @@ abstract class Head extends View { function __construct($document) { parent::__construct($document); $this->sources = array(); + $this->searchable = false; $this->metas = $this->initMetas(); $this->rawFields = $this->initRawFields(); $this->title = $this->initTitle(); diff --git a/core/Elements/View.class.php b/core/Elements/View.class.php index 3b16f2f..873c458 100644 --- a/core/Elements/View.class.php +++ b/core/Elements/View.class.php @@ -2,6 +2,8 @@ namespace Elements; +use External\PHPMailer\Exception; + abstract class View extends StaticView { private Document $document; @@ -25,6 +27,21 @@ abstract class View extends StaticView { public function isSearchable() { return $this->searchable; } public function getReference() { return $this->reference; } + protected function load(string $viewClass) : string { + try { + $reflectionClass = new \ReflectionClass($viewClass); + if ($reflectionClass->isSubclassOf(View::class) && $reflectionClass->isInstantiable()) { + $view = $reflectionClass->newInstanceArgs(array($this->getDocument())); + $view->loadView(); + return $view; + } + } catch(\ReflectionException $e) { + error_log($e->getMessage()); + } + + return ""; + } + private function loadLanguageModules() { $lang = $this->document->getUser()->getLanguage(); foreach($this->langModules as $langModule) { diff --git a/core/Views/LanguageFlags.class.php b/core/Views/LanguageFlags.class.php index 5a76854..381ef1b 100644 --- a/core/Views/LanguageFlags.class.php +++ b/core/Views/LanguageFlags.class.php @@ -11,9 +11,11 @@ class LanguageFlags extends View { public function __construct($document) { parent::__construct($document); $this->languageFlags = array(); + $this->searchable = false; } - public function getCode() { + public function loadView() { + parent::loadView(); $request = new \Api\Language\Get($this->getDocument()->getUser()); if ($request->execute()) { @@ -51,8 +53,10 @@ class LanguageFlags extends View { "\"$langName\"" ); } - - return implode('', $this->languageFlags); } } + + public function getCode() { + return implode('', $this->languageFlags); + } } \ No newline at end of file diff --git a/core/Views/LoginBody.class.php b/core/Views/LoginBody.class.php index fb089f5..25e1c73 100644 --- a/core/Views/LoginBody.class.php +++ b/core/Views/LoginBody.class.php @@ -32,7 +32,7 @@ class LoginBody extends Body { $backToStartPage = L("Back to Start Page"); $stayLoggedIn = L("Stay logged in"); - $flags = new LanguageFlags($this->getDocument()); + $flags = $this->load(LanguageFlags::class); $iconBack = $this->createIcon("arrow-circle-left"); $domain = $_SERVER['HTTP_HOST']; $protocol = getProtocol(); diff --git a/core/core.php b/core/core.php index 26af7e6..a75510f 100644 --- a/core/core.php +++ b/core/core.php @@ -1,104 +1,122 @@ 0) { - $numCharacters = 26 + 26 + 10; // a-z + A-Z + 0-9 - for ($i = 0; $i < $length; $i++) - { - try { - $num = random_int(0, $numCharacters - 1); - } catch (Exception $e) { - $num = rand(0, $numCharacters - 1); - } - - if($num < 26) $randomString .= chr(ord('a') + $num); - else if($num - 26 < 26) $randomString .= chr(ord('A') + $num - 26); - else $randomString .= chr(ord('0') + $num - 26 - 26); - } - } - - return $randomString; - } - - function startsWith($haystack, $needle) { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); - } - - function endsWith($haystack, $needle) { - $length = strlen($needle); - if ($length == 0) - return true; - - return (substr($haystack, -$length) === $needle); - } - - function intendCode($code, $escape=true) { - $newCode = ""; - $first = true; - $brackets = array(); - $intend = 0; - - foreach(explode("\n", $code) as $line) { - if(!$first) $newCode .= "\n"; - if($escape) $line = htmlspecialchars($line); - $line = trim($line); - - if(count($brackets) > 0 && startsWith($line, current($brackets))) { - $intend = max(0, $intend - 2); - array_pop($brackets); +function generateRandomString($length): string { + $randomString = ''; + if ($length > 0) { + $numCharacters = 26 + 26 + 10; // a-z + A-Z + 0-9 + for ($i = 0; $i < $length; $i++) { + try { + $num = random_int(0, $numCharacters - 1); + } catch (Exception $e) { + $num = rand(0, $numCharacters - 1); } - $newCode .= str_repeat(" ", $intend); - $newCode .= $line; - $first = false; + if ($num < 26) $randomString .= chr(ord('a') + $num); + else if ($num - 26 < 26) $randomString .= chr(ord('A') + $num - 26); + else $randomString .= chr(ord('0') + $num - 26 - 26); + } + } - if(endsWith($line, "{")) { - $intend += 2; - array_push($brackets, "}"); - } else if(endsWith($line, "(")) { - $intend += 2; - array_push($brackets, ")"); - } + return $randomString; +} + +function startsWith($haystack, $needle) { + $length = strlen($needle); + return (substr($haystack, 0, $length) === $needle); +} + +function endsWith($haystack, $needle) { + $length = strlen($needle); + if ($length == 0) + return true; + + return (substr($haystack, -$length) === $needle); +} + +function intendCode($code, $escape = true) { + $newCode = ""; + $first = true; + $brackets = array(); + $intend = 0; + + foreach (explode("\n", $code) as $line) { + if (!$first) $newCode .= "\n"; + if ($escape) $line = htmlspecialchars($line); + $line = trim($line); + + if (count($brackets) > 0 && startsWith($line, current($brackets))) { + $intend = max(0, $intend - 2); + array_pop($brackets); } - return $newCode; - } + $newCode .= str_repeat(" ", $intend); + $newCode .= $line; + $first = false; - function replaceCssSelector($sel) { - return preg_replace("~[.#<>]~", "_", preg_replace("~[:\-]~", "", $sel)); - } - - function getClassPath($class, $suffix=true) { - $path = str_replace('\\', '/', $class); - $path = array_values(array_filter(explode("/", $path))); - - if (strcasecmp($path[0], "api") === 0 && count($path) > 2 && strcasecmp($path[1], "Parameter") !== 0) { - $path = "Api/" . $path[1] . "API"; - } else { - $path = implode("/", $path); + if (endsWith($line, "{")) { + $intend += 2; + array_push($brackets, "}"); + } else if (endsWith($line, "(")) { + $intend += 2; + array_push($brackets, ")"); } - - $suffix = ($suffix ? ".class" : ""); - return "core/$path$suffix.php"; } - function createError($msg) { - return json_encode(array("success" => false, "msg" => $msg)); + return $newCode; +} + +function replaceCssSelector($sel) { + return preg_replace("~[.#<>]~", "_", preg_replace("~[:\-]~", "", $sel)); +} + +function getClassPath($class, $suffix = true) { + $path = str_replace('\\', '/', $class); + $path = array_values(array_filter(explode("/", $path))); + + if (strcasecmp($path[0], "api") === 0 && count($path) > 2 && strcasecmp($path[1], "Parameter") !== 0) { + $path = "Api/" . $path[1] . "API"; + } else { + $path = implode("/", $path); } + + $suffix = ($suffix ? ".class" : ""); + return "core/$path$suffix.php"; +} + +function createError($msg) { + return json_encode(array("success" => false, "msg" => $msg)); +} + +function serveStatic(string $webRoot, string $file): string { + + $path = realpath($webRoot . "/" . $file); + if (!startsWith($path, $webRoot . "/")) { + http_response_code(406); + return "Access restricted, requested file outside web root: " . htmlspecialchars($path); + } + + if (!file_exists($path) || !is_file($path) || !is_readable($path)) { + http_response_code(500); + return "Unable to read file: " . htmlspecialchars($path); + } + + $pathInfo = pathinfo($file); + + // maybe I will allow more later… + $allowedExtension = array("html", "htm"); + $ext = $pathInfo["extension"] ?? ""; + if (!in_array($ext, $allowedExtension)) { + http_response_code(406); + return "Access restricted: Extension '" . htmlspecialchars($ext) . "' not allowed."; + } + + $mimeType = mime_content_type($file); + header("Content-Type: $mimeType"); + return readfile($file); +} diff --git a/index.php b/index.php index 7ead9e1..5136212 100644 --- a/index.php +++ b/index.php @@ -89,6 +89,7 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) { $success = $req->execute(array("request" => $documentName)); $response = ""; if (!$success) { + http_response_code(500); $response = "Unable to find route: " . $req->getLastError(); } else { $route = $req->getResult()["route"]; @@ -106,8 +107,8 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) { header("Location: $target"); break; case "static": - http_response_code(501); - $response = "Not implemented yet."; + $currentDir = dirname(__FILE__); + $response = serveStatic($currentDir, $target); break; case "dynamic": $view = $route["extra"] ?? ""; diff --git a/static/welcome.html b/static/welcome.html new file mode 100644 index 0000000..4aeac1e --- /dev/null +++ b/static/welcome.html @@ -0,0 +1,30 @@ + + + + + + + + Welcome + + + + +
+
+
+
+

Congratulations!

+

Your Web-Base Installation is now ready to use!

+
+

+ You can now login into your Administrator Dashboard to adjust your settings + and add routes & pages. + You can add new documents and views by adding classes in the corresponding + directories and link to them, by creating rules in the Administrator Dashboard. +

+
+
+
+
+ \ No newline at end of file