<?php
###############################################################################
# Layout.php
#
#
# @author Anil Kumar <akumar@codepunch.com>
# @link   https://codepunch.com
#
############################################################################### 

namespace 	CodePunch\UI;

use 		CodePunch\Base\Util as UTIL;
use			CodePunch\Base\Text as TEXT;
use 		CodePunch\Base\CPLogger;
use 		Exception;

###############################################################################

class Layout {
	
	public $authentication	= null;
	
	const	API_URL_PATH	= "api.php";
	
	###########################################################################
	
	public function __construct($auth=null) {
		$this->authentication = $auth;
	}
	
	###########################################################################
	
	public function show($pageinfo) 
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		
		if(UTIL::is_cli()) {
			if(isset($pageinfo['heading']))
				UTIL::print($pageinfo['heading']);
			if(isset($pageinfo['body']))
				UTIL::print($pageinfo['body']);
			echo("Unable to display more information in CLI\n");
			exit;
		}
		
		if(!is_array($pageinfo)) {
			if(strstr($pageinfo, "\\") === false) 
				$class = "\\CodePunch\\UI\\Modules\\" . ucfirst($pageinfo);
			else
				$class = $pageinfo;
			$pageinfo = array();
			if(class_exists($class)) {
				$pageinfo['class'] = $class;
			}
		}
		if(isset($pageinfo['class']) && class_exists($pageinfo['class'])) {
			$params = UTIL::get_from_array($pageinfo['params'], array());
			$c = new $pageinfo['class']($params);
			$pageinfo = $c->get($this->authentication, $params);
		}
		
		$layout = UTIL::get_from_array($pageinfo['template'], "dashboard");
		$style = UTIL::get_from_array($pageinfo['style'], "default");
		
		$homedata   = $this->getLayoutData($layout, $style, "home");
		$content    = $this->getLayoutData($layout, $style, "content");
		$headerdata = $this->getLayoutData($layout, $style, "header");
		$footerdata = $this->getLayoutData($layout, $style, "footer");
		$headtail   = $this->getLayoutData($layout, $style, "headtail");
		$bodytail   = $this->getLayoutData($layout, $style, "bodytail");
		
		$homedata = UTIL::get_from_array($pageinfo['home'], $homedata);
		$content = UTIL::get_from_array($pageinfo['content'], $content);
		$headerdata = UTIL::get_from_array($pageinfo['header'], $headerdata);
		$footerdata = UTIL::get_from_array($pageinfo['footer'], $footerdata);
		$headtail = UTIL::get_from_array($pageinfo['headtail'], $headtail);
		$bodytail = UTIL::get_from_array($pageinfo['bodytail'], $bodytail);
		
		$heading = UTIL::get_from_array($pageinfo['heading'], "");
		$subheading = UTIL::get_from_array($pageinfo['subheading'], "");
		$message = UTIL::get_from_array($pageinfo['body'], "");
		
		$extracss = UTIL::get_from_array($pageinfo['extracss'], "");
		$extrajs = UTIL::get_from_array($pageinfo['extrajs'], "");
		
		$allpanels   = $this->getAllPanels($layout, $style);

		$homedata = str_ireplace("{{CONTENT}}", $content, $homedata);
		$homedata = str_ireplace("{{HEADER}}", $headerdata, $homedata);
		$homedata = str_ireplace("{{FOOTER}}", $footerdata, $homedata);
		$homedata = str_ireplace("{{HEADTAIL}}", "\t".$headtail, $homedata);
		$homedata = str_ireplace("{{BODYTAIL}}", $bodytail, $homedata);
		$homedata = str_ireplace("{{PANELS}}", $allpanels, $homedata);
		
		$homedata = str_ireplace("{{HEADING}}", $heading, $homedata);
		$homedata = str_ireplace("{{SUBHEADING}}", $subheading, $homedata);
		$homedata = str_ireplace("{{MESSAGE}}", $message, $homedata);
		$homedata = str_ireplace("{{EXTRACSS}}", $extracss, $homedata);
		$homedata = str_ireplace("{{EXTRAJS}}", $extrajs, $homedata);
		
		$ignore_transparency = $setup->getBoolean("minimize_transparency_level", true);
		$homedata = $this->getCSSFilesInFolder($homedata, $layout, $style, $ignore_transparency);
		$homedata = $this->getJSFilesInFolder($homedata, $layout, $style);
		
		$homedata = $this->getIncudedFiles($homedata, $layout, $style);
		
		$homedata = $this->getDomainData($homedata);
		$homedata = $this->getPlatformData($homedata);
		$homedata = $this->getAboutAppData($homedata);
		$homedata = $this->getThemeFontsData($homedata);
		
		$approoturl = UTIL::get_root_url();
		$homedata = str_ireplace("{{APPROOTURL}}", $approoturl, $homedata);
		$homedata = str_ireplace("{{APIURLPATH}}", self::API_URL_PATH, $homedata);
		$homedata = $this->getThemeJSData($homedata);
		
		$version = \CodePunch\Config\Defaults::version() . "." . \CodePunch\Config\Defaults::BUILD_INDEX;
		$product = \CodePunch\Config\Defaults::product();
		$builddate = \CodePunch\Config\Defaults::builddate();
		$company = \CodePunch\Config\Defaults::companyname();
		$homedata = str_ireplace("{{VERSION}}", $version, $homedata);
		$homedata = str_ireplace("{{PRODUCT}}", $product, $homedata);
		$homedata = str_ireplace("{{BUILDDATE}}", $builddate, $homedata);
		$homedata = str_ireplace("{{COMPANYNAME}}", $company, $homedata);
		
		if(stristr($homedata, "{{CLIKEY}}") !== false) {
			if($this->authentication->validateSession(false, false) == $this->authentication::VALID) {
				$clikey = $this->authentication->getRemoteKey();
				$homedata = str_ireplace("{{CLIKEY}}", $clikey, $homedata);
			}
		}
		
		if(stristr($homedata, "{{SSOLOGINLINK}}") !== false) {
			$show_saml_logout = $setup->getOption("saml_allow_logout", true);
			$ssolink = "";
			$ssoname = "";
			$status = \CodePunch\Config\SAML\SAML::getStatus($this->authentication, $ssoname);
			if($status > 0) {
				$ssoname != "" ? $ssoname : "SSO/SAML";
				if($status == 2) {
					$ssolink = "<p class=\"small text-center mt-2 mb-0\"><a class=\"btn btn-login btn-block btn-dlg btn-dlg-sm\" href=\"" . UTIL::get_root_url() . "saml.php\" alt=\"$ssoname Access\">$ssoname Access" . "</a></p>";
					if($show_saml_logout)
						$ssolink .= "<p class=\"small text-center mt-0 mb-0\"><a class=\"mt-1 btn btn-login btn-block btn-dlg btn-dlg-sm\" href=\"" . UTIL::get_root_url() . "saml.php?slo\" alt=\"$ssoname Logout\">$ssoname Logout" . "</a></p>";
				}
				else 
					$ssolink = "<p class=\"small text-center mt-2 mb-0\"><a class=\"btn btn-login btn-block btn-dlg btn-dlg-sm\" href=\"" . UTIL::get_root_url() . "saml.php\" alt=\"$ssoname Login\">$ssoname Login" . "</a></p>";
			}
			$homedata = str_ireplace("{{SSOLOGINLINK}}", $ssolink, $homedata);
		}
		
		// This should be the last.
		$authstatus = $this->authentication ? "" : "none";
		$homedata = str_ireplace("{AUTHSTATUS}", $authstatus, $homedata);
		$lang = TEXT::getLanguage();
		$homedata = str_ireplace("{lang}", $lang, $homedata);
		$homedata = $this->getLanguageData($homedata);
		
		echo $homedata;
	}
	
	###########################################################################
	
	public function getIncudedFiles($pagedata, $layout, $style)
	{
		$pos = strpos($pagedata, "{{INC:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+6);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$ttext = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$ttext = $this->getLayoutData($layout, $style, $ttext);
				$pagedata = $tleft . $ttext . $tright;
				$pos = strpos($pagedata, "{{INC:");
			}
			else
				break;
		}
		return $pagedata;
	}
	
	###########################################################################
	// Add the required CSS Files. The minimized version will be used if found.
	// lib/layouts/css
	// lib/layouts/$style/$layout/css  folders will be searched.
	
	public function getCSSFilesInFolder($pagedata, $layout, $style, $ignore_transparency=true)
	{
		$pos = strpos($pagedata, "{{CSSFILES}}");
		if($pos !== false) {
			$cssinfo = "";
			$cssfolder = UTIL::get_install_folder_path() . "lib/layouts/css/src";
			$files = UTIL::find_all_matched_files($cssfolder, "/*.css");
			sort($files);
			$allfiles = array();
			foreach($files as $file) {
				if($ignore_transparency && !strcasecmp($file, "transparency.css"))
					continue;
				$cssfile = $cssfolder . "/" . $file;
				if(is_file($cssfile)) 
					$allfiles[] = $file;
			}
			$allfiles = implode("|", $allfiles);
			$cssinfo .= "\t<link href=\"" . self::API_URL_PATH . "?c=css&auth={AUTHSTATUS}&file=$allfiles&loc=&theme={{THEMENAME}}\" rel=\"stylesheet\">\n";
			$pagedata = str_replace("{{CSSFILES}}", rtrim($cssinfo), $pagedata);
		}
		
		$pos = strpos($pagedata, "{{THEMECSS}}");
		if($pos !== false) {
			$cssinfo = "";
			$cssfolder = UTIL::get_install_folder_path() . "lib/layouts/$style/$layout/css/src";
			$files = UTIL::find_all_matched_files($cssfolder, "/*.css");
			sort($files);
			$allfiles = array();
			foreach($files as $file) {
				if($ignore_transparency && !strcasecmp($file, "transparency.css"))
					continue;
				$cssfile = $cssfolder . "/" . $file;
				if(is_file($cssfile)) 
					$allfiles[] = $file;
			}
			$allfiles = implode("|", $allfiles);
			$cssinfo .= "\t<link href=\"" . self::API_URL_PATH . "?c=css&auth={AUTHSTATUS}&file=$allfiles&loc=$style/$layout&theme={{THEMENAME}}\" rel=\"stylesheet\">\n";
			$pagedata = str_replace("{{THEMECSS}}", rtrim($cssinfo), $pagedata);
		}
		return $pagedata;
	}
	
	###########################################################################
	// Add the required CSS Files. The minimized version will be used if found.
	// lib/layouts/js
	// lib/layouts/$style/$layout/js/src  folders will be searched.
	
	public function getJSFilesInFolder($pagedata, $layout, $style)
	{
		$pos = strpos($pagedata, "{{JSFILES}}");
		if($pos !== false) {
			$jsinfo = "";
			
			$jsfolder = UTIL::get_install_folder_path() . "lib/layouts/js/src";
			$files = UTIL::find_all_matched_files($jsfolder, "/*.js");
			$allfiles = array();
			foreach($files as $file) {
				$jsfile = $jsfolder . "/" . $file;
				if(is_file($jsfile))
					$allfiles[] = $file;
			}
			$allfiles = implode("|", $allfiles);
			$jsinfo .= "<script type=\"text/javascript\" src=\"" . self::API_URL_PATH . "?c=js&auth={AUTHSTATUS}&file=$allfiles&loc=&theme={{THEMENAME}}\"></script>\n";

			$jsfolder = UTIL::get_install_folder_path() . "lib/layouts/$style/$layout/js/src";
			$files = UTIL::find_all_matched_files($jsfolder, "/*.js");
			$allfiles = array();
			foreach($files as $file) {
				$jsfile = $jsfolder . "/" . $file;
				if(is_file($jsfile))
					$allfiles[] = $file;
			}
			$allfiles = implode("|", $allfiles);
			$jsinfo .= "<script type=\"text/javascript\" src=\"" . self::API_URL_PATH . "?c=js&auth={AUTHSTATUS}&file=$allfiles&loc=$style/$layout&theme={{THEMENAME}}\"></script>\n";
			$pagedata = str_replace("{{JSFILES}}", rtrim($jsinfo), $pagedata);
		}
		return $pagedata;
	}
	
	###########################################################################
	
	public function getLanguageData($pagedata)
	{
		$pos = strpos($pagedata, "{{LANG:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+7);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$ttext = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$ttext = TEXT::get($ttext);
				$pagedata = $tleft . $ttext . $tright;
				$pos = strpos($pagedata, "{{LANG:");
			}
			else
				break;
		}
		return $pagedata;
	}
	
	###########################################################################
	
	public function getRegistrarJSAliases($pagedata)
	{
		$aliases = array();
		if($this->authentication) {
			$db = $this->authentication->getDatabase();
			if($db) {
				$table = $db->getRegistrarAliasTableName();
				$rows = $db->getFromTable("name,alias", $table);
				if($rows !== false) {
					$index = 0;
					foreach($rows as $row) {
						$row = $db->fetchRow($rows, $index++);
						$aliases[$row['name']] = $row['alias'];
					}
				}
			}
		}
		return str_ireplace("{{REGALIASES}}", "var registrar_aliases = " . json_encode($aliases) . ";\n", $pagedata);
	}
	
	###########################################################################
	
	public function getThemeJSData($pagedata)
	{
		$themename = $this->getThemeName();
		$pagedata = str_ireplace("{{THEMENAME}}", $themename, $pagedata);
		
		// Output the current theme data in json format into a JS variable (if {{THEME}} token is found)
		$tdata = "";
		$thistheme = self::getTheme($themename);
		if($thistheme !== false) 
			$tdata = json_encode($thistheme);
		$tdata = "var theme = $tdata;\n";
		$pagedata = str_ireplace("{{THEME}}", $tdata, $pagedata);

		// Output individual theme colors as JS variables.
		$pos = strpos($pagedata, "{{THEME:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+8);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$ttext = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$kval = $this->getThemeEntry($ttext, $thistheme);
				if(is_array($kval))
					$kval = "";
				$pagedata = $tleft . $kval . $tright;
				$pos = strpos($pagedata, "{{THEME:");
			}
			else
				break;
		}
		return $pagedata;
	}
	
	###########################################################################
	
	public function getThemeFontsData($pagedata)
	{
		$themename = $this->getThemeName();
		$thistheme = self::getTheme($themename);
		$fonts = $this->getThemeFonts($thistheme);
		$style = "";
		$gfonts = array();
		$fontpath = UTIL::get_install_folder_path() . "lib/layouts/fonts/";
		foreach($fonts as $f) {
			$font = str_replace(" ", "-", $f);
			$fontfile = $fontpath . $font . "/fonts.css";
			
			if(is_file($fontfile))
				$style .= "\t<link href=\"lib/layouts/fonts/$font/fonts.css\" rel=\"stylesheet\">\n";
			else 
				$gfonts[] = $f;
		}
		if(count($gfonts)) {
			$style .= "\t<style>";
			$style .=  "\n\t@import url('https://fonts.googleapis.com/css?family=" . implode("|", $gfonts) . "')\n";
			$style .= "\t</style>";
		}
		$pagedata = str_ireplace("{{FONTS}}", rtrim($style), $pagedata);
		
		return $pagedata;
	}
	
	###########################################################################
	
	public static function getThemeFonts($thetheme)
	{
		// Get Fonts
		$default_fonts = array('Droid Sans', 'Coda', 'Dosis');
		$fonts = array();
		$fcount = 1;
		while(1) {
			$key = "gf".$fcount++;
			$font = UTIL::get_from_array($thetheme['font'][$key], "");
			if($font != "")
				$fonts[] = $font;
			else
				break;
		}
		if(!count($fonts))
			$fonts[] = $default_fonts[0];
		
		$gfonts = "";
		if(isset($_COOKIE['gfonts']))
			$gfonts = filter_var($_COOKIE['gfonts'], FILTER_SANITIZE_STRING);
		$gfonts = explode(",", $gfonts);
		for($i = 0; $i < count($fonts); $i++) {
			$font = UTIL::get_from_array($gfonts[$i], $fonts[$i]);
			if($font != "")
				$fonts[$i] = $font;
		}
		
		return $fonts;
	}
	
	###########################################################################
	
	public function getPlatformData($pagedata)
	{
		$driver = "";
		$dbhost = "-";
		if($this->authentication != null && $this->authentication->getDatabase()) {
			$platform = $this->authentication->getDatabase()->connection->getDatabasePlatform();
			$driver = $platform->getName();
			$params = $this->authentication->getDBALConnectionParams();
			$dbhost = UTIL::get_from_array($params['host'], "-");
		}
		if($dbhost == "")
			$dbhost = "-";
		$pagedata = str_ireplace("{{DBDRIVER}}", $driver, $pagedata);
		$pagedata = str_ireplace("{{DBHOST}}", $dbhost, $pagedata);
		
		return $pagedata;
	}
	
	###########################################################################
	
	public function getUserInfo()
	{
		$userinfo = array('uid'=>\CodePunch\Config\ConfigRoot::INVALID_USERID, 'user'=>'', 'rights'=>0, 'groups'=>'');
		if($this->authentication != null && $this->authentication->getDatabase()) {
			$auth = $this->authentication;
			$db = $auth->getDatabase();
			$cids = $db->getCategoryIDsForCurrentUser($auth);
			$cinfo = array();
			foreach($cids as $cid) {
				$name = $db->findOneOf($db->getCategoryTableName(), "cid", $cid, "name");
				$cinfo[] = array('cid'=>$cid, 'name'=>$name);
			}
			$userinfo['rights'] = $auth->getUserAccess();
			$userinfo['uid'] = $auth->getUserID();
			$userinfo['groups'] = $cinfo;
			$userinfo['user'] = $auth->getUserName();
		}
		return $userinfo;
	}
	
	###########################################################################
	
	public function getUserJSData($pagedata)
	{
		$userinfo = array('uid'=>\CodePunch\Config\ConfigRoot::INVALID_USERID, 'user'=>'', 'rights'=>0, 'groups'=>'');
		if($this->authentication != null && $this->authentication->getDatabase()) {
			$auth = $this->authentication;
			$db = $auth->getDatabase();
			$cids = $db->getCategoryIDsForCurrentUser($auth);
			$cinfo = array();
			foreach($cids as $cid) {
				$name = $db->findOneOf($db->getCategoryTableName(), "cid", $cid, "name");
				$cinfo[] = array('cid'=>$cid, 'name'=>$name);
			}
			$userinfo['rights'] = $auth->getUserAccess();
			$userinfo['uid'] = $auth->getUserID();
			$userinfo['groups'] = $cinfo;
			$userinfo['user'] = $auth->getUserName();
		}
		$pagedata = str_ireplace("{{USERINFOJS}}", "var user_details = " . json_encode($userinfo) . ";\n", $pagedata);
		return $pagedata;
	}
		
	###########################################################################
	
	public function getAboutAppData($pagedata)
	{
		$userinfo = $this->getUserInfo();
		$rights = array("0"=>"None", "1"=>"View", "2"=>"Edit", "4"=>"Add", "8"=>"delete", "16"=>"Lookup", "32"=>"UI Change", "64"=>"Download", "128"=>"Category Edit", "256"=>"All Domains");
		$uinfo = "<table class=\"luqtable\" width=\"100%\" style=\"margin-bottom: 10px;\">";
		foreach($userinfo as $key=>$value) {
			if($key == 'uid')
				continue;
			if($key == 'rights') {
				if($this->authentication && $this->authentication->isSetupAdmin())
					$value = "<code>Setup Administrator</code>";
				else if(is_numeric($value)) {
					$values = array();
					foreach($rights as $r=>$v) {
						if(intval($r) & $value)
							$values[] = $v;
					}
					$value = "<code>" . implode(", ", $values) . "</code>";
				} 
			}
			$key = ucfirst($key);
			if(is_array($value)) {
				$groups = array();
				foreach($value as $v)
					$groups[] = $v['name'];
				$value = implode(", ", $groups);
				$uinfo .= "<tr><td colspan=\"2\"><b><span class=\"small\">$value</span></b></td></tr>\n";
			}
			else
				$uinfo .= "<tr><td>$key</td><td><b>$value</b></td></tr>\n";
			if(!strcasecmp($key, "user")) {
				if(isset($_SESSION[\CodePunch\Config\ConfigRoot::SAMLUSER])) {
					$samldata = "";
					if(isset($_SESSION[\CodePunch\Config\ConfigRoot::SAMLIDP]))
						$samldata = $_SESSION[\CodePunch\Config\ConfigRoot::SAMLIDP];
					if(strcasecmp($value, $_SESSION[\CodePunch\Config\ConfigRoot::SAMLUSER]))
						$samldata .= ": " . $_SESSION[\CodePunch\Config\ConfigRoot::SAMLUSER];
					$samldata = trim($samldata, ": ");
					if($samldata != "")
						$uinfo .= "<tr><td>IDP</td><td>$samldata</td></tr>\n";
				}
			}
		}
		$uinfo .= "</table>\n";
		$pagedata = str_ireplace("{{USERINFO}}", $uinfo, $pagedata);
		
		$linfo = "<table class=\"luqtable\" width=\"100%\" style=\"margin-bottom: 10px;\">";
		if($this->authentication) {
			$li = $this->authentication->getLicense();
			foreach($li as $key=>$value) {
				$linfo .= "<tr><td style=\"vertical-align:top\">$key</td><td style=\"vertical-align:top\"><b>$value</b></td></tr>\n";
			}
			$setup = new \CodePunch\Config\Settings($this->authentication);
			if($setup->getBoolean("alert_on_app_update", false)) {
				$avail = $setup->getOption("app_update_available", "0");
				$new = ($avail == "1") ? "<code>New!</code>" : "";
				$builddate = $setup->getOption("available_app_build_date", "");
				if($builddate != "")
					$linfo .= "<tr><td style=\"vertical-align:top\">Latest Version</td><td style=\"vertical-align:top\"><b>$builddate</b> $new</td></tr>\n";
			}

			// This done whenever the about pane data is requested.
			// (need to do this in non-cli mode at least once but not every time any app module is opened.)
			if(!UTIL::is_cli()) // Save application Root URL to settings
				$setup->setOption("application_root_url", UTIL::get_root_url());
		}
		$linfo .= "</table>\n";
		$pagedata = str_ireplace("{{LINFO}}", $linfo, $pagedata);
		
		return $pagedata;
	}
	
	###########################################################################
	
	public function getDomainData($pagedata) 
	{
		if($this->authentication) 
			$db = $this->authentication->getDatabase();
		else 
			$db = null;
		$pos = strpos($pagedata, "{{DBDTCOUNT:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+12);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$query = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$dc = "";
				if($db) {
					$table = $db->getDomainTableName();
					$pos = stripos($query, ":");
					if($pos !== false) {
						$tn = strtolower(substr($query, 0, $pos));
						if($tn == "sd")
							$table = $db->getSubdomainTableName();
						else if($tn == "d")
							$table = $db->getDomainTableName();
						$query = substr($query, $pos+1);
					}
					// Make sure only the domains that are accessible to the user is counted.
					$catquery = $db->getCategoryQueryForCurrentUser(0, $this->authentication);
					if($query != "" && $catquery != "") 
						$query = "($query) AND ($catquery)";
					else if($query == "" && $catquery != "")
						$query = $catquery;
					
					$dc = $db->GetRowCount("$table d", $query);
					if($dc === false)
						$dc = "";
				}
				$pagedata = $tleft . $dc . $tright;
				$pos = strpos($pagedata, "{{DBDTCOUNT:");
			}
			else
				break;
		}
		
		if(strpos($pagedata, "{{REGOPTIONLIST}}") !== false) {
			$reg = "";
			$folder = UTIL::get_install_folder_path();
			$files = UTIL::find_all_matched_files($folder . "lib/php/CodePunch/LU/Registrars/", "*.php");
			foreach($files as $r) {
				$v = str_replace(".php", "", $r);
				if($v != "RegistrarAPI")
					$reg .= "<option value=\"$v\">$v</option>\n";
			}
			$pagedata = str_ireplace("{{REGOPTIONLIST}}", $reg, $pagedata);
		}
		
		if(strpos($pagedata, "{{2FAOPTIONLIST}}") !== false) {
			$reg = "";
			$folder = UTIL::get_install_folder_path();
			$files = UTIL::find_all_matched_files($folder . "lib/php/CodePunch/Config/Security/", "*.php");
			foreach($files as $r) {
				$v = str_replace(".php", "", $r);
				if($v != "Base")
					$reg .= "<option value=\"$v\">$v</option>\n";
			}
			$pagedata = str_ireplace("{{2FAOPTIONLIST}}", $reg, $pagedata);
		}
		
		if(strpos($pagedata, "{{SAMLOPTIONLIST}}") !== false) {
			$reg = "";
			$folder = UTIL::get_install_folder_path();
			$files = UTIL::find_all_matched_files($folder . "lib/php/CodePunch/Config/SAML/", "*.php");
			foreach($files as $r) {
				$v = str_replace(".php", "", $r);
				if($v != "SAML" && $v != "SAMLBase" && $v != "DummyIDP")
					$reg .= "<option value=\"$v\">$v</option>\n";
			}
			$pagedata = str_ireplace("{{SAMLOPTIONLIST}}", $reg, $pagedata);
		}
		
		return $pagedata;
	}
	
	###########################################################################
	
	public function getDomainColumnJSModel($pagedata)
	{
		if($this->authentication) {
			$setup = new \CodePunch\Config\Settings($this->authentication);
		}
		$pos = strpos($pagedata, "{{DCOLMODEL:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+12);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$cmname = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$colmodel = "''";
				if($this->authentication) {
					if($this->authentication->validateSession(false, false) == $this->authentication::VALID) {
						$colmodel = json_encode($setup->getDomainColumnModelByName($cmname));
						if($colmodel === false)
							$colmodel = "''";
					}
				}
				$pagedata = $tleft . $colmodel . $tright;
				$pos = strpos($pagedata, "{{DCOLMODEL:");
			}
			else
				break;
		}
		
		if(stristr($pagedata, "{{RCOLMODEL_NAMES}}") !== false) {
			$rcolmodelnames = "";
			if($this->authentication && $this->authentication->getDatabase()) {
				$db = $this->authentication->getDatabase();
				$rows = $db->getFromTable("name", $db->getReportSchedulerTableName(), "", array(), "sortindex");
				if($rows !== false) {
					$index = 0;
					foreach($rows as $row) {
						$row = $db->fetchRow($rows, $index++);
						$rcolmodelnames .= "'" . $row['name'] . "',";
					}
					$rcolmodelnames = trim($rcolmodelnames, ",");
				}
			}
			$pagedata = str_replace("{{RCOLMODEL_NAMES}}", $rcolmodelnames, $pagedata);
		}
		
		return $pagedata;
	}
	
	###########################################################################
	
	public function getSubdomainColumnJSModel($pagedata)
	{
		if($this->authentication) {
			$setup = new \CodePunch\Config\Settings($this->authentication);
		}
		$pos = strpos($pagedata, "{{SDCOLMODEL:");
		while($pos !== false) {
			$tleft = substr($pagedata, 0, $pos);
			$tright = substr($pagedata, $pos+13);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$cmname = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				$colmodel = "";
				if($this->authentication) {
					if($this->authentication->validateSession(false, false) == $this->authentication::VALID) {
						$colmodel = json_encode($setup->getSubdomainColumnModelByName($cmname));
						if($colmodel === false)
							$colmodel = "";
					}
				}
				//$colmodel = str_ireplace("s.", "", $colmodel);
				$pagedata = $tleft . $colmodel . $tright;
				$pos = strpos($pagedata, "{{SDCOLMODEL:");
			}
			else
				break;
		}
		return $pagedata;
	}
	
	###########################################################################
	# Get the current theme name from session.
	# If theme is set using a REQUEST variable, save it to session.
	
	public static function getThemeName() 
	{
		$theme = strtolower(UTIL::get_sanitized_request_string("theme", ""));
		if($theme != "") 
			setCookie(\CodePunch\Config\ConfigRoot::COLORSCHEME, $theme, time()+24*30*3600);
		if($theme == "" && isset($_COOKIE[\CodePunch\Config\ConfigRoot::COLORSCHEME]))
			$theme = $_COOKIE[\CodePunch\Config\ConfigRoot::COLORSCHEME];
		$themes = UTIL::array_flatten(self::getTheme(""));
		if(!UTIL::in_array_casei($theme, $themes) && strpos($theme, "_") === false)
			$theme = "";
		if($theme == "" || $theme === false) {
			$theme = "Gray";
			setCookie(\CodePunch\Config\ConfigRoot::COLORSCHEME, $theme, time()+24*30*3600);
		}
		return $theme;
	}
	
	###########################################################################
	// Return the theme data corresponding to $themename.
	// If $themename is null/empty/false, will return all valid theme names in
	// an array.
	
	public static function getTheme($themename)
	{
		//TODO: Dynamic Color Schemes from 1-3 Pivot colors
		if(UTIL::starts_with($themename, "_") !== false) {
			$parts = array_values(array_filter(explode("_", $themename)));
			if(count($parts) == 1) {
				$parts[] = UTIL::invert_color("#".$parts[0]);
				$parts[] = UTIL::adjust_color_brightness("#".$parts[0], 20);
			}
			if(count($parts) == 2) 
				$parts[] = UTIL::invert_color("#"+$parts[0]);
			if(count($parts) == 3)
				return self::getDynamicThemeFile("", $themename, "#".$parts[0], "#".$parts[1], "#".$parts[2]);
		}
		
		$allthemenames = array();
		$folder = UTIL::get_install_folder_path();
		$themefolder = $folder . "lib/layouts/themes/";
		$files = UTIL::find_all_matched_files($themefolder, "*.json");
		$folders = UTIL::find_all_matched_folders($themefolder);
		foreach($folders as $folder) {
			$themefile = $themefolder . $folder . DIRECTORY_SEPARATOR . "theme.json";
			if(is_file($themefile))
				$files[] = $folder . DIRECTORY_SEPARATOR . "theme.json";
		}
		$defaultTheme = null;
		foreach($files as $file) {
			$themefile = $themefolder . $file;
			if(is_file($themefile)) {
				$str = file_get_contents($themefile);
				$str = str_replace("{{ROOTPATH}}", UTIL::get_install_url_path(), $str);
				$str = str_replace("{{THEMEPATH}}", UTIL::get_install_url_path() . "lib/layouts/themes/", $str);
				$themedata = json_decode($str, true);
				if(is_array($themedata)) {
					if($defaultTheme == null)
						$defaultTheme = reset($themedata);
					if($themename == "" || $themename === false || $themename == null) {
						$allthemenames[] = array_keys($themedata);
					}
					else {
						$themename = strtolower($themename);
						$themedata = array_change_key_case($themedata,CASE_LOWER);
						if(isset($themedata[$themename])) 
							return $themedata[$themename];
					}
				}
			}
		}
		return count($allthemenames) ? $allthemenames : $defaultTheme;
	}
	
	###########################################################################
	
	public function getJSFile($filename, $templatefile=true) {
		$jsdata = "";
		if(is_file($filename)) {
			$jsdata = file_get_contents($filename);
			if($templatefile) {
				$approoturl = UTIL::get_root_url();
				$jsdata = str_ireplace("{{APPROOTURL}}", $approoturl, $jsdata);
				$jsdata = str_ireplace("{{APIURLPATH}}", self::API_URL_PATH, $jsdata);
				$jsdata = $this->getRegistrarJSAliases($jsdata);

				$jsdata = $this->getThemeJSData($jsdata);
				$jsdata = $this->getDomainColumnJSModel($jsdata);
				$jsdata = $this->getSubdomainColumnJSModel($jsdata);
				$jsdata = $this->getUserJSData($jsdata);
				$jsdata = $this->getDomainData($jsdata);

				// Hack for fixing callback function names in JS code
				// created from json_encode.
				// Remove "jsf_  and _jsf" so that quotes are gone.
				$jsdata = str_replace("\"jsf_", "", $jsdata);
				$jsdata = str_replace("_jsf\"", "", $jsdata);
				//
				$setup = new \CodePunch\Config\Settings($this->authentication);
				$dateformat = $this->authentication ? $setup->getOption("date_display_format", "Y-m-d") : "Y-m-d";
				$jsdateformat = str_replace("M", "MMM", $dateformat);
				$jsdateformat = str_replace("Y", "yyyy", $jsdateformat);
				$jsdateformat = str_replace("d", "dd", $jsdateformat);
				$jsdateformat = str_replace("m", "MM", $jsdateformat);
				$jsdata = str_ireplace("{{JSDATEFORMAT}}", $jsdateformat, $jsdata);
				$jsdata = str_ireplace("{{DATEFORMAT}}", $dateformat, $jsdata);
				$jsdata = str_ireplace("{{EGDATEFORMAT}}", date($dateformat), $jsdata);
				$gridsearchdelay = $this->authentication ? $setup->getOption("grid_auto_search_delay", "800") : "800";
				$gridsearchdelay = $gridsearchdelay < 300 ? 300 : ($gridsearchdelay > 1500 ? 1500 : $gridsearchdelay);
				$gridsearchenter = $this->authentication ? $setup->getBoolean("grid_search_on_enter", false) : false;
				$gridenableinledit = $this->authentication ? $setup->getBoolean("grid_enable_inline_edit", true) : false;
				$jsdata = str_ireplace("{{GRIDSEARCHDELAY}}", $gridsearchdelay, $jsdata);
				$jsdata = str_ireplace("{{GRIDSEARCHENTER}}", $gridsearchenter ? 'true' : 'false', $jsdata);
				$jsdata = str_ireplace("{{GRIDENABLEINLEDIT}}", $gridenableinledit ? 'true' : 'false', $jsdata);
			}
		}
		return "$jsdata\n";
	}
	
	###########################################################################
	
	public function getCSSData($styledata)
	{
		$themename = self::getThemeName();
		$thistheme = self::getTheme($themename);
		
		$pos = strpos($styledata, "{{");
		while($pos !== false) {
			$tleft = substr($styledata, 0, $pos);
			$tright = substr($styledata, $pos+2);
			$pend = strpos($tright, "}}");
			if($pend !== false) {
				$ttext = substr($tright, 0, $pend);
				$tright = substr($tright, $pend+2);
				if($ttext == "ROOTPATH")
					$kval = UTIL::get_install_url_path();
				else {
					if(UTIL::starts_with($ttext, "FONT.GF")) {
						$index = intval(str_replace("FONT.GF", "", $ttext)) - 1;
						$fonts = self::getThemeFonts($thistheme);
						if($index >= 0 && $index < count($fonts)) 
							$kval = $fonts[$index];
						else
							$kval = UTIL::get_from_array($fonts[0], "Droid Sans");
					}
					else {
						$kval = $this->getThemeEntry($ttext, $thistheme);
						if(is_array($kval))
							$kval = "";
					}				
				}
				$styledata = $tleft . $kval . $tright;
				$pos = strpos($styledata, "{{");
			}
			else
				break;
		}

		return "/* $themename */\n\n $styledata\n";
	}
	
	###########################################################################
	
	public function getCSSFile($filename) {
		$styledata = "";
		if(is_file($filename)) 
			$styledata = $this->getCSSData(file_get_contents($filename)); 
		return $styledata;
	}
	
	###########################################################################
	# Locate image and return the correct path.
	
	public static function findImage($imagefile)
	{
		// if remote image return exactly as it is.
		if(UTIL::starts_with(strtolower($imagefile), "http")) 
			return $imagefile;
		else {
			$folder = UTIL::get_install_folder_path();
			$path = $folder . $imagefile;
			if(is_file($path))
				return UTIL::get_install_url_path() . $imagefile;
			else {
				// Look in various folders
				$lpos = strrchr($imagefile, "/");
				$onlyfile = ($lpos !== false) ? substr($lpos, 1) : $imagefile;
				$fdlist = array('stock', 'custom', 'logos', 'bg', 'icons', 'menu');
				foreach($fdlist as $fd) {
					if(is_file($folder . "lib/layouts/images/$fd/" . $onlyfile))
						return UTIL::get_install_url_path() . "lib/layouts/images/$fd/" . $onlyfile;
				}
			}
		}
		return false;
	}
	
	###########################################################################
	
	private function getThemeEntry($ttext, $kval) 
	{
		$ttext = strtolower($ttext);
		if($ttext == "header.logo" || $ttext == "header.logobig" || substr($ttext, 0, 6) == "image.") {
			if($this->authentication && $this->authentication->getDatabase()) {
				$setup = new \CodePunch\Config\Settings($this->authentication);
				if($ttext == "header.logo") {
					$logoimage = $setup->getOption("main_logo_image", false);
					$logoimage = $this->findImage($logoimage);
					if($logoimage !== false && $logoimage != "")
						return $logoimage;
				}
				else if($ttext == "header.logobig") {
					$logoimage = $setup->getOption("big_logo_image", false);
					$logoimage = $this->findImage($logoimage);
					if($logoimage !== false && $logoimage != "")
						return $logoimage;
				}
				else if($ttext == "image.source") {
					$bgimage = $setup->getOption("background_image", false);
					$bgimage = $this->findImage($bgimage);
					if($bgimage !== false && $bgimage != "")
						return $bgimage;
				}
				else if($ttext == "image.prop") {
					$optionvalue = $setup->getOption("background_property", false);
					if($optionvalue !== false && $optionvalue != "")
						return $optionvalue;
				}
				else if($ttext == "image.size") {
					$optionvalue = $setup->getOption("background_size", false);
					if($optionvalue !== false && $optionvalue != "")
						return $optionvalue;
				}
			}
		}

		$parts = explode(".", $ttext);
		foreach($parts as $key) {
			if(!is_array($kval) && (UTIL::starts_with($key, "contrast") || UTIL::starts_with($key, "alpha"))) {
				//{{PANEL.BG.CONTRAST_XXX_ALPHA_YYY}};
				// XXX = 0 to 255, YYY = 0 to 255
				$contrast = false;
				$alpha = false;
				if(UTIL::starts_with($key, "contrast")) {
					$key = str_replace("contrast_", "", $key);
					$pos = strpos($key, "_");
					if($pos !== false) {
						$ck = substr($key, 0, $pos);
						$key = substr($key, $pos+1);
					}
					else 
						$ck = $key;
					if($ck != "" && intval($ck) >= 0 && intval($ck) <= 255) 
						$contrast = ((intval($ck))*2) - 255;
				}
				$key = str_replace("alpha_", "", $key);
				if($key != "" && intval($key) >= 0 && intval($key) <=255) 
					$alpha = round(intval($key)/255, 2);
				if($contrast !== false) {
					$hex = UTIL::convert_rgba_to_hex($kval);
					$kval = UTIL::adjust_color_brightness($hex, $contrast);
				}
				if($alpha != false)
					$kval = UTIL::change_alpha_in_color_code($kval, $alpha);
				break;
			}
			$kval = UTIL::get_from_array($kval[$key], false);
			if($kval === false) {
				if($ttext == "image")
					$kval = "lib/layouts/images/stock/smoke-wbg.jpg";
				else
					$kval = "";
				break;
			}
		}
		return $kval;
	}
	
	###########################################################################
	
	public static function getDynamicThemeFile($filename, $themename, $primary, $secondary="#FFFFFF", $tertiary="#FFFFFF") 
	{
		$content = "";
		if($filename == "") {
			$folder = UTIL::get_install_folder_path();
			$filename = $folder . "lib/layouts/themes/theme.template";
		}
		if(is_file($filename)) {
			$content = file_get_contents($filename);
			$content = str_replace("{{THEMENAME}}", $themename, $content);
			$colorcodes = array($primary, $secondary, $tertiary, 
						UTIL::invert_color($primary), UTIL::invert_color($secondary), UTIL::invert_color($tertiary)
						);
			$tags = array("{{PC(", "{{SC(", "{{TC(", "{{PTC(", "{{STC(", "{{TTC(");
			for($i = 0; $i < 6; $i++) {
				while(1) {
					$pos = stripos($content, $tags[$i]);
					if($pos === false)
						break;
					$left = substr($content, 0, $pos);
					$right = substr($content, $pos+strlen($tags[$i]));
					$p2 = stripos($right, ")}}");
					if($p2 === false)
						break;
					$mid = substr($right, 0, $p2);
					$right = substr($right, $p2+3);
					$rgba = UTIL::adjust_color_brightness($colorcodes[$i], $mid);
					$content = $left . $rgba . $right;
				}
			}
			$content = json_decode($content, true);
			$content = array_change_key_case($content,CASE_LOWER);
			$content = reset($content);
		}
		return $content;
	}
	
	###########################################################################
	
	private function getLayoutData($layout, $style, $dataname)
	{
		$thedata = "";
		$folder = UTIL::get_install_folder_path();
		$datafile = $folder . "lib/layouts/$style/$dataname.htm";
		if(!is_file($datafile))
			$datafile = $folder . "lib/layouts/$style/$layout/$dataname.htm";
		if(!is_file($datafile)) 
			$datafile = $folder . "lib/layouts/$style/common/$dataname.htm";
		if(is_file($datafile))
			$thedata = file_get_contents($datafile);
		return $thedata;
	}		
	
	###########################################################################
	
	private function getAllPanels($layout, $style)
	{
		$folder = UTIL::get_install_folder_path();
		$panelsfolder = $folder . "lib/layouts/$style/$layout/panels/";
		$files = UTIL::find_all_matched_files($panelsfolder, "*.htm");
		$thedata = "";
		if($files !== false) {
			foreach($files as $file) {
				$thefile = $panelsfolder . $file;
				if(is_file($thefile)) {
					$thedata .= file_get_contents($thefile);
				}
			}
		}
		return $thedata;
	}		
	
	###########################################################################
	
	public static function showPage($page, $dorepair=false) 
	{
		// Not having PHP v7.1+ is critical problem
		try {
			if(version_compare(PHP_VERSION, '7.1.0') < 0 || version_compare(PHP_VERSION, '8.0.0') >= 0)
				throw new Exception(TEXT::get("wrong_php_version"));
			$ioncubestatus = \CodePunch\Config\ConfigRoot::check_ioncube_status();
			if($ioncubestatus < 0)
				throw new Exception(TEXT::get("no_ioncube_loader"));
			if (!extension_loaded('mbstring'))
				throw new Exception(TEXT::get("Missing php multibyte string support (mbstring)"));
			//if (!extension_loaded('intl'))
			//	throw new Exception(TEXT::get("Missing php internationalization support (intl)"));
			$dbaction = $dorepair ? \CodePunch\DB\Database::REPAIR_TABLES : \CodePunch\DB\Database::READONLY_TABLES;
			$auth = new \CodePunch\Config\Auth($dbaction);
			if(UTIL::is_cli() && $page == "verify") 
				\CodePunch\UI\Modules\Verify::show($auth);
			else
				$auth->validateSession($page, true);
			exit;
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			if(UTIL::is_cli() && $page == "verify" && version_compare(PHP_VERSION, '7.1.0') >= 0) 
				\CodePunch\UI\Modules\Verify::show(null, $e->getMessage());
			else {
				$layout = new \CodePunch\UI\Layout(null, $e->getMessage());
				if($page == "verify") {
					if(version_compare(PHP_VERSION, '7.1.0') >= 0) {
						$pageinfo['class'] = "\\CodePunch\\UI\\Modules\\Verify";
						$pageinfo['params'] = $e->getMessage();
					}
				}
				if(!isset($pageinfo['class'])){
					$pageinfo['heading'] = "<h1>" . TEXT::get("db_therewasaproblem") . "</h1>";
					$pageinfo['body'] = "<p class=\"problem\">" . $e->getMessage() . "</p>";
				}
				$layout->show($pageinfo);
			}
		}
	}
	
}

###############################################################################
