various fixes, improvements, and feature additions

This commit is contained in:
Michael Becker 2023-11-11 20:32:41 -05:00
parent 688663b85b
commit 4892cb9f26
3 changed files with 354 additions and 85 deletions

View File

@ -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,14 +396,18 @@
* @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);
@ -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);
@ -565,6 +621,18 @@
// 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)
{
@ -680,6 +751,144 @@
}
}
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,9 +975,10 @@
{
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);
@ -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();

View File

@ -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();
@ -65,6 +71,12 @@
public $Title;
public $ClassList;
/**
* Indicates whether the request is a postback.
* @var bool
*/
public $IsPostback;
/**
* The class reference for this WebPage.
* @var PhastPage
@ -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");
trigger_error("Control with ID '" . $id . "' not found");
}
else
{
trigger_error("control collection null for " . __CLASS__);
}
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)
@ -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);
}
}
}
@ -827,6 +870,8 @@
return;
}
$handled = false;
switch (System::$WebPageFormat)
{
case WebPageFormat::JavaScript:
@ -930,12 +975,6 @@
header("Content-Type: text/html; charset=utf-8");
if (!$this->IsPartial)
{
if (!$this->UseCompatibleRenderingMode)
{
echo("<!DOCTYPE html>\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("<!DOCTYPE html>\r\n");
}
}
else
{
$re = new RenderingEventArgs(RenderMode::Partial);
$this->OnRendering($re);
if ($re->Cancel) return;
}
$re = new RenderingEventArgs(RenderMode::Any);
$this->OnRendering($re);
if ($re->Cancel) return;
if (is_callable($this->Content) && count($this->Controls) == 0)
{
$tagBODY->Content = $this->Content;
$handled = $re->Handled;
}
else if (!is_callable($this->Content) && count($this->Controls) > 0)
{
$controls = $this->Controls;
if ($this->MasterPage != null)
{
$controls = $this->MergeMasterPageControls($this->MasterPage->Controls);
}
foreach ($controls as $ctl)
if (!$handled)
{
/*
$re = new RenderingEventArgs(RenderMode::Any);
$this->OnRendering($re);
if ($re->Cancel) return;
*/
if (is_callable($this->Content) && count($this->Controls) == 0)
{
$tagBODY->Controls[] = $ctl;
$tagBODY->Content = $this->Content;
}
else if (!is_callable($this->Content) && count($this->Controls) > 0)
{
$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)

View File

@ -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;
}