From 4892cb9f26925cb64bacbe016b78259f3fa33033 Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Sat, 11 Nov 2023 20:32:41 -0500 Subject: [PATCH] various fixes, improvements, and feature additions --- app/lib/phast/System.inc.php | 290 ++++++++++++++++++++++++++++---- app/lib/phast/WebPage.inc.php | 141 +++++++++++----- app/lib/phast/WebScript.inc.php | 8 +- 3 files changed, 354 insertions(+), 85 deletions(-) diff --git a/app/lib/phast/System.inc.php b/app/lib/phast/System.inc.php index dca4d79..a0e9f16 100644 --- a/app/lib/phast/System.inc.php +++ b/app/lib/phast/System.inc.php @@ -84,6 +84,8 @@ */ class System { + public static $GlobalVariables; + private static $RequestURL; public static function GetRequestURL() { @@ -141,6 +143,11 @@ public static function GetTenantName() { + if (is_callable(System::$BeforeGetTenantNameHandler)) + { + return call_user_func(System::$BeforeGetTenantNameHandler); + } + if (System::$EnableTenantedHosting) { $path = System::GetVirtualPath(false); @@ -150,7 +157,18 @@ } return $path[0]; } - return ""; + else + { + if (System::$TenantName != null) + { + return System::$TenantName; + } + else + { + return $_SESSION["CurrentTenantName"]; + } + } + return null; } /** @@ -175,7 +193,8 @@ public static function WriteErrorLog($message) { - $caller = next(debug_backtrace()); + $bt = debug_backtrace(); + $caller = next($bt); trigger_error($message . " (in '" . $caller['function'] . "' called from '" . $caller['file'] . "' on line " . $caller['line'] . ")"); } @@ -201,8 +220,8 @@ * Retrieves the value of the global configuration property with the given key if it is defined, * or the default value if it has not been defined. * @param string $key The key of the configuration property to search for. - * @param string $defaultValue The value to return if the global configuration property with the specified key has not been defined. - * @return string The value of the global configuration property with the given key if defined; otherwise, defaultValue. + * @param mixed $defaultValue The value to return if the global configuration property with the specified key has not been defined. + * @return mixed The value of the global configuration property with the given key if defined; otherwise, defaultValue. */ public static function GetConfigurationValue($key, $defaultValue = null) { @@ -327,6 +346,11 @@ * @var callable */ public static $ErrorEventHandler; + /** + * The event handler that is called before retrieving a tenant name + * @var callable + */ + public static $BeforeGetTenantNameHandler; /** * The event handler that is called before this application executes. @@ -343,12 +367,28 @@ * Redirects the user to the specified path via a Location header. * @param string $path The expandable string path to navigate to. */ - public static function Redirect($path) + public static function Redirect($path, $forceNonTenanted = false) { - $realpath = System::ExpandRelativePath($path); + $realpath = System::ExpandRelativePath($path, false, $forceNonTenanted); header("Location: " . $realpath); return; } + + public static function MapVirtualPath($path) + { + $torepl = System::GetConfigurationValue("Application.BasePath"); + $torepl_nontenanted = $torepl; + + $retval_nontenanted = str_replace("~", $torepl_nontenanted, $path); + $physicalFilePath = System::GetApplicationPath() . $retval_nontenanted; + + if (file_exists($physicalFilePath)) + { + return $physicalFilePath; + } + return null; + } + /** * Expands the given path by replacing the tilde character (~) with the value of the * configuration property Application.BasePath. @@ -356,16 +396,20 @@ * @param boolean $includeServerInfo True if server information should be included in the response; false otherwise. * @return string The expanded form of the given expandable string path. */ - public static function ExpandRelativePath($path, $includeServerInfo = false) + public static function ExpandRelativePath($path, $includeServerInfo = false, $forceNonTenanted = false) { $torepl = System::GetConfigurationValue("Application.BasePath"); $torepl_nontenanted = $torepl; - $tenantName = System::GetTenantName(); - if ($tenantName != "") + + if (!$forceNonTenanted) { - $torepl .= "/" . $tenantName; + $tenantName = System::GetTenantName(); + if ($tenantName != null) + { + $torepl .= "/" . $tenantName; + } } - + $retval_nontenanted = str_replace("~", $torepl_nontenanted, $path); $physicalFilePath = System::GetApplicationPath() . $retval_nontenanted; @@ -381,6 +425,7 @@ if ($includeServerInfo) { // from http://stackoverflow.com/questions/6768793/php-get-the-full-url + $s = ""; // idk $sp = strtolower($_SERVER["SERVER_PROTOCOL"]); $protocol = substr($sp, 0, strpos($sp, "/")) . $s; $port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]); @@ -490,7 +535,7 @@ } return $ret; } - public static function RedirectToLoginPage() + public static function RedirectToLoginPage($forceNonTenanted = false) { if (System::$EnableTenantedHosting) { @@ -500,7 +545,7 @@ { $_SESSION["System.LastRedirectURL"] = $_SERVER["REQUEST_URI"]; } - System::Redirect(System::GetConfigurationValue("LoginPageRedirectURL", "~/account/login")); + System::Redirect(System::ReplaceVariables(System::GetConfigurationValue("LoginPageRedirectURL", "~/account/login")), $forceNonTenanted); return; } public static function RedirectFromLoginPage() @@ -531,7 +576,7 @@ { $array = explode("/", $_GET["virtualpath"]); - if ($supportTenantedHosting) + if ($supportTenantedHosting && System::$EnableTenantedHosting) { $tenantName = System::GetTenantName(); if ($tenantName != "") @@ -545,6 +590,17 @@ } return array(); } + public static function GetVirtualPathString() : string + { + if (isset($_GET["virtualpath"])) + { + if ($_GET["virtualpath"] != null) + { + return $_GET["virtualpath"]; + } + } + return null; + } public static function IncludeFile($filename, $isRequired) { $filename = str_replace("~/", System::GetApplicationPath() . "/", $filename); @@ -564,7 +620,19 @@ // require_once changed to include_once to ensure that PHP configuration is not required for Phast 2.0 (Website.xml) sites include_once($RootPath . "/include/Configuration.inc.php"); - + + foreach (System::$IncludeFiles as $includeFile) + { + if ($includeFile->IsRequired) + { + require_once($RootPath . $includeFile->FileName); + } + else + { + include_once($RootPath . $includeFile->FileName); + } + } + // load the xml files in Configuration directory $a = glob($RootPath . "/include/Configuration/*.xml"); foreach ($a as $filename) @@ -600,29 +668,32 @@ } // Local MasterPages Code-Behind loader - $a = glob($RootPath . "/include/MasterPages/*.phpx.php"); - foreach ($a as $filename) + $pagesPaths = System::GetConfigurationValue("Paths.MasterPages", [ "/include/MasterPages" ]); + foreach ($pagesPaths as $pagesPath) { - require_once($filename); - } - // Local MasterPages loader - $a = glob($RootPath . "/include/MasterPages/*.phpx"); - foreach ($a as $filename) - { - System::$Parser->LoadFile($filename); - } - - // Local Pages Code-Behind loader - $a = glob($RootPath . "/include/Pages/*.phpx.php"); - foreach ($a as $filename) - { - require_once($filename); + $a = glob($RootPath . $pagesPath . "/*.phpx.php"); + foreach ($a as $filename) + { + require_once($filename); + } + // Local MasterPages loader + $a = glob($RootPath . $pagesPath . "/*.phpx"); + foreach ($a as $filename) + { + System::$Parser->LoadFile($filename); + } } // Local Pages loader $pagesPaths = System::GetConfigurationValue("Paths.Pages", [ "/include/Pages" ]); foreach ($pagesPaths as $pagesPath) { + // we need to load the include files BEFORE the phpx layout files + $a = glob($RootPath . $pagesPath . "/*.phpx.php"); + foreach ($a as $filename) + { + require_once($filename); + } $a = glob($RootPath . $pagesPath . "/*.phpx"); foreach ($a as $filename) { @@ -679,7 +750,145 @@ require_once($RootPath . "/include/Application.phpx.php"); } } - + + public static function ReplaceVariables($str) + { + $vars = array + ( + "CurrentTenantName" => System::GetTenantName() + ); + + foreach ($vars as $key => $value) + { + $str = str_replace("$(" . $key . ")", $value, $str); + } + return $str; + } + + /** + * Parses a path in the form /static/routes/{with}/dynamic/{variables}.htmld + * /static/routes/glbakdlsfjoaisf/dynamic/fds.htmld + * + * If the route matches, returns an array of the resulting path vars. + * If the route does not match, returns FALSE. + */ + private static function ParsePathStr(string $template, string $pathstr) + { + + $l = strlen($template); + $i = 0; + $j = 0; + $escape = false; + $insideVariable = false; + + $varname = null; + $varvalue = null; + + while ($i < $l) + { + if ($template[$i] == "\\") + { + $escape = true; + $i++; + continue; + } + + if (!$escape && ($template[$i] == "{" || ($template[$i] == "\$" && strlen($template) - $i > 0 && $template[$i + 1] == "("))) + { + $insideVariable = true; + $varname = ""; + $varvalue = ""; + $i++; + continue; + } + else if (!$escape && ($insideVariable && ($template[$i] == "}" || $template[$i] == ")"))) + { + $insideVariable = false; + $i++; + continue; + } + else + { + if ($insideVariable) + { + $varname .= $template[$i]; + $i++; + continue; + } + else + { + if ($template[$i] == $pathstr[$j]) + { + // yay, we match + // save the last-known variable, if any... + if ($varname != null && $varvalue != null) + { + $vars[$varname] = $varvalue; + + // don't forget to reset it + $varname = null; + $varvalue = null; + } + + // ... and keep going + $i++; + $j++; + } + else + { + // no match + if ($varname != null) + { + if ($j + 1 < strlen($pathstr)) + { + // we are currently reading a variable value + $varvalue .= $pathstr[$j]; + $j++; + } + else + { + // don't even question it, just return false since we should never reach this point + return false; + } + continue; + } + else + { + // we do not match! + return false; + } + + if ($varvalue != null) + { + // we are in a variable + $varvalue .= $pathstr[$j]; + $j++; + continue; + } + } + } + } + + $escape = false; + } + + if ($varvalue !== null && $j < strlen($pathstr)) + { + while ($j < strlen($pathstr)) + { + $varvalue .= $pathstr[$j]; + $j++; + } + + $vars[$varname] = $varvalue; + $varname = null; + $varvalue = null; + } + return $vars; + + } + + /** * Starts the Phast application. * @return boolean True if the launch succeeded; false if a failure occurred. @@ -766,10 +975,11 @@ { if (!$page->Enabled) continue; - $actualPathParts = $path; - // try to parse the path, for example: - // profile/$(username)/dashboard - + /* + // this is simple, but only works if all path parts are within the //s... + // for example, it fails on d/{onevar}/xxx/{somevar}.htmld + // because {somevar} is not entirely within // like {onevar} is + $pathParts = explode("/", $page->FileName); $pathPartCount = count($pathParts); $found = true; @@ -799,9 +1009,13 @@ } } } - if ($found) + */ + + $vars = System::ParsePathStr($page->FileName, System::GetVirtualPathString()); + if ($vars !== false) { $actualPage = $page; + $pathVars = $vars; break; } } @@ -964,6 +1178,8 @@ require("Configuration/V1/ConfigurationSpacer.inc.php"); System::$Configuration = array(); + System::$GlobalVariables = array(); + System::$EnableTenantedHosting = false; System::$IncludeFiles = array(); diff --git a/app/lib/phast/WebPage.inc.php b/app/lib/phast/WebPage.inc.php index b99d2bd..aa52d36 100644 --- a/app/lib/phast/WebPage.inc.php +++ b/app/lib/phast/WebPage.inc.php @@ -44,6 +44,12 @@ */ public $PhysicalFileName; + /** + * The name of the CSS stylesheet theme to use for this page. + * @var string + */ + public $ThemeName; + public function GetCanonicalFileName() { $path = System::GetVirtualPath(); @@ -64,6 +70,12 @@ */ public $Title; public $ClassList; + + /** + * Indicates whether the request is a postback. + * @var bool + */ + public $IsPostback; /** * The class reference for this WebPage. @@ -161,17 +173,29 @@ public function GetControlByID($id, $recurse = true) { $ctls = $this->GetAllControls(); - foreach ($ctls as $ctl) + if ($ctls === null) { - if ($ctl->ID == $id) return $ctl; - if ($recurse) - { - $ctl1 = $ctl->GetControlByID($id, true); - if ($ctl1 != null) return $ctl1; - } + // we really need to fix this sh!t + $ctls = $this->Page->GetAllControls(); + } + if ($ctls !== null) + { + foreach ($ctls as $ctl) + { + if ($ctl->ID == $id) return $ctl; + if ($recurse) + { + $ctl1 = $ctl->GetControlByID($id, true); + if ($ctl1 != null) return $ctl1; + } + } + + trigger_error("Control with ID '" . $id . "' not found"); + } + else + { + trigger_error("control collection null for " . __CLASS__); } - - trigger_error("Control with ID '" . $id . "' not found"); return null; } @@ -210,6 +234,11 @@ { $page->UseCompatibleRenderingMode = ($attUseCompatibleRenderingMode->Value == "true"); } + $attThemeName = $element->GetAttribute("ThemeName"); + if ($attThemeName != null) + { + $page->ThemeName = $attThemeName->Value; + } $tagScripts = $element->GetElement("Scripts"); if ($tagScripts != null) @@ -217,7 +246,7 @@ foreach ($tagScripts->Elements as $elem) { if (get_class($elem) != "UniversalEditor\\ObjectModels\\Markup\\MarkupTagElement") continue; - + $attContentType = $elem->GetAttribute("ContentType"); $contentType = "text/javascript"; if ($attContentType != null) $contentType = $attContentType->Value; @@ -340,12 +369,12 @@ } else { - System::WriteErrorLog("Code-behind for '" . $page->CodeBehindClassName . "' does not define an 'OnClassLoaded' entry point"); + //System::WriteErrorLog("Code-behind for '" . $page->CodeBehindClassName . "' does not define an 'OnClassLoaded' entry point"); } } else { - System::WriteErrorLog("Code-behind for '" . $page->CodeBehindClassName . "' not found"); + //System::WriteErrorLog("Code-behind for '" . $page->CodeBehindClassName . "' not found"); } } return $page; @@ -550,6 +579,13 @@ */ protected function OnRendering(RenderingEventArgs $e) { + if ($this->ClassReference != null) + { + if (method_exists($this->ClassReference, "OnRendering")) + { + $this->ClassReference->OnRendering($e); + } + } } /** @@ -558,6 +594,13 @@ */ protected function OnRendered(RenderedEventArgs $e) { + if ($this->ClassReference != null) + { + if (method_exists($this->ClassReference, "OnRendered")) + { + $this->ClassReference->OnRendered($e); + } + } } @@ -826,6 +869,8 @@ trigger_error("Could not initialize the WebPage"); return; } + + $handled = false; switch (System::$WebPageFormat) { @@ -930,12 +975,6 @@ header("Content-Type: text/html; charset=utf-8"); if (!$this->IsPartial) { - - if (!$this->UseCompatibleRenderingMode) - { - echo("\r\n"); - } - $tagHTML = new HTMLControl(); $tagHTML->TagName = "html"; @@ -996,7 +1035,16 @@ // ========== BEGIN: StyleSheets ========== $items = array(); - $items[] = new WebStyleSheet("$(Configuration:System.StaticPath)/StyleSheets/Main.css"); + if (System::GetConfigurationValue("Application.ThemeName") != null) + { + $items[] = new WebStyleSheet("$(Configuration:System.StaticPath)/" . "themes/" . System::GetConfigurationValue("Application.ThemeName") . "/theme.css"); + } + if ($this->ThemeName != null) + { + //$items[] = new WebStyleSheet("$(Configuration:System.StaticPath)/" . "themes/" . $this->ThemeName . "/theme.css"); + } + + #$items[] = new WebStyleSheet("$(Configuration:System.StaticPath)/StyleSheets/Main.css"); if ($this->MasterPage != null) { @@ -1134,36 +1182,49 @@ $re = new RenderingEventArgs(RenderMode::Complete); $this->OnRendering($re); if ($re->Cancel) return; + + $handled = $re->Handled; + if (!$handled && !$this->UseCompatibleRenderingMode) + { + echo("\r\n"); + } } else { $re = new RenderingEventArgs(RenderMode::Partial); $this->OnRendering($re); if ($re->Cancel) return; + + $handled = $re->Handled; } - $re = new RenderingEventArgs(RenderMode::Any); - $this->OnRendering($re); - if ($re->Cancel) return; - - if (is_callable($this->Content) && count($this->Controls) == 0) + + if (!$handled) { - $tagBODY->Content = $this->Content; - } - else if (!is_callable($this->Content) && count($this->Controls) > 0) - { - $controls = $this->Controls; - if ($this->MasterPage != null) + /* + $re = new RenderingEventArgs(RenderMode::Any); + $this->OnRendering($re); + if ($re->Cancel) return; + */ + if (is_callable($this->Content) && count($this->Controls) == 0) { - $controls = $this->MergeMasterPageControls($this->MasterPage->Controls); + $tagBODY->Content = $this->Content; } - - foreach ($controls as $ctl) + else if (!is_callable($this->Content) && count($this->Controls) > 0) { - $tagBODY->Controls[] = $ctl; + $controls = $this->Controls; + if ($this->MasterPage != null) + { + $controls = $this->MergeMasterPageControls($this->MasterPage->Controls); + } + + foreach ($controls as $ctl) + { + $tagBODY->Controls[] = $ctl; + } } + $tagHTML->Controls[] = $tagBODY; + $tagHTML->Render(); } - $tagHTML->Controls[] = $tagBODY; - $tagHTML->Render(); $this->OnRendered(new RenderedEventArgs(RenderMode::Any)); $renderedEventArgs = null; @@ -1176,14 +1237,6 @@ $renderedEventArgs = new RenderedEventArgs(RenderMode::Complete); } $this->OnRendered($renderedEventArgs); - - if ($this->ClassReference != null) - { - if (method_exists($this->ClassReference, "OnRendered")) - { - $this->ClassReference->OnRendered($renderedEventArgs); - } - } } private function MergeMasterPageControls($controls = null) diff --git a/app/lib/phast/WebScript.inc.php b/app/lib/phast/WebScript.inc.php index d57e110..e1ddf26 100644 --- a/app/lib/phast/WebScript.inc.php +++ b/app/lib/phast/WebScript.inc.php @@ -7,15 +7,15 @@ public $ContentType; public $Content; - public static function FromFile($FileName, $ContentType = null) + public static function FromFile($fileName, $contentType = null) { - $script = new WebScript($FileName, $ContentType); + $script = new WebScript($fileName, $contentType); return $script; } - public static function FromContent($Content, $ContentType = null) + public static function FromContent($content, $contentType = null) { $script = new WebScript(null, $contentType); - $script->Content = $Content; + $script->Content = $content; return $script; }