<?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");
		
		$homedata   = $this->getLayoutData($layout, "home");
		$content    = $this->getLayoutData($layout, "content");
		$headerdata = $this->getLayoutData($layout, "header");
		$footerdata = $this->getLayoutData($layout, "footer");
		$headtail   = $this->getLayoutData($layout, "headtail");
		$bodytail   = $this->getLayoutData($layout, "bodytail");
		$commonhead = $this->getLayoutData($layout, "commonhead");
		
		$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);
		$commonhead = UTIL::get_from_array($pageinfo['commonhead'], $commonhead);
		
		$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);

		$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("{{COMMONHEAD}}", $commonhead, $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);
		
		$homedata = $this->getCSSFilesInFolder($homedata, $layout);
		$homedata = $this->getJSFilesInFolder($homedata, $layout);
		
		$homedata = $this->getIncudedFiles($homedata, $layout);
		
		$homedata = $this->getDomainData($homedata);
		$homedata = $this->getPlatformData($homedata);
		$homedata = $this->getAboutAppData($homedata);
		
		$approoturl = UTIL::get_root_url();
		$homedata = str_ireplace("{{APPROOTURL}}", $approoturl, $homedata);
		$homedata = str_ireplace("{{APIURLPATH}}", self::API_URL_PATH, $homedata);
		$homedata = $this->getImagesData($homedata);
		
		$majorversion = \CodePunch\Config\Defaults::majorversion();
		$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();
		$servertime = date("d M Y, H:i:s");
		$homedata = str_ireplace("{{VERSION}}", $version, $homedata);
		$homedata = str_ireplace("{{MAJORVERSION}}", $majorversion, $homedata);
		$homedata = str_ireplace("{{PRODUCT}}", $product, $homedata);
		$homedata = str_ireplace("{{BUILDDATE}}", $builddate, $homedata);
		$homedata = str_ireplace("{{COMPANYNAME}}", $company, $homedata);
		$homedata = str_ireplace("{{SERVERTIME}}", $servertime, $homedata);
		
		if(stristr($homedata, "{{CLIKEY}}") !== false && $this->authentication) {
			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 tabindex=\"0\" class=\"btn btn-block btn-with-image btn-saml-login btn-success\" href=\"" . UTIL::get_root_url() . "saml.php\" alt=\"$ssoname Access\">Access with $ssoname" . "</a></p>";
					if($show_saml_logout)
						$ssolink .= "<p class=\"small text-center mt-0 mb-0\"><a tabindex=\"0\" class=\"mt-1 btn btn-block btn-with-image btn-login btn-warning\" href=\"" . UTIL::get_root_url() . "saml.php?slo\" alt=\"$ssoname Logout\">Logout from $ssoname" . "</a></p>";
				}
				else 
					$ssolink = "<p class=\"small text-center mt-2 mb-0\"><a tabindex=\"0\" class=\"btn btn-block btn-with-image btn-saml-login btn-success\" href=\"" . UTIL::get_root_url() . "saml.php\" alt=\"$ssoname Login\">Sign in with $ssoname" . "</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)
	{
		$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, $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/modules/$layout/css  folders will be searched.
	
	public function getCSSFilesInFolder($pagedata, $layout)
	{
		if(strpos($pagedata, "{{FONTS}}") !== false) {
			$fontcss = "";
			$fontfolder = UTIL::get_install_folder_path() . "lib/layouts/fonts/";
			$fonts = array("Quicksand", "Roboto Slab", "Josefin Sans");
			foreach($fonts as $fname) {
				// Folder names of locally stored fonts use - instead of space.
				$font = str_replace(" ", "-", $fname);
				$fontpath = $fontfolder . $font . "/fonts.css";
				if(is_file($fontpath))
					$fontcss .= "\t<link href=\"lib/layouts/fonts/" . $font . "/fonts.css\" rel=\"stylesheet\">\n";
				else {
					// Google imported fonts use + instead of space in font family name.
					$family = str_replace(" ", "+", $fname);
					$fontcss .= "\t<style>@import url('https://fonts.googleapis.com/css2?family=" . $family . "&display=swap');</style>\n";
				}
			}
			$fontcss = rtrim($fontcss);
			$pagedata = str_replace("{{FONTS}}", $fontcss, $pagedata);
		}
		if(strpos($pagedata, "{{LIBRARYCSS") !== false) {
			$name = $this->getResFileName($pagedata, "LIBRARYCSS", $replacestring, $filelist, $location);
			$cssinfo = $this->constructCSS($filelist, "", $name);
			$pagedata = str_replace($replacestring, rtrim($cssinfo), $pagedata);
		}
		if(strpos($pagedata, "{{MODULECSS") !== false) {
			$name = $this->getResFileName($pagedata, "MODULECSS", $replacestring, $filelist, $location);
			$location = ($location == "" ? $layout : $location);
			$cssinfo = $this->constructCSS($filelist, $location, $name);
			$pagedata = str_replace($replacestring, rtrim($cssinfo), $pagedata);
		}
		while(strpos($pagedata, "{{EXTERNCSS") !== false) {
			$name = $this->getResFileName($pagedata, "EXTERNCSS", $replacestring, $filelist, $location);
			$location = ($location == "" ? $layout : $location);
			$cssinfo = $this->constructCSS($filelist, $location, $name);
			$pagedata = str_replace($replacestring, rtrim($cssinfo), $pagedata);
		}
		return $pagedata;
	}
	
	###########################################################################
	
	private function constructCSS($filelist, $module, $name) {
		$cssinfo = "";
		if($module == "") {
			$cssfolder = UTIL::get_install_folder_path() . "lib/layouts/css/src";
			$minfolder = UTIL::get_install_folder_path() . "lib/layouts/css/min";
			$cssinpath = "lib/layouts/css/src";
			$cssoutpath = "lib/layouts/css/min";
		}
		else {
			$cssfolder = UTIL::get_install_folder_path() . "lib/layouts/modules/$module/css/src";
			$minfolder = UTIL::get_install_folder_path() . "lib/layouts/modules/$module/css/min";
			$cssinpath = "lib/layouts/modules/$module/css/src";
			$cssoutpath = "lib/layouts/modules/$module/css/min";
		}
		if($filelist != "" && $filelist != "*")
			$files = explode("|", $filelist);
		else {
			$files = UTIL::find_all_matched_files($cssfolder, "/*.css");
			sort($files);
		}
		$outfile = $minfolder . DIRECTORY_SEPARATOR . $name;
		$allfiles = array();
		$timestamp = 0;
		foreach($files as $file) {
			$cssfile = $cssfolder . "/" . $file;
			if(is_file($cssfile)) {
				$allfiles[] = $file;
				$ts = filemtime($cssfile);
				$timestamp = $ts > $timestamp ? $ts : $timestamp;
				$cssinfo .= "\t<link href=\"$cssinpath/$file\" rel=\"stylesheet\">\n";
			}
		}
		if($name != "" && count($allfiles)) {
			$createfile = (!is_file($outfile)) ? true : (($timestamp > filemtime($outfile)) ? true : false);
			if($createfile) {
				$cssdata = "";
				foreach($allfiles as $f) {
					$thefile = $cssfolder . "/$f";
					if(is_file($thefile))
						$cssdata .= trim(file_get_contents($thefile)) . "\n/* $thefile */\n\n";
				}
				$buffer = $this->getCSSData($cssdata);
				UTIL::create_containing_folders($minfolder);
				$buffer = UTIL::compressCSS($buffer);
				file_put_contents($outfile, $buffer);
			}
		}
		if(is_file($outfile)) {
			$cssinfo = "\t<link href=\"$cssoutpath/$name\" rel=\"stylesheet\">\n";
		}
		return $cssinfo;
	}
	
	###########################################################################
	# parse
	# {{$token:$filelist:$location:$name}} OR {{$token:$filelist:$name}} OR {{$token:$name}}
	# {{LIBRARYCSS:a,b,c,d:common.min.css}}
	# {{MODULECSS:dashboard.min.css}}  etc.
	#
	
	private function getResFileName($pagedata, $token, &$replacestring, &$filelist, &$location) {
		$filelist = "";
		$location = "";
		$pos = strpos($pagedata, "{{" . $token);
		$name = "";
		$replacestring = "{{" . $token . "}}";
		if(strpos($pagedata, "{{" . $token . ":") !== false) {
			$extract = substr($pagedata, $pos+strlen("{{" . $token . ":"), 1024);
			$posend = strpos($extract, "}}");
			if($posend !== false) {
				$extract = substr($extract, 0, $posend);
				$replacestring = "{{" . $token . ":" . $extract . "}}";
				$parts = explode(":", $extract);
				if(count($parts) > 1) {
					$filelist = $parts[0];
					if(count($parts) == 2)
						$name = $parts[1];
					else if(count($parts) > 2) {
						$location = $parts[1];
						$name = $parts[2];
					}
				}
			}
		}
		return $name;
	}
	
	###########################################################################
	
	private function constructJS($filelist, $module, $name) {
		$jsinfo = "";
		if($module == "") {
			$jsfolder = UTIL::get_install_folder_path() . "lib/layouts/js/src";
			$minfolder = UTIL::get_install_folder_path() . "lib/layouts/js/min";
			$jsinpath = "lib/layouts/js/src";
			$jsoutpath = "lib/layouts/js/min";
		}
		else {
			$jsfolder = UTIL::get_install_folder_path() . "lib/layouts/modules/$module/js/src";
			$minfolder = UTIL::get_install_folder_path() . "lib/layouts/modules/$module/js/min";
			$jsinpath = "lib/layouts/modules/$module/js/src";
			$jsoutpath = "lib/layouts/modules/$module/js/min";
		}
		if($filelist != "" && $filelist != "*")
			$files = explode("|", $filelist);
		else {
			$files = UTIL::find_all_matched_files($jsfolder, "/*.js");
			sort($files);
		}
		$outfile = $minfolder . DIRECTORY_SEPARATOR . $name;
		$allfiles = array();
		$timestamp = 0;
		foreach($files as $file) {
			$jsfile = $jsfolder . "/" . $file;
			if(is_file($jsfile)) {
				$allfiles[] = $file;
				$ts = filemtime($jsfile);
				$timestamp = $ts > $timestamp ? $ts : $timestamp;
				$jsinfo .= "<script type=\"text/javascript\" src=\"$jsinpath/$file\"></script>\n";
			}
		}
		if($name != "" && count($allfiles)) {
			$createfile = (!is_file($outfile)) ? true : (($timestamp > filemtime($outfile)) ? true : false);
			if($createfile) {
				$jsdata = "";
				foreach($allfiles as $f) {
					$thefile = $jsfolder . "/$f";
					if(is_file($thefile))
						$jsdata .= trim(file_get_contents($thefile)) . "\n/* $thefile */\n\n";
				}
				UTIL::create_containing_folders($minfolder);
				require_once UTIL::get_install_folder_path() . 'lib/php/JShrink/Minifier.php';
				$jsdata = \JShrink\Minifier::minify($jsdata);
				file_put_contents($outfile, $jsdata);
			}
		}
		if(is_file($outfile)) {
			$jsinfo = "<script type=\"text/javascript\" src=\"$jsoutpath/$name\"></script>\n";
		}
		return $jsinfo;
	}
	
	###########################################################################
	// Add the required JS Files. The minimized version will be used if found.
	// lib/layouts/js
	// lib/layouts/modules/$layout/js/src  folders will be searched.
	
	public function getJSFilesInFolder($pagedata, $layout)
	{
		if(strpos($pagedata, "{{LIBRARYJS") !== false) {
			$name = $this->getResFileName($pagedata, "LIBRARYJS", $replacestring, $filelist, $location);
			$jsinfo = $this->constructJS($filelist, "", $name);
			$pagedata = str_replace($replacestring, rtrim($jsinfo), $pagedata);
		}
		if(strpos($pagedata, "{{MODULEJS") !== false) {
			$name = $this->getResFileName($pagedata, "MODULEJS", $replacestring, $filelist, $location);
			$location = ($location == "" ? $layout : $location);
			$jsinfo = $this->constructJS($filelist, $location, $name);
			$pagedata = str_replace($replacestring, rtrim($jsinfo), $pagedata);
		}
		while(strpos($pagedata, "{{EXTERNJS") !== false) {
			$name = $this->getResFileName($pagedata, "EXTERNJS", $replacestring, $filelist, $location);
			$location = ($location == "" ? $layout : $location);
			$jsinfo = $this->constructJS($filelist, $location, $name);
			$pagedata = str_replace($replacestring, 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)
	{
		if(stristr($pagedata, "{{REGALIASES}}") !== false) {
			$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[strtolower($row['name'])] = $row['alias'];
						}
					}
				}
			}
			$pagedata = str_ireplace("{{REGALIASES}}", "var registrar_aliases = " . json_encode($aliases) . ";\n", $pagedata);
		}
		return $pagedata;
	}
	
	###########################################################################
	
	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 = "<div class=\"userinfo\">";
		foreach($userinfo as $key=>$value) {
			if($key == 'uid')
				continue;
			if($key == 'rights') {
				if($this->authentication && $this->authentication->isSetupAdmin())
					$value = "Setup Administrator";
				else if(is_numeric($value)) {
					$values = array();
					foreach($rights as $r=>$v) {
						if(intval($r) & $value)
							$values[] = $v;
					}
					$value = implode(", ", $values);
				} 
			}
			$key = ucfirst($key);
			if(is_array($value)) {
				$groups = array();
				foreach($value as $v)
					$groups[] = $v['name'];
				$value = implode(", ", $groups);
				$uinfo .= "<div class=\"userinfo-key\">Categories</div><div class=\"userinfo-data\">$value</div>\n";
			}
			else
				$uinfo .= "<div class=\"userinfo-key\">$key</div><div class=\"userinfo-data\"><b>$value</b></div>\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 .= "<div class=\"userinfo-key\">IDP</div><div class=\"userinfo-data\">$samldata</div>\n";
				}
			}
		}
		$uinfo .= "</div>\n";
		$pagedata = str_ireplace("{{USERINFO}}", $uinfo, $pagedata);

		$linfo = "<div class=\"appinfo\">";
		if($this->authentication) {
			$li = $this->authentication->getLicense();
			foreach($li as $key=>$value) {
				$linfo .= "<div class=\"appinfo-key\">$key</div><div class=\"appinfo-data\"><b>$value</b></div>\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") ? "New!" : "";
				$builddate = $setup->getOption("available_app_build_date", "");
				if($builddate != "")
					$linfo .= "<div class=\"appinfo-key\">Latest Version</div><div class=\"appinfo-data\"><b>$builddate</b> $new</div>\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 .= "</div>\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) {
					$tn = "";
					$table = $db->getDomainTableName();
					$pos = stripos($query, ":");
					if($pos !== false) {
						$tn = strtolower(substr($query, 0, $pos));
						if($tn == "sd")
							$table = $db->getSubdomainTableName();
						else if($tn == "ssl")
							$table = $db->getSSLCertificateTableName();
						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 = ($tn != "ssl") ? $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() 
	{
		return "";
	}
	
	###########################################################################
	
	public function getJSFile($filename, $templatefile=true) {
		$jsdata = "";
		if(is_file($filename)) {
			$jsdata = file_get_contents($filename);
			if($templatefile) {
				$approoturl = UTIL::get_root_url();
				$product = \CodePunch\Config\Defaults::product();
				$jsdata = str_ireplace("{{PRODUCT}}", $product, $jsdata);
				$jsdata = str_ireplace("{{APPROOTURL}}", $approoturl, $jsdata);
				$jsdata = str_ireplace("{{APIURLPATH}}", self::API_URL_PATH, $jsdata);

				$jsdata = $this->getImagesData($jsdata);
				$jsdata = $this->getDomainColumnJSModel($jsdata);
				$jsdata = $this->getSubdomainColumnJSModel($jsdata);
				$jsdata = $this->getUserJSData($jsdata);
				$jsdata = $this->getDomainData($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);
				
				$sslbrowser = 'false';
				$cookiebrowser = 'false';
				if($this->authentication) {
					$sslbrowser = ($this->authentication->getProClass("SSL", true) ? "true" : "false");
					$cookiebrowser = ($this->authentication->getProClass("Cookies", true) ? "true" : "false");
				}
				$jsdata = str_ireplace("{{SSLBROWSER}}", $sslbrowser, $jsdata);
				$jsdata = str_ireplace("{{COOKIEBROWSER}}", $cookiebrowser, $jsdata);
				
				$domainfields = $setup->getText("domain_data_fields", '');
				$jsdata = str_ireplace("{{DOMAINFIELDS}}", $domainfields, $jsdata);
				$jsdata = $this->getRegistrarJSAliases($jsdata);
			}
		}
		return strlen($jsdata) ? "$jsdata\n" : "";
	}
	
	###########################################################################
	
	public function getCSSData($styledata)
	{
		return $styledata;
	}
	
	###########################################################################
	
	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', 'backgrounds', '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;
	}
	
	###########################################################################
	
	public function getImagesData($homedata) {
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$logoimage = $setup->getOption("main_logo_image", false);
		$logoimage = $this->findImage($logoimage);
		if($logoimage === false)
			$logoimage = "";
		$homedata = str_ireplace("{{LOGOIMAGE}}", $logoimage, $homedata);
		
		$logoimage = $setup->getOption("big_logo_image", false);
		$logoimage = $this->findImage($logoimage);
		if($logoimage === false)
			$logoimage = "";
		$homedata = str_ireplace("{{BIGLOGOIMAGE}}", $logoimage, $homedata);
		
		$bgimage = $setup->getOption("background_image", false);
		$bgimage = $this->findImage($bgimage);
		if($bgimage === false)
			$bgimage = "";
		$homedata = str_ireplace("{{BACKGROUNDIMAGE}}", $bgimage, $homedata);
		
		$optionvalue = $setup->getOption("background_property", false);
		if($optionvalue === false)
			$optionvalue = "";
		$homedata = str_ireplace("{{BACKGROUNDPROP}}", $optionvalue, $homedata);
		
		$optionvalue = $setup->getOption("background_size", false);
		if($optionvalue === false)
			$optionvalue = "";
		$homedata = str_ireplace("{{BACKGROUNDSIZE}}", $optionvalue, $homedata);
		
		return $homedata;
	}
	
	###########################################################################
	
	private function getLayoutData($layout, $dataname)
	{
		$thedata = "";
		$folder = UTIL::get_install_folder_path();
		$datafile = $folder . "lib/layouts/$dataname.htm";
		if(!is_file($datafile))
			$datafile = $folder . "lib/layouts/modules/$dataname.htm";
		if(!is_file($datafile))
			$datafile = $folder . "lib/layouts/modules/$layout/$dataname.htm";
		if(!is_file($datafile)) 
			$datafile = $folder . "lib/layouts/modules/common/$dataname.htm";
		if(is_file($datafile))
			$thedata = file_get_contents($datafile);
		return $thedata;
	}		
	
	###########################################################################
	
	private function getAllPanels($layout)
	{
		$folder = UTIL::get_install_folder_path();
		$panelsfolder = $folder . "lib/layouts/modules/$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);
				}
			}
		}
		$thedata = $this->getIncudedFiles($thedata, $layout);
		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)
				throw new Exception(TEXT::get("wrong_php_version"));
			$ioncubestatus = \CodePunch\Config\ConfigRoot::check_ioncube_status();
			if($ioncubestatus == \CodePunch\Config\ConfigRoot::IONCUBE_REQUIRED_UNSUPPORTED_PHP)
				throw new Exception(TEXT::get("no_ioncube_loader_for_php_8_0"));
			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'])){
					$folder = UTIL::get_install_folder_path();
					$homefile = $folder . "lib/layouts/modules/dashboard/verify.htm";
					$pageinfo['home'] = file_get_contents($homefile);
					$pageinfo['heading'] = "<h1>" . TEXT::get("db_therewasaproblem") . "</h1>";
					$pageinfo['body'] = "<p class=\"problem\">" . $e->getMessage() . "</p>";
				}
				$layout->show($pageinfo);
			}
		}
	}
	
}

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