diff --git a/includes/core.php b/includes/core.php index cbc98bc..a938432 100644 --- a/includes/core.php +++ b/includes/core.php @@ -74,7 +74,7 @@ $status_list = array ( ); require_once('cli.php'); -require_once('smarty.php'); +require_once('templates.php'); Url::init(isset($public_root_url)?$public_root_url:null); require_once('url-helpers.php'); require_once('db.php'); diff --git a/includes/functions.php b/includes/functions.php index db6a41b..fbfc42a 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -3,6 +3,8 @@ use EesyPHP\Check; use EesyPHP\Log; use EesyPHP\Session; +use EesyPHP\Tpl; +use EesyPHP\Url; /* * Check values helpers @@ -312,12 +314,12 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t **/ function check_ajax_request($session_key=null) { global $ajax, $debug_ajax; - $ajax = true; + Url :: api_mode(true); if (Session :: check_key($session_key)) - fatal_error('Invalid request'); + Tpl :: fatal_error('Invalid request'); - if ($debug_ajax) + if (Tpl :: debug_ajax()) Log :: debug("Ajax Request : ".vardump($_REQUEST)); } diff --git a/includes/smarty.php b/includes/smarty.php deleted file mode 100644 index b264562..0000000 --- a/includes/smarty.php +++ /dev/null @@ -1,329 +0,0 @@ - security = True; - - // Allow functions in IF clauses - foreach($functions as $function) - $smarty -> security_settings['IF_FUNCS'][] = $function; - - // Allow modifier functions - foreach($modifiers as $modifier) - $smarty -> security_settings['MODIFIER_FUNCS'][] = $modifier; - } - - function smarty_register_function($func_name, $func) { - global $smarty; - $smarty -> register_function($func_name, $func); - } -} -elseif (method_exists($smarty,'registerPlugin')) { - $_smarty_version = 3; - - function smarty_enable_security_mode($functions=array(), $modifiers=array()) { - global $smarty; - - // Define security policy - $smarty_security_policy = new Smarty_Security($smarty); - - // Allow functions in IF clauses - foreach($functions as $function) - $smarty_security_policy->php_functions[] = $function; - - // Allow modifier functions - foreach($modifiers as $modifier) - $smarty_security_policy->php_modifiers[] = $modifier; - - // Enable security - $smarty->enableSecurity($smarty_security_policy); - } - - function smarty_register_function($func_name, $func) { - global $smarty; - $smarty -> registerPlugin("function", $func_name, $func); - } -} -else { - Log :: fatal(_('Smarty version not supported.')); -} - -// Configure templates/templates_c directories -if ( - !isset($smarty_templates_dir) - || !is_dir($smarty_templates_dir) -) - Log :: fatal( - "Template directory not found (%s)", - isset($smarty_templates_dir)?$smarty_templates_dir:'not set'); -else - $smarty->setTemplateDir($smarty_templates_dir); - -if ( - !isset($smarty_templates_c_dir) - || !is_dir($smarty_templates_c_dir) - || !is_writable($smarty_templates_c_dir) -) - Log :: fatal( - "Template cache directory not found or not writable (%s)", - isset($smarty_templates_c_dir)?$smarty_templates_c_dir:'not set'); -else - $smarty->setCompileDir($smarty_templates_c_dir); - -// Enable Smarty security -smarty_enable_security_mode( - // Allow some functions in IF clauses - array( - 'isset', 'empty', 'count', 'in_array', 'is_array', 'array_key_exists', 'is_null', - 'can_modify', 'can_archive', 'can_delete' - ), - // Allow some modifier functions - array('range', 'implode', 'stripslashes') -); - -// Defined some global template variables -$smarty->assign('public_root_url', isset($public_root_url)?$public_root_url:'/'); -$smarty->assign('main_pagetitle', isset($main_pagetitle)?$main_pagetitle:null); -$smarty->assign('session_key', $_SESSION['session_key']); - -// Handle in-page errors & messages -if (!isset($_SESSION['errors'])) - $_SESSION['errors'] = array(); -function add_error($error) { - // If more than one arguments passed, format error message using sprintf - if (func_num_args() > 1) { - $error = call_user_func_array( - 'sprintf', - array_merge(array($error), array_slice(func_get_args(), 1)) - ); - } - $_SESSION['errors'][] = $error; -} - -if (!isset($_SESSION['messages'])) - $_SESSION['messages'] = array(); -function add_message($message) { - // If more than one arguments passed, format message using sprintf - if (func_num_args() > 1) { - $message = call_user_func_array( - 'sprintf', - array_merge(array($message), array_slice(func_get_args(), 1)) - ); - } - $_SESSION['messages'][] = $message; -} - -// Handle CSS & JS files included -if (isset($included_css_files) && is_array($included_css_files)) { - $_css = $included_css_files; -} -else { - $_css = array(); -} -function add_css_file() { - global $_css; - foreach (func_get_args() as $files) { - if (!is_array($files)) $files=array($files); - foreach ($files as $file) { - if (!in_array($file, $_css)) - $_css[]=$file; - } - } -} - -$_js = array(); -function add_js_file() { - global $_js; - foreach (func_get_args() as $files) { - if (!is_array($files)) $files = array($files); - foreach ($files as $file) { - if (!in_array($file, $_js)) - $_js[] = $file; - } - } -} - -function _defineCommonTemplateVariables($template, $pagetitle) { - global $smarty, $_css, $_js, $status_list, $auth_user, $admin, $webstats_js_code; - $smarty->assign('pagetitle', $pagetitle); - - // Messages - $smarty -> assign('errors', (isset($_SESSION['errors'])?$_SESSION['errors']:array())); - $smarty -> assign('messages', (isset($_SESSION['messages'])?$_SESSION['messages']:array())); - - // Files inclusions - $smarty -> assign('css', $_css); - $smarty -> assign('js', $_js); - - // Authenticated user info - if (isset($auth_user)) - $smarty->assign('auth_user', $auth_user); - - // Webstats JS code - $smarty->assign( - 'webstats_js_code', - isset($webstats_js_code)?$webstats_js_code:null); -} - -function display_template($template, $pagetitle=false) { - if (!$template) - Log :: fatal(_("No template specified.")); - - // 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"); - - global $smarty; - // If more than 2 arguments passed, format pagetitle using sprintf - if ($pagetitle && func_num_args() > 2) { - $pagetitle = call_user_func_array( - 'sprintf', - array_merge(array($pagetitle), array_slice(func_get_args(), 2)) - ); - } - try { - _defineCommonTemplateVariables($template, $pagetitle); - $smarty->display($template); - unset($_SESSION['errors']); - unset($_SESSION['messages']); - } - 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.")); - } - - $sentry_span->finish(); - -} - -function display_ajax_return($data=null, $pretty=false) { - global $debug_ajax; - if (is_null($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 ($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(); -} - -$ajax=false; -function fatal_error($error) { - global $smarty, $ajax; - - // If more than one arguments passed, format error message using sprintf - if (func_num_args() > 1) { - $error = call_user_func_array( - 'sprintf', - array_merge(array($error), array_slice(func_get_args(), 1)) - ); - } - - if (php_sapi_name() == "cli") - die("FATAL ERROR : $error\n"); - - // Set HTTP reponse code to 500 - http_response_code(500); - - if ($ajax || Url :: api_mode()) - display_ajax_return(array('success' => false, 'error' => $error)); - - $smarty->assign('fatal_error', $error); - display_template('fatal_error.tpl'); - exit(); -} - -// Templates functions -function smarty_item_status($params) { - global $status_list; - $status2class = array ( - 'pending' => 'info', - 'validated' => 'success', - 'refused' => 'danger', - 'archived' => 'secondary', - ); - if (array_key_exists($params['item']['status'], $status2class)) { - $class = $status2class[$params['item']['status']]; - } - else - $class='danger'; - echo ""; - echo ( - array_key_exists($params['item']['status'], $status_list)? - $status_list[$params['item']['status']]: - "Inconnu (".$params['item']['status'].")" - ); - echo ""; -} -smarty_register_function('item_status','smarty_item_status'); - -function smarty_format_time($params) { - echo format_time($params['time'], (!isset($params['with_time']) || (bool)$params['with_time'])); -} -smarty_register_function('format_time','smarty_format_time'); - -function smarty_format_size($params, $smarty) { - if(!isset($params['digit'])) $params['digit'] = 2; - echo format_size($params['size'],$params['digit']); -} -smarty_register_function('format_size','smarty_format_size'); - -function smarty_table_ordered_th($params, $smarty) { - if ($params['order'] && $params['url'] && $params['text'] && is_array($params['search'])) { - $params['url'] .= (strpos($params['url'], '?') === false?'?':'&')."order=".$params['order']; - echo "".$params['text'].""; - } - if ($params['order']==$params['search']['order']) { - echo ( - ' '); - } -} -smarty_register_function('table_ordered_th','smarty_table_ordered_th'); - -function smarty_encodeJsonBase64($params, $smarty) { - if (isset($params['data'])) - echo base64_encode(json_encode($params['data'])); -} -smarty_register_function('encodeJsonBase64','smarty_encodeJsonBase64'); - -# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab diff --git a/includes/templates.php b/includes/templates.php new file mode 100644 index 0000000..bcacba7 --- /dev/null +++ b/includes/templates.php @@ -0,0 +1,107 @@ + 'info', + 'validated' => 'success', + 'refused' => 'danger', + 'archived' => 'secondary', + ); + if (array_key_exists($params['item']['status'], $status2class)) { + $class = $status2class[$params['item']['status']]; + } + else + $class='danger'; + echo ""; + echo ( + array_key_exists($params['item']['status'], $status_list)? + $status_list[$params['item']['status']]: + "Inconnu (".$params['item']['status'].")" + ); + echo ""; +} +Tpl :: register_function('item_status','smarty_item_status'); + +function smarty_format_time($params) { + echo format_time($params['time'], (!isset($params['with_time']) || (bool)$params['with_time'])); +} +Tpl :: register_function('format_time','smarty_format_time'); + +function smarty_format_size($params, $smarty) { + if(!isset($params['digit'])) $params['digit'] = 2; + echo format_size($params['size'],$params['digit']); +} +Tpl :: register_function('format_size','smarty_format_size'); + +function smarty_table_ordered_th($params, $smarty) { + if ($params['order'] && $params['url'] && $params['text'] && is_array($params['search'])) { + $params['url'] .= (strpos($params['url'], '?') === false?'?':'&')."order=".$params['order']; + echo "".$params['text'].""; + } + if ($params['order']==$params['search']['order']) { + echo ( + ' '); + } +} +Tpl :: register_function('table_ordered_th','smarty_table_ordered_th'); + +function smarty_encodeJsonBase64($params, $smarty) { + if (isset($params['data'])) + echo base64_encode(json_encode($params['data'])); +} +Tpl :: register_function('encodeJsonBase64','smarty_encodeJsonBase64'); + +# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab diff --git a/includes/url-helpers.php b/includes/url-helpers.php index f43ceb7..9332631 100644 --- a/includes/url-helpers.php +++ b/includes/url-helpers.php @@ -2,6 +2,7 @@ use EesyPHP\Check; use EesyPHP\Log; +use EesyPHP\Tpl; function get_item_from_url($id, $fatal=false) { if (!Check :: id($id)) @@ -12,7 +13,7 @@ function get_item_from_url($id, $fatal=false) { $error = sprintf(_("Item #% s not found."), $id); if ($fatal) Log :: fatal($error); - add_error($error); + Tpl :: add_error($error); return false; } return $item; diff --git a/includes/url-public.php b/includes/url-public.php index bbfc7e1..de5b773 100644 --- a/includes/url-public.php +++ b/includes/url-public.php @@ -2,6 +2,7 @@ use EesyPHP\Check; use EesyPHP\Log; +use EesyPHP\Tpl; use EesyPHP\Url; use function EesyPHP\vardump; @@ -10,12 +11,12 @@ if (php_sapi_name() == "cli") return true; function handle_homepage($request) { - display_template("homepage.tpl", _("Hello world !")); + Tpl :: display("homepage.tpl", _("Hello world !")); } Url :: add_url_handler('#^$#', 'handle_homepage'); function handle_search($request) { - global $smarty, $status_list; + global $status_list; // Manage params if( @@ -39,7 +40,7 @@ function handle_search($request) { if (check_status($_REQUEST['status']) || $_REQUEST['status'] == 'all') $_SESSION['search']['status'] = $_REQUEST['status']; else - $smarty -> assign('status_error', true); + Tpl :: assign('status_error', true); } if (isset($_REQUEST['pattern'])) { @@ -48,7 +49,7 @@ function handle_search($request) { else if (Check :: search_pattern($_REQUEST['pattern'])) $_SESSION['search']['pattern'] = $_REQUEST['pattern']; else - $smarty -> assign('pattern_error', true); + Tpl :: assign('pattern_error', true); } // Order @@ -91,23 +92,23 @@ function handle_search($request) { Log :: debug('Search params : '.vardump($_SESSION['search'])); $result = search_items($_SESSION['search']); if (!is_array($result)) - fatal_error( + Tpl :: fatal_error( _("An error occurred while listing the items. ". "If the problem persists, please contact support.") ); - $smarty -> assign('result', $result); - $smarty -> assign('search', $_SESSION['search']); - $smarty -> assign('nbs_by_page', $nbs_by_page); - $smarty -> assign('status_list', $status_list); + Tpl :: assign('result', $result); + Tpl :: assign('search', $_SESSION['search']); + Tpl :: assign('nbs_by_page', $nbs_by_page); + Tpl :: assign('status_list', $status_list); - add_js_file(array( + Tpl :: add_js_file(array( 'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js', 'js/myconfirm.js', 'js/search.js' )); - display_template("search.tpl", _("Search")); + Tpl :: display("search.tpl", _("Search")); } Url :: add_url_handler('|^item/?$|', 'handle_search'); @@ -116,21 +117,19 @@ Url :: add_url_handler('|^item/?$|', 'handle_search'); */ function handle_show($request) { - global $smarty; - $item = get_item_from_url($request -> id); if (!$item) Url :: error_404(); - $smarty->assign('item', $item); + Tpl :: assign('item', $item); // Dialog - add_js_file(array( + Tpl :: add_js_file(array( 'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js', 'js/myconfirm.js', )); - display_template( + Tpl :: display( "show.tpl", _("Element %s"), (is_array($item)?$item['name']:"#".$request -> id) ); @@ -138,42 +137,42 @@ function handle_show($request) { Url :: add_url_handler('|^item/(?P[0-9]+)$|', 'handle_show'); function handle_create($request) { - global $smarty, $status_list; + global $status_list; $info = array(); $field_errors = handle_item_post_data($info); if (isset($_POST['submit']) && empty($field_errors)) { $item = add_item($info); if (is_array($item)) { - add_message(_("The element '% s' has been created."), $item['name']); + Tpl :: add_message(_("The element '% s' has been created."), $item['name']); Url :: redirect('item/'.$item['id']); } else { - add_error(_("An error occurred while saving this item.")); + Tpl :: add_error(_("An error occurred while saving this item.")); } } Log :: debug('Validated data : '.vardump($info)); Log :: debug('Fields errors : '.vardump($field_errors)); if (isset($_POST['submit']) && !empty($field_errors)) - add_error( + Tpl :: add_error( _("There are errors preventing this item from being saved. ". "Please correct them before attempting to add this item.")); - $smarty->assign('submited', isset($_POST['submit'])); - $smarty->assign('info', $info); - $smarty->assign('field_errors', $field_errors); - $smarty->assign('status_list', $status_list); + Tpl :: assign('submited', isset($_POST['submit'])); + Tpl :: assign('info', $info); + Tpl :: assign('field_errors', $field_errors); + Tpl :: assign('status_list', $status_list); - display_template("form.tpl", _("New")); + Tpl :: display("form.tpl", _("New")); } Url :: add_url_handler('|^item/new$|', 'handle_create'); function handle_modify($request) { - global $smarty, $status_list; + global $status_list; $item = get_item_from_url($request -> id); if(is_array($item)) { if (!can_modify($item)) { - add_error(_('You cannot edit this item.')); + Tpl :: add_error(_('You cannot edit this item.')); Url :: redirect('item/'.$item['id']); } $info = array(); @@ -186,34 +185,34 @@ function handle_modify($request) { } Log :: debug('Changes : '.vardump($changes)); if (empty($changes)) { - add_message(_("You have not made any changes to element '% s'."), $item['name']); + Tpl :: add_message(_("You have not made any changes to element '% s'."), $item['name']); Url :: redirect('item/'.$item['id']); } else if (update_item($item['id'], $changes) === true) { - add_message(_("The element '% s' has been updated successfully."), $item['name']); + Tpl :: add_message(_("The element '% s' has been updated successfully."), $item['name']); Url :: redirect('item/'.$item['id']); } else { - add_error(_("An error occurred while updating this item.")); + Tpl :: add_error(_("An error occurred while updating this item.")); } } Log :: debug('Validated data : '.vardump($info)); Log :: debug('Fields errors : '.vardump($field_errors)); - $smarty->assign('submited', isset($_POST['submit'])); + Tpl :: assign('submited', isset($_POST['submit'])); if (isset($_POST['submit']) && !empty($field_errors)) - add_error( + Tpl :: add_error( _("There are errors preventing this item from being saved. ". "Please correct them before attempting to save your changes.")); - $smarty->assign('info', (!empty($info)?$info:$item)); - $smarty->assign('item_id', $item['id']); - $smarty->assign('field_errors', $field_errors); - $smarty -> assign('status_list', $status_list); + Tpl :: assign('info', (!empty($info)?$info:$item)); + Tpl :: assign('item_id', $item['id']); + Tpl :: assign('field_errors', $field_errors); + Tpl :: assign('status_list', $status_list); } else { Url :: error_404(); } - display_template( + Tpl :: display( "form.tpl", _("Element %s: Modification"), (is_array($item)?$item['name']:"#".$request -> id) ); @@ -221,44 +220,40 @@ function handle_modify($request) { Url :: add_url_handler('|^item/(?P[0-9]+)/modify$|', 'handle_modify'); function handle_archive($request) { - global $smarty; - $item = get_item_from_url($request -> id); if(!is_array($item)) { - add_error(_("Item #% s not found."), $request -> id); + Tpl :: add_error(_("Item #% s not found."), $request -> id); Url :: redirect('item'); } elseif ($item['status'] == 'archived') { - add_message(_("This item is already archived.")); + Tpl :: add_message(_("This item is already archived.")); } else if (!can_archive($item)) { - add_error(_('You cannot archive this item.')); + Tpl :: add_error(_('You cannot archive this item.')); } else if (archive_item($item['id']) === true) { - add_message(_("The element '% s' has been archived successfully."), $item['name']); + Tpl :: add_message(_("The element '% s' has been archived successfully."), $item['name']); } else { - add_error(_('An error occurred while archiving this item.')); + Tpl :: add_error(_('An error occurred while archiving this item.')); } Url :: redirect('item/'.$item['id']); } Url :: add_url_handler('|^item/(?P[0-9]+)/archive$|', 'handle_archive'); function handle_delete($request) { - global $smarty; - $item = get_item_from_url($request -> id); if(!is_array($item)) { - add_error(_("Item #% s not found."), $request -> id); + Tpl :: add_error(_("Item #% s not found."), $request -> id); } else if (!can_delete($item)) { - add_error(_('You cannot delete this item.')); + Tpl :: add_error(_('You cannot delete this item.')); } else if (delete_item($item['id']) === true) { - add_message(_("The element '% s' has been deleted successfully."), $item['name']); + Tpl :: add_message(_("The element '% s' has been deleted successfully."), $item['name']); } else { - add_error(_('An error occurred while deleting this item.')); + Tpl :: add_error(_('An error occurred while deleting this item.')); Url :: redirect('item/'.$item['id']); } Url :: redirect('item'); diff --git a/src/I18n.php b/src/I18n.php index c7f7871..703f4d1 100644 --- a/src/I18n.php +++ b/src/I18n.php @@ -4,9 +4,9 @@ namespace EesyPHP; use EesyPHP\Cli; use EesyPHP\Log; +use EesyPHP\Tpl; use Locale; -use add_js_file; class I18n { // Gettext text domain @@ -109,9 +109,9 @@ class I18n { php_sapi_name() != "cli" && isset($root_dir_path) && $root_dir_path && is_file("$root_dir_path/public_html/$js_translation_file") - && function_exists('add_js_file') + && Tpl :: initialized() ) { - add_js_file(array("lib/babel.js", "js/translation.js", $js_translation_file)); + Tpl :: add_js_file(array("lib/babel.js", "js/translation.js", $js_translation_file)); } if (php_sapi_name() == 'cli') { @@ -254,7 +254,10 @@ class I18n { * @return void */ public static function cli_extract_messages($command_args) { - global $root_dir_path, $smarty_templates_dir; + global $root_dir_path; + + // Store list of generated POT files + $pot_files = array(); // List PHP files to parse $php_files = run_external_command( @@ -281,7 +284,7 @@ class I18n { ); if (!is_array($result) || $result[0] != 0) Log :: fatal(_("Fail to extract messages from PHP files using xgettext.")); - + $pot_files[] = self :: $root_path."/php-messages.pot"; // List JS files to parse $js_files = run_external_command( @@ -308,18 +311,22 @@ class I18n { ); if (!is_array($result) || $result[0] != 0) Log :: fatal(_("Fail to extract messages from JS files using xgettext.")); + $pot_files[] = self :: $root_path."/js-messages.pot"; - // Extract messages from templates files using tsmarty2c.php - $result = run_external_command( - array ( - "$root_dir_path/vendor/bin/tsmarty2c.php", - "-o", self :: $root_path."/templates-messages.pot", - $smarty_templates_dir, - ) - ); - if (!is_array($result) || $result[0] != 0) - Log :: fatal( - _("Fail to extract messages from template files using tsmarty2c.php script.")); + if (Tpl :: initialized()) { + // Extract messages from templates files using tsmarty2c.php + $result = run_external_command( + array ( + "$root_dir_path/vendor/bin/tsmarty2c.php", + "-o", self :: $root_path."/templates-messages.pot", + Tpl :: templates_directory(), + ) + ); + if (!is_array($result) || $result[0] != 0) + Log :: fatal( + _("Fail to extract messages from template files using tsmarty2c.php script.")); + $pot_files[] = self :: $root_path."/templates-messages.pot"; + } $fd = fopen(self :: $root_path."/headers.pot", 'w'); $headers = array( @@ -335,14 +342,14 @@ class I18n { fclose($fd); // Merge previous results in messages.pot file using msgcat - $result = run_external_command(array( - 'msgcat', - self :: $root_path."/headers.pot", - self :: $root_path."/php-messages.pot", - self :: $root_path."/js-messages.pot", - self :: $root_path."/templates-messages.pot", - "-t", "utf-8", "--use-first", - "-o", self :: $root_path."/messages.pot", + $result = run_external_command(array_merge( + array( + 'msgcat', + "-t", "utf-8", "--use-first", + "-o", self :: $root_path."/messages.pot", + self :: $root_path."/headers.pot", + ), + $pot_files )); if (!is_array($result) || $result[0] != 0) Log :: fatal(_("Fail to merge messages using msgcat.")); @@ -356,7 +363,7 @@ class I18n { * @return bool */ public static function cli_update_messages($command_args) { - global $root_dir_path, $smarty_templates_dir; + global $root_dir_path; $compendium_args = array(); foreach ($command_args as $path) { @@ -444,7 +451,7 @@ class I18n { * @return bool */ public static function cli_compile_messages($command_args) { - global $root_dir_path, $smarty_templates_dir; + global $root_dir_path; if ($dh = opendir(self :: $root_path)) { $error = False; diff --git a/src/Tpl.php b/src/Tpl.php new file mode 100644 index 0000000..e750fa2 --- /dev/null +++ b/src/Tpl.php @@ -0,0 +1,362 @@ + + */ + private static array $css_files = array(); + + /** + * JavaScript files to load in next displayed page + * @var array + */ + private static array $js_files = array(); + + /** + * Initialization + * @param string $templates_dir Smarty templates directory path + * @param string $templates_c_dir Smarty cache templates directory path + * @param bool $debug_ajax Enable/disable AJAX returned data debugging in logs + * (optional, default: false) + * @return void + */ + public static function init($templates_dir, $templates_c_dir, $debug_ajax=false) { + // Check templates/templates_c directories + 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); + 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|null $functions List of function names granted in IF clauses + * @param array|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> $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> $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; + } +} diff --git a/src/Url.php b/src/Url.php index a5bfdbc..2b56413 100644 --- a/src/Url.php +++ b/src/Url.php @@ -5,6 +5,7 @@ namespace EesyPHP; use EesyPHP\Log; use EesyPHP\SentrySpan; use EesyPHP\SentryTransaction; +use EesyPHP\Tpl; use EesyPHP\UrlRequest; use function EesyPHP\format_callable; @@ -141,7 +142,6 @@ class Url { * @return void **/ public static function error_page($request=null, $error_code=null) { - global $smarty; $http_errors = array( 400 => array( 'pagetitle' => _("Bad request"), @@ -170,10 +170,12 @@ class Url { ); http_response_code($error_code); - if (isset($smarty) && $smarty) - $smarty -> assign('message', $error['message']); - display_template('error_page.tpl', $error['pagetitle']); - exit(); + if (Tpl :: initialized()) { + Tpl :: assign('message', $error['message']); + Tpl :: display('error_page.tpl', $error['pagetitle']); + exit(); + } + die($error['message']); } /** @@ -391,8 +393,6 @@ class Url { * @return void **/ public static function handle_request($default_url=null) { - global $smarty; - $sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request'); $request = self :: get_request($default_url); @@ -406,8 +406,8 @@ class Url { if ($request -> api_mode) self :: $_api_mode = true; - if (isset($smarty) && $smarty) - $smarty -> assign('request', $request); + if (Tpl :: initialized()) + Tpl :: assign('request', $request); // Check authentication (if need) if($request -> authenticated)