Move templating stuff in EesyPHP namespace

This commit is contained in:
Benjamin Renard 2023-01-31 00:30:04 +01:00
parent d422afa66e
commit 1fff8f0af4
9 changed files with 564 additions and 419 deletions

View file

@ -74,7 +74,7 @@ $status_list = array (
); );
require_once('cli.php'); require_once('cli.php');
require_once('smarty.php'); require_once('templates.php');
Url::init(isset($public_root_url)?$public_root_url:null); Url::init(isset($public_root_url)?$public_root_url:null);
require_once('url-helpers.php'); require_once('url-helpers.php');
require_once('db.php'); require_once('db.php');

View file

@ -3,6 +3,8 @@
use EesyPHP\Check; use EesyPHP\Check;
use EesyPHP\Log; use EesyPHP\Log;
use EesyPHP\Session; use EesyPHP\Session;
use EesyPHP\Tpl;
use EesyPHP\Url;
/* /*
* Check values helpers * 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) { function check_ajax_request($session_key=null) {
global $ajax, $debug_ajax; global $ajax, $debug_ajax;
$ajax = true; Url :: api_mode(true);
if (Session :: check_key($session_key)) 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)); Log :: debug("Ajax Request : ".vardump($_REQUEST));
} }

View file

@ -1,329 +0,0 @@
<?php
use EesyPHP\Log;
use EesyPHP\SentrySpan;
use EesyPHP\SentryTransaction;
use EesyPHP\Url;
if (php_sapi_name() == "cli")
return true;
$smarty = new Smarty();
/*
* Configure Smarty regarding the installed version
*/
if (method_exists($smarty,'register_function')) {
$_smarty_version = 2;
function smarty_enable_security_mode($functions=array(), $modifiers=array()) {
global $smarty;
// Enable security
$smarty -> 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 "<span class='badge badge-$class'>";
echo (
array_key_exists($params['item']['status'], $status_list)?
$status_list[$params['item']['status']]:
"Inconnu (".$params['item']['status'].")"
);
echo "</span>";
}
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 "<a href='".$params['url']."'>".$params['text']."</a>";
}
if ($params['order']==$params['search']['order']) {
echo (
' <i class="fa fa-sort-'.
(strtolower($params['search']['order_direction'])=='asc'?'up':'down').
'" aria-hidden="true"></i>');
}
}
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

107
includes/templates.php Normal file
View file

@ -0,0 +1,107 @@
<?php
use EesyPHP\Hook;
use EesyPHP\Log;
use EesyPHP\Tpl;
if (php_sapi_name() == "cli")
return true;
Tpl :: init(
isset($smarty_templates_dir)?$smarty_templates_dir:null,
isset($smarty_templates_c_dir)?$smarty_templates_c_dir:null,
isset($debug_ajax)?$debug_ajax:false
);
// Enable Smarty security
Tpl :: 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
Tpl :: assign('public_root_url', isset($public_root_url)?$public_root_url:'/');
Tpl :: assign('main_pagetitle', isset($main_pagetitle)?$main_pagetitle:null);
Tpl :: assign('session_key', $_SESSION['session_key']);
// Handle CSS & JS files included
if (isset($included_css_files) && is_array($included_css_files))
Tpl :: add_css_file($included_css_files);
if (isset($included_js_files) && is_array($included_js_files))
Tpl :: add_css_file($included_js_files);
function define_common_template_variables($event) {
global $status_list, $admin, $webstats_js_code;
Tpl :: assign(
'status_list',
isset($status_list) && is_array($status_list)?
$status_list:array()
);
Tpl :: assign('admin', isset($admin) && $admin);
Tpl :: assign(
'webstats_js_code',
isset($webstats_js_code)?$webstats_js_code:null);
}
Hook :: register('before_displaying_template', 'define_common_template_variables');
// 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 "<span class='badge badge-$class'>";
echo (
array_key_exists($params['item']['status'], $status_list)?
$status_list[$params['item']['status']]:
"Inconnu (".$params['item']['status'].")"
);
echo "</span>";
}
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 "<a href='".$params['url']."'>".$params['text']."</a>";
}
if ($params['order']==$params['search']['order']) {
echo (
' <i class="fa fa-sort-'.
(strtolower($params['search']['order_direction'])=='asc'?'up':'down').
'" aria-hidden="true"></i>');
}
}
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

View file

@ -2,6 +2,7 @@
use EesyPHP\Check; use EesyPHP\Check;
use EesyPHP\Log; use EesyPHP\Log;
use EesyPHP\Tpl;
function get_item_from_url($id, $fatal=false) { function get_item_from_url($id, $fatal=false) {
if (!Check :: id($id)) if (!Check :: id($id))
@ -12,7 +13,7 @@ function get_item_from_url($id, $fatal=false) {
$error = sprintf(_("Item #% s not found."), $id); $error = sprintf(_("Item #% s not found."), $id);
if ($fatal) if ($fatal)
Log :: fatal($error); Log :: fatal($error);
add_error($error); Tpl :: add_error($error);
return false; return false;
} }
return $item; return $item;

View file

@ -2,6 +2,7 @@
use EesyPHP\Check; use EesyPHP\Check;
use EesyPHP\Log; use EesyPHP\Log;
use EesyPHP\Tpl;
use EesyPHP\Url; use EesyPHP\Url;
use function EesyPHP\vardump; use function EesyPHP\vardump;
@ -10,12 +11,12 @@ if (php_sapi_name() == "cli")
return true; return true;
function handle_homepage($request) { function handle_homepage($request) {
display_template("homepage.tpl", _("Hello world !")); Tpl :: display("homepage.tpl", _("Hello world !"));
} }
Url :: add_url_handler('#^$#', 'handle_homepage'); Url :: add_url_handler('#^$#', 'handle_homepage');
function handle_search($request) { function handle_search($request) {
global $smarty, $status_list; global $status_list;
// Manage params // Manage params
if( if(
@ -39,7 +40,7 @@ function handle_search($request) {
if (check_status($_REQUEST['status']) || $_REQUEST['status'] == 'all') if (check_status($_REQUEST['status']) || $_REQUEST['status'] == 'all')
$_SESSION['search']['status'] = $_REQUEST['status']; $_SESSION['search']['status'] = $_REQUEST['status'];
else else
$smarty -> assign('status_error', true); Tpl :: assign('status_error', true);
} }
if (isset($_REQUEST['pattern'])) { if (isset($_REQUEST['pattern'])) {
@ -48,7 +49,7 @@ function handle_search($request) {
else if (Check :: search_pattern($_REQUEST['pattern'])) else if (Check :: search_pattern($_REQUEST['pattern']))
$_SESSION['search']['pattern'] = $_REQUEST['pattern']; $_SESSION['search']['pattern'] = $_REQUEST['pattern'];
else else
$smarty -> assign('pattern_error', true); Tpl :: assign('pattern_error', true);
} }
// Order // Order
@ -91,23 +92,23 @@ function handle_search($request) {
Log :: debug('Search params : '.vardump($_SESSION['search'])); Log :: debug('Search params : '.vardump($_SESSION['search']));
$result = search_items($_SESSION['search']); $result = search_items($_SESSION['search']);
if (!is_array($result)) if (!is_array($result))
fatal_error( Tpl :: fatal_error(
_("An error occurred while listing the items. ". _("An error occurred while listing the items. ".
"If the problem persists, please contact support.") "If the problem persists, please contact support.")
); );
$smarty -> assign('result', $result); Tpl :: assign('result', $result);
$smarty -> assign('search', $_SESSION['search']); Tpl :: assign('search', $_SESSION['search']);
$smarty -> assign('nbs_by_page', $nbs_by_page); Tpl :: assign('nbs_by_page', $nbs_by_page);
$smarty -> assign('status_list', $status_list); Tpl :: assign('status_list', $status_list);
add_js_file(array( Tpl :: add_js_file(array(
'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js', 'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js',
'js/myconfirm.js', 'js/myconfirm.js',
'js/search.js' 'js/search.js'
)); ));
display_template("search.tpl", _("Search")); Tpl :: display("search.tpl", _("Search"));
} }
Url :: add_url_handler('|^item/?$|', 'handle_search'); Url :: add_url_handler('|^item/?$|', 'handle_search');
@ -116,21 +117,19 @@ Url :: add_url_handler('|^item/?$|', 'handle_search');
*/ */
function handle_show($request) { function handle_show($request) {
global $smarty;
$item = get_item_from_url($request -> id); $item = get_item_from_url($request -> id);
if (!$item) if (!$item)
Url :: error_404(); Url :: error_404();
$smarty->assign('item', $item); Tpl :: assign('item', $item);
// Dialog // Dialog
add_js_file(array( Tpl :: add_js_file(array(
'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js', 'lib/bootstrap4dialog/dist/js/bootstrap4dialog.min.js',
'js/myconfirm.js', 'js/myconfirm.js',
)); ));
display_template( Tpl :: display(
"show.tpl", _("Element %s"), "show.tpl", _("Element %s"),
(is_array($item)?$item['name']:"#".$request -> id) (is_array($item)?$item['name']:"#".$request -> id)
); );
@ -138,42 +137,42 @@ function handle_show($request) {
Url :: add_url_handler('|^item/(?P<id>[0-9]+)$|', 'handle_show'); Url :: add_url_handler('|^item/(?P<id>[0-9]+)$|', 'handle_show');
function handle_create($request) { function handle_create($request) {
global $smarty, $status_list; global $status_list;
$info = array(); $info = array();
$field_errors = handle_item_post_data($info); $field_errors = handle_item_post_data($info);
if (isset($_POST['submit']) && empty($field_errors)) { if (isset($_POST['submit']) && empty($field_errors)) {
$item = add_item($info); $item = add_item($info);
if (is_array($item)) { 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']); Url :: redirect('item/'.$item['id']);
} }
else { 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('Validated data : '.vardump($info));
Log :: debug('Fields errors : '.vardump($field_errors)); Log :: debug('Fields errors : '.vardump($field_errors));
if (isset($_POST['submit']) && !empty($field_errors)) if (isset($_POST['submit']) && !empty($field_errors))
add_error( Tpl :: add_error(
_("There are errors preventing this item from being saved. ". _("There are errors preventing this item from being saved. ".
"Please correct them before attempting to add this item.")); "Please correct them before attempting to add this item."));
$smarty->assign('submited', isset($_POST['submit'])); Tpl :: assign('submited', isset($_POST['submit']));
$smarty->assign('info', $info); Tpl :: assign('info', $info);
$smarty->assign('field_errors', $field_errors); Tpl :: assign('field_errors', $field_errors);
$smarty->assign('status_list', $status_list); Tpl :: assign('status_list', $status_list);
display_template("form.tpl", _("New")); Tpl :: display("form.tpl", _("New"));
} }
Url :: add_url_handler('|^item/new$|', 'handle_create'); Url :: add_url_handler('|^item/new$|', 'handle_create');
function handle_modify($request) { function handle_modify($request) {
global $smarty, $status_list; global $status_list;
$item = get_item_from_url($request -> id); $item = get_item_from_url($request -> id);
if(is_array($item)) { if(is_array($item)) {
if (!can_modify($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']); Url :: redirect('item/'.$item['id']);
} }
$info = array(); $info = array();
@ -186,34 +185,34 @@ function handle_modify($request) {
} }
Log :: debug('Changes : '.vardump($changes)); Log :: debug('Changes : '.vardump($changes));
if (empty($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']); Url :: redirect('item/'.$item['id']);
} }
else if (update_item($item['id'], $changes) === true) { 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']); Url :: redirect('item/'.$item['id']);
} }
else { 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('Validated data : '.vardump($info));
Log :: debug('Fields errors : '.vardump($field_errors)); 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)) if (isset($_POST['submit']) && !empty($field_errors))
add_error( Tpl :: add_error(
_("There are errors preventing this item from being saved. ". _("There are errors preventing this item from being saved. ".
"Please correct them before attempting to save your changes.")); "Please correct them before attempting to save your changes."));
$smarty->assign('info', (!empty($info)?$info:$item)); Tpl :: assign('info', (!empty($info)?$info:$item));
$smarty->assign('item_id', $item['id']); Tpl :: assign('item_id', $item['id']);
$smarty->assign('field_errors', $field_errors); Tpl :: assign('field_errors', $field_errors);
$smarty -> assign('status_list', $status_list); Tpl :: assign('status_list', $status_list);
} }
else { else {
Url :: error_404(); Url :: error_404();
} }
display_template( Tpl :: display(
"form.tpl", _("Element %s: Modification"), "form.tpl", _("Element %s: Modification"),
(is_array($item)?$item['name']:"#".$request -> id) (is_array($item)?$item['name']:"#".$request -> id)
); );
@ -221,44 +220,40 @@ function handle_modify($request) {
Url :: add_url_handler('|^item/(?P<id>[0-9]+)/modify$|', 'handle_modify'); Url :: add_url_handler('|^item/(?P<id>[0-9]+)/modify$|', 'handle_modify');
function handle_archive($request) { function handle_archive($request) {
global $smarty;
$item = get_item_from_url($request -> id); $item = get_item_from_url($request -> id);
if(!is_array($item)) { if(!is_array($item)) {
add_error(_("Item #% s not found."), $request -> id); Tpl :: add_error(_("Item #% s not found."), $request -> id);
Url :: redirect('item'); Url :: redirect('item');
} }
elseif ($item['status'] == 'archived') { elseif ($item['status'] == 'archived') {
add_message(_("This item is already archived.")); Tpl :: add_message(_("This item is already archived."));
} }
else if (!can_archive($item)) { 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) { 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 { 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 :: redirect('item/'.$item['id']);
} }
Url :: add_url_handler('|^item/(?P<id>[0-9]+)/archive$|', 'handle_archive'); Url :: add_url_handler('|^item/(?P<id>[0-9]+)/archive$|', 'handle_archive');
function handle_delete($request) { function handle_delete($request) {
global $smarty;
$item = get_item_from_url($request -> id); $item = get_item_from_url($request -> id);
if(!is_array($item)) { 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)) { 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) { 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 { 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/'.$item['id']);
} }
Url :: redirect('item'); Url :: redirect('item');

View file

@ -4,9 +4,9 @@ namespace EesyPHP;
use EesyPHP\Cli; use EesyPHP\Cli;
use EesyPHP\Log; use EesyPHP\Log;
use EesyPHP\Tpl;
use Locale; use Locale;
use add_js_file;
class I18n { class I18n {
// Gettext text domain // Gettext text domain
@ -109,9 +109,9 @@ class I18n {
php_sapi_name() != "cli" php_sapi_name() != "cli"
&& isset($root_dir_path) && $root_dir_path && isset($root_dir_path) && $root_dir_path
&& is_file("$root_dir_path/public_html/$js_translation_file") && 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') { if (php_sapi_name() == 'cli') {
@ -254,7 +254,10 @@ class I18n {
* @return void * @return void
*/ */
public static function cli_extract_messages($command_args) { 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 // List PHP files to parse
$php_files = run_external_command( $php_files = run_external_command(
@ -281,7 +284,7 @@ class I18n {
); );
if (!is_array($result) || $result[0] != 0) if (!is_array($result) || $result[0] != 0)
Log :: fatal(_("Fail to extract messages from PHP files using xgettext.")); Log :: fatal(_("Fail to extract messages from PHP files using xgettext."));
$pot_files[] = self :: $root_path."/php-messages.pot";
// List JS files to parse // List JS files to parse
$js_files = run_external_command( $js_files = run_external_command(
@ -308,18 +311,22 @@ class I18n {
); );
if (!is_array($result) || $result[0] != 0) if (!is_array($result) || $result[0] != 0)
Log :: fatal(_("Fail to extract messages from JS files using xgettext.")); Log :: fatal(_("Fail to extract messages from JS files using xgettext."));
$pot_files[] = self :: $root_path."/js-messages.pot";
if (Tpl :: initialized()) {
// Extract messages from templates files using tsmarty2c.php // Extract messages from templates files using tsmarty2c.php
$result = run_external_command( $result = run_external_command(
array ( array (
"$root_dir_path/vendor/bin/tsmarty2c.php", "$root_dir_path/vendor/bin/tsmarty2c.php",
"-o", self :: $root_path."/templates-messages.pot", "-o", self :: $root_path."/templates-messages.pot",
$smarty_templates_dir, Tpl :: templates_directory(),
) )
); );
if (!is_array($result) || $result[0] != 0) if (!is_array($result) || $result[0] != 0)
Log :: fatal( Log :: fatal(
_("Fail to extract messages from template files using tsmarty2c.php script.")); _("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'); $fd = fopen(self :: $root_path."/headers.pot", 'w');
$headers = array( $headers = array(
@ -335,14 +342,14 @@ class I18n {
fclose($fd); fclose($fd);
// Merge previous results in messages.pot file using msgcat // Merge previous results in messages.pot file using msgcat
$result = run_external_command(array( $result = run_external_command(array_merge(
array(
'msgcat', '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", "-t", "utf-8", "--use-first",
"-o", self :: $root_path."/messages.pot", "-o", self :: $root_path."/messages.pot",
self :: $root_path."/headers.pot",
),
$pot_files
)); ));
if (!is_array($result) || $result[0] != 0) if (!is_array($result) || $result[0] != 0)
Log :: fatal(_("Fail to merge messages using msgcat.")); Log :: fatal(_("Fail to merge messages using msgcat."));
@ -356,7 +363,7 @@ class I18n {
* @return bool * @return bool
*/ */
public static function cli_update_messages($command_args) { public static function cli_update_messages($command_args) {
global $root_dir_path, $smarty_templates_dir; global $root_dir_path;
$compendium_args = array(); $compendium_args = array();
foreach ($command_args as $path) { foreach ($command_args as $path) {
@ -444,7 +451,7 @@ class I18n {
* @return bool * @return bool
*/ */
public static function cli_compile_messages($command_args) { 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)) { if ($dh = opendir(self :: $root_path)) {
$error = False; $error = False;

362
src/Tpl.php Normal file
View file

@ -0,0 +1,362 @@
<?php
namespace EesyPHP;
use EesyPHP\Hook;
use EesyPHP\Log;
use EesyPHP\SentrySpan;
use EesyPHP\SentryTransaction;
use EesyPHP\Url;
use function EesyPHP\vardump;
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
* @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<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;
}
}

View file

@ -5,6 +5,7 @@ namespace EesyPHP;
use EesyPHP\Log; use EesyPHP\Log;
use EesyPHP\SentrySpan; use EesyPHP\SentrySpan;
use EesyPHP\SentryTransaction; use EesyPHP\SentryTransaction;
use EesyPHP\Tpl;
use EesyPHP\UrlRequest; use EesyPHP\UrlRequest;
use function EesyPHP\format_callable; use function EesyPHP\format_callable;
@ -141,7 +142,6 @@ class Url {
* @return void * @return void
**/ **/
public static function error_page($request=null, $error_code=null) { public static function error_page($request=null, $error_code=null) {
global $smarty;
$http_errors = array( $http_errors = array(
400 => array( 400 => array(
'pagetitle' => _("Bad request"), 'pagetitle' => _("Bad request"),
@ -170,11 +170,13 @@ class Url {
); );
http_response_code($error_code); http_response_code($error_code);
if (isset($smarty) && $smarty) if (Tpl :: initialized()) {
$smarty -> assign('message', $error['message']); Tpl :: assign('message', $error['message']);
display_template('error_page.tpl', $error['pagetitle']); Tpl :: display('error_page.tpl', $error['pagetitle']);
exit(); exit();
} }
die($error['message']);
}
/** /**
* Error 404 handler * Error 404 handler
@ -391,8 +393,6 @@ class Url {
* @return void * @return void
**/ **/
public static function handle_request($default_url=null) { public static function handle_request($default_url=null) {
global $smarty;
$sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request'); $sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request');
$request = self :: get_request($default_url); $request = self :: get_request($default_url);
@ -406,8 +406,8 @@ class Url {
if ($request -> api_mode) if ($request -> api_mode)
self :: $_api_mode = true; self :: $_api_mode = true;
if (isset($smarty) && $smarty) if (Tpl :: initialized())
$smarty -> assign('request', $request); Tpl :: assign('request', $request);
// Check authentication (if need) // Check authentication (if need)
if($request -> authenticated) if($request -> authenticated)