2023-01-31 00:30:04 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace EesyPHP;
|
|
|
|
|
|
|
|
use Exception;
|
|
|
|
use Smarty;
|
|
|
|
use Smarty_Security;
|
|
|
|
|
|
|
|
class Tpl {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Smarty object
|
|
|
|
* @var \Smarty
|
|
|
|
*/
|
|
|
|
public static Smarty $smarty;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Smarty_Security object
|
|
|
|
* @var \Smarty_Security|null
|
|
|
|
*/
|
|
|
|
public static $smarty_security_policy = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Smarty templates directory path
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
public static string $templates_dir;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Smarty cache templates directory path
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
public static string $templates_c_dir;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable/disable AJAX returned data debugging in logs
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public static bool $_debug_ajax;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CSS files to load in next displayed page
|
|
|
|
* @var array<string>
|
|
|
|
*/
|
|
|
|
private static array $css_files = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* JavaScript files to load in next displayed page
|
|
|
|
* @var array<string>
|
|
|
|
*/
|
|
|
|
private static array $js_files = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialization
|
|
|
|
* @param string $templates_dir Smarty templates directory path
|
2023-02-08 02:27:15 +01:00
|
|
|
* (optional, default: from template.directory config key)
|
2023-01-31 00:30:04 +01:00
|
|
|
* @param string $templates_c_dir Smarty cache templates directory path
|
2023-02-08 02:27:15 +01:00
|
|
|
* (optional, default: from template.cache_directory config key)
|
2023-01-31 00:30:04 +01:00
|
|
|
* @param bool $debug_ajax Enable/disable AJAX returned data debugging in logs
|
2023-02-08 02:27:15 +01:00
|
|
|
* (optional, default: from template.debug_ajax or debug_ajax config keys if set,
|
|
|
|
* false otherwise)
|
2023-01-31 00:30:04 +01:00
|
|
|
* @return void
|
|
|
|
*/
|
2023-02-08 02:27:15 +01:00
|
|
|
public static function init($templates_dir=null, $templates_c_dir=null, $debug_ajax=null) {
|
2023-01-31 00:30:04 +01:00
|
|
|
// Check templates/templates_c directories
|
2023-02-08 02:27:15 +01:00
|
|
|
if (is_null($templates_dir))
|
|
|
|
$templates_dir = Config::get('template.directory', null, 'string');
|
|
|
|
if (is_null($templates_c_dir))
|
|
|
|
$templates_c_dir = Config::get('template.cache_directory', null, 'string');
|
2023-01-31 00:30:04 +01:00
|
|
|
if (!$templates_dir || !is_dir($templates_dir)) {
|
|
|
|
Log :: fatal(
|
|
|
|
"Template directory not found (%s)",
|
|
|
|
$templates_dir?$templates_dir:'not set');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!$templates_c_dir || !is_dir($templates_c_dir) || !is_writable($templates_c_dir)) {
|
|
|
|
Log :: fatal(
|
|
|
|
"Template cache directory not found or not writable (%s)",
|
|
|
|
$templates_c_dir?$templates_c_dir:'not set');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self :: $smarty = new Smarty();
|
|
|
|
self :: $smarty->setTemplateDir($templates_dir);
|
|
|
|
self :: $smarty->setCompileDir($templates_c_dir);
|
2023-02-08 02:27:15 +01:00
|
|
|
if (is_null($debug_ajax))
|
|
|
|
$debug_ajax = Config::get('template.debug_ajax', Config::get('debug_ajax'));
|
2023-01-31 00:30:04 +01:00
|
|
|
self :: $_debug_ajax = boolval($debug_ajax);
|
|
|
|
Log :: register_fatal_error_handler(array('\\EesyPHP\\Tpl', 'fatal_error'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable security in mode to limit functions (in IF clauses) and modifiers usable from
|
|
|
|
* template files
|
|
|
|
* @param array<string>|null $functions List of function names granted in IF clauses
|
|
|
|
* @param array<string>|null $modifiers List of modifier names granted
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function enable_security_mode($functions=null, $modifiers=null) {
|
|
|
|
// Define security policy
|
|
|
|
self :: $smarty_security_policy = new Smarty_Security(self :: $smarty);
|
|
|
|
|
|
|
|
// Allow functions in IF clauses
|
|
|
|
if (is_array($functions))
|
|
|
|
foreach($functions as $function)
|
|
|
|
self :: $smarty_security_policy->php_functions[] = $function;
|
|
|
|
|
|
|
|
// Allow modifier functions
|
|
|
|
if (is_array($modifiers))
|
|
|
|
foreach($modifiers as $modifier)
|
|
|
|
self :: $smarty_security_policy->php_modifiers[] = $modifier;
|
|
|
|
|
|
|
|
// Enable security
|
|
|
|
self :: $smarty -> enableSecurity(self :: $smarty_security_policy);
|
|
|
|
|
|
|
|
// Initialize errors & messages session variables
|
|
|
|
if (!isset($_SESSION['errors']))
|
|
|
|
$_SESSION['errors'] = array();
|
|
|
|
if (!isset($_SESSION['messages']))
|
|
|
|
$_SESSION['messages'] = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a function usable from template files
|
|
|
|
* @param string $name The function name
|
|
|
|
* @param callable $callable The function
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function register_function($name, $callable) {
|
|
|
|
self :: $smarty -> registerPlugin("function", $name, $callable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assign template variable
|
|
|
|
* @param string $name The variable name
|
|
|
|
* @param mixed $value The variable value
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function assign($name, $value) {
|
|
|
|
self :: $smarty -> assign($name, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add error message
|
|
|
|
* @param string $error The message
|
|
|
|
* @param array $extra_args Extra arguments to use to compute error message using sprintf
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function add_error($error, ...$extra_args) {
|
|
|
|
// If extra arguments passed, format error message using sprintf
|
|
|
|
if ($extra_args) {
|
|
|
|
$error = call_user_func_array(
|
|
|
|
'sprintf',
|
|
|
|
array_merge(array($error), $extra_args)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$_SESSION['errors'][] = $error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add informational message
|
|
|
|
* @param string $message The message
|
|
|
|
* @param array $extra_args Extra arguments to use to compute message using sprintf
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function add_message($message, ...$extra_args) {
|
|
|
|
// If extra arguments passed, format message using sprintf
|
|
|
|
if ($extra_args) {
|
|
|
|
$message = call_user_func_array(
|
|
|
|
'sprintf',
|
|
|
|
array_merge(array($message), $extra_args)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$_SESSION['messages'][] = $message;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register CSS file(s) to load on next displayed page
|
|
|
|
* @param array<string|array<string>> $args CSS files to load
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function add_css_file(...$args) {
|
|
|
|
foreach ($args as $files) {
|
|
|
|
if (!is_array($files)) $files = array($files);
|
|
|
|
foreach ($files as $file) {
|
|
|
|
if (!in_array($file, self :: $css_files))
|
|
|
|
self :: $css_files[] = $file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register JS file(s) to load on next displayed page
|
|
|
|
* @param array<string|array<string>> $args JS files to load
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function add_js_file(...$args) {
|
|
|
|
foreach ($args as $files) {
|
|
|
|
if (!is_array($files)) $files = array($files);
|
|
|
|
foreach ($files as $file) {
|
|
|
|
if (!in_array($file, self :: $js_files))
|
|
|
|
self :: $js_files[] = $file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define common variables
|
|
|
|
* @param string|null $pagetitle The page title
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
protected static function define_common_variables($pagetitle=null) {
|
|
|
|
global $auth_user;
|
|
|
|
self :: assign('pagetitle', $pagetitle);
|
|
|
|
|
|
|
|
// Messages
|
|
|
|
self :: assign('errors', (isset($_SESSION['errors'])?$_SESSION['errors']:array()));
|
|
|
|
self :: assign('messages', (isset($_SESSION['messages'])?$_SESSION['messages']:array()));
|
|
|
|
|
|
|
|
// Files inclusions
|
|
|
|
self :: assign('css', self :: $css_files);
|
|
|
|
self :: assign('js', self :: $js_files);
|
|
|
|
|
|
|
|
// Authenticated user info
|
|
|
|
if (isset($auth_user))
|
|
|
|
self :: assign('auth_user', $auth_user);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display the template
|
|
|
|
* @param string $template The template to display
|
|
|
|
* @param string|null $pagetitle The page title (optional)
|
|
|
|
* @param array $extra_args Extra arguments to use to compute the page title using sprintf
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function display($template, $pagetitle=null, ...$extra_args) {
|
|
|
|
if (!$template) {
|
|
|
|
Log :: fatal(_("No template specified."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If refresh parameter is present, remove it and redirect
|
|
|
|
if (isset($_GET['refresh'])) {
|
|
|
|
unset($_GET['refresh']);
|
|
|
|
$url = Url :: get_current_url();
|
|
|
|
if (!empty($_GET))
|
|
|
|
$url .= '?'.http_build_query($_GET);
|
|
|
|
Url :: redirect($url);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$sentry_span = new SentrySpan('smarty.display_template', "Display Smarty template");
|
|
|
|
|
|
|
|
// If extra arguments passed, format pagetitle using sprintf
|
|
|
|
if ($pagetitle && $extra_args) {
|
|
|
|
$pagetitle = call_user_func_array(
|
|
|
|
'sprintf',
|
|
|
|
array_merge(array($pagetitle), $extra_args)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
Hook :: trigger('before_displaying_template');
|
|
|
|
self :: define_common_variables($pagetitle);
|
|
|
|
self :: $smarty->display($template);
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
Log :: exception($e, "Smarty - An exception occured displaying template '$template'");
|
|
|
|
if ($template != 'fatal_error.tpl')
|
|
|
|
Log :: fatal(_("An error occurred while displaying this page."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unset($_SESSION['errors']);
|
|
|
|
unset($_SESSION['messages']);
|
|
|
|
Hook :: trigger('after_displaying_template');
|
|
|
|
$sentry_span->finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display AJAX return
|
|
|
|
* @param array|null $data AJAX returned data (optional)
|
|
|
|
* @param bool $pretty AJAX returned data
|
|
|
|
* (optional, default: true if $_REQUEST['pretty'] is set, False otherwise)
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function display_ajax_return($data=null, $pretty=false) {
|
|
|
|
if (!is_array($data))
|
|
|
|
$data = array();
|
|
|
|
// Adjust HTTP error code on unsuccessfull request
|
|
|
|
elseif (isset($data['success']) && !$data['success'] && http_response_code() == 200)
|
|
|
|
http_response_code(400);
|
|
|
|
|
|
|
|
if (isset($_SESSION['messages']) && !empty($_SESSION['messages'])) {
|
|
|
|
$data['messages'] = $_SESSION['messages'];
|
|
|
|
unset($_SESSION['messages']);
|
|
|
|
}
|
|
|
|
if (isset($_SESSION['errors']) && !empty($_SESSION['errors'])) {
|
|
|
|
$data['errors'] = $_SESSION['errors'];
|
|
|
|
unset($_SESSION['errors']);
|
|
|
|
}
|
|
|
|
if (self :: $_debug_ajax)
|
|
|
|
Log :: debug("AJAX Response : ".vardump($data));
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
echo json_encode($data, (($pretty||isset($_REQUEST['pretty']))?JSON_PRETTY_PRINT:0));
|
|
|
|
exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a fatal error
|
|
|
|
* @param string $error The error message
|
|
|
|
* @param array $extra_args Extra arguments to use to compute the error message using sprintf
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function fatal_error($error, ...$extra_args) {
|
|
|
|
// If extra arguments passed, format error message using sprintf
|
|
|
|
if ($extra_args) {
|
|
|
|
$error = call_user_func_array(
|
|
|
|
'sprintf',
|
|
|
|
array_merge(array($error), $extra_args)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (php_sapi_name() == "cli")
|
|
|
|
die("FATAL ERROR : $error\n");
|
|
|
|
|
|
|
|
// Set HTTP reponse code to 500
|
|
|
|
http_response_code(500);
|
|
|
|
|
|
|
|
// Handle API mode
|
|
|
|
if (Url :: api_mode()) {
|
|
|
|
self :: display_ajax_return(array('success' => false, 'error' => $error));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self :: assign('fatal_error', $error);
|
|
|
|
self :: display('fatal_error.tpl');
|
|
|
|
exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get/set AJAX debug mode
|
|
|
|
* @param bool|null $value If boolean, the current API mode will be changed
|
|
|
|
* @return bool Current API mode
|
|
|
|
*/
|
|
|
|
public static function debug_ajax($value=null) {
|
|
|
|
if (is_bool($value)) self :: $_debug_ajax = $value;
|
|
|
|
return self :: $_debug_ajax;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if initialized
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function initialized() {
|
|
|
|
return isset(self :: $smarty);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the templates directory path
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
public static function templates_directory() {
|
|
|
|
return isset(self :: $templates_directory)?self :: $templates_directory:null;
|
|
|
|
}
|
|
|
|
}
|