From 6410d7ef1ac90d567b03556ffac3f84034fb033f Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Sat, 23 Sep 2023 12:51:30 +1200 Subject: [PATCH] Add Smarty3 Compatibility helper --- CRM/Core/Smarty.php | 13 +- CRM/Core/SmartyCompatibility.php | 237 ++++++++++++++++++ .../CRM/common/civicrm.settings.php.template | 15 ++ 3 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 CRM/Core/SmartyCompatibility.php diff --git a/CRM/Core/Smarty.php b/CRM/Core/Smarty.php index b5c22c62df..aacd44ae4c 100644 --- a/CRM/Core/Smarty.php +++ b/CRM/Core/Smarty.php @@ -22,14 +22,10 @@ use Civi\Core\Event\SmartyErrorEvent; -if (!class_exists('Smarty')) { - require_once 'Smarty/Smarty.class.php'; -} - /** * */ -class CRM_Core_Smarty extends Smarty { +class CRM_Core_Smarty extends CRM_Core_SmartyCompatibility { const // use print.tpl and bypass the CMS. Civi prints a valid html file PRINT_PAGE = 1, @@ -141,7 +137,12 @@ class CRM_Core_Smarty extends Smarty { $this->assign('langSwitch', CRM_Core_I18n::uiLanguages()); } - if (CRM_Utils_Constant::value('CIVICRM_SMARTY_DEFAULT_ESCAPE')) { + if (CRM_Utils_Constant::value('CIVICRM_SMARTY_DEFAULT_ESCAPE') + && !CRM_Utils_Constant::value('CIVICRM_SMARTY3_AUTOLOAD_PATH')) { + // Currently DEFAULT escape does not work with Smarty3 + // dunno why - thought it would be the default with Smarty3 - but + // getting onto Smarty 3 is higher priority. + // The include below loads the v2 version which is why id doesn't work. // When default escape is enabled if the core escape is called before // any custom escaping is done the modifier_escape function is not // found, so require_once straight away. Note this was hit on the basic diff --git a/CRM/Core/SmartyCompatibility.php b/CRM/Core/SmartyCompatibility.php new file mode 100644 index 0000000000..25364d9055 --- /dev/null +++ b/CRM/Core/SmartyCompatibility.php @@ -0,0 +1,237 @@ +assign_var` to + * `$smarty->assignVar()` + * 2) if someone adds the Smarty3 package onto their site and + * defines CIVICRM_SMARTY3_AUTOLOAD_PATH then Smarty3 will load from that + * location. + * + * Note that experimenting with `CIVICRM_SMARTY3_AUTOLOAD_PATH` will not + * go well if extensions are installed that have not run civix upgrade + * somewhat recently (ie have the old version of the hook_civicrm_config + * with reference to `$template =& CRM_Core_Smarty::singleton();` + */ + +/** + * Fix for bug CRM-392. Not sure if this is the best fix or it will impact + * other similar PEAR packages. doubt it + */ +if (!class_exists('Smarty')) { + if (defined('CIVICRM_SMARTY3_AUTOLOAD_PATH')) { + // @todo - this is experimental but it allows someone to + // get Smarty3 to load instead of Smarty2 if set. + // It is likely the final Smarty3 solution will look + // different but this makes testing possible without re-inventing + // it each time we try... + require_once CIVICRM_SMARTY3_AUTOLOAD_PATH; + } + else { + require_once 'Smarty/Smarty.class.php'; + } +} + +/** + * + */ +class CRM_Core_SmartyCompatibility extends Smarty { + + public function loadFilter($type, $name) { + if (method_exists(get_parent_class(), 'load_filter')) { + parent::load_filter($type, $name); + return; + } + parent::loadFilter($type, $name); + } + + /** + * @deprecated + * + * @param string $type + * @param string $name + * + * @throws \SmartyException + */ + public function load_filter($type, $name) { + if (method_exists(get_parent_class(), 'load_filter')) { + parent::load_filter($type, $name); + return; + } + parent::loadFilter($type, $name); + } + + /** + * Registers modifier to be used in templates + * + * @deprecated + * + * @param string $modifier name of template modifier + * @param string $modifier_impl name of PHP function to register + */ + public function register_modifier($modifier, $modifier_impl) { + if (method_exists(get_parent_class(), 'register_modifier')) { + parent::register_modifier($modifier, $modifier_impl); + return; + } + parent::registerPlugin('modifier', $modifier, $modifier_impl); + } + + public function registerPlugin($type, $name, $callback, $cacheable = TRUE, $cache_attr = NULL) { + if (method_exists(get_parent_class(), 'registerPlugin')) { + parent::registerPlugin($type, $name, $callback, $cacheable = TRUE, $cache_attr = NULL); + return; + } + if ($type === 'modifier') { + parent::register_modifier($name, $callback); + } + } + + /** + * Registers a resource to fetch a template + * + * @param string $type name of resource + * @param array $functions array of functions to handle resource + */ + public function register_resource($type, $functions) { + if (method_exists(get_parent_class(), 'register_resource')) { + parent::register_resource($type, $functions); + return; + } + parent::registerResource($type, $functions); + } + + /** + * Registers custom function to be used in templates + * + * @param string $function the name of the template function + * @param string $function_impl the name of the PHP function to register + * @param bool $cacheable + * @param null $cache_attrs + * + * @throws \SmartyException + */ + public function register_function($function, $function_impl, $cacheable = TRUE, $cache_attrs = NULL) { + if (method_exists(get_parent_class(), 'register_function')) { + parent::register_function($function, $function_impl, $cacheable = TRUE, $cache_attrs = NULL); + return; + } + parent::registerPlugin('function', $function, $function, $cacheable, $cache_attrs); + } + + /** + * Returns an array containing template variables + * + * @param string $name + * + * @return array + */ + public function &get_template_vars($name = NULL) { + if (method_exists(get_parent_class(), 'get_template_vars')) { + return parent::get_template_vars($name); + } + $var = parent::getTemplateVars($name); + return $var; + } + + /** + * Returns a single or all template variables + * + * @api Smarty::getTemplateVars() + * @link http://www.smarty.net/docs/en/api.get.template.vars.tpl + * + * @param string $varName variable name or NULL + * @param \Smarty_Internal_Data|\Smarty_Internal_Template|\Smarty $_ptr optional pointer to data object + * @param bool $searchParents include parent templates? + * + * @return mixed variable value or or array of variables + */ + public function getTemplateVars($varName = NULL, Smarty_Internal_Data $_ptr = NULL, $searchParents = TRUE) { + if (method_exists(get_parent_class(), 'getTemplateVars')) { + return parent::getTemplateVars($varName . $_ptr, $searchParents); + } + return parent::get_template_vars($varName); + } + + /** + * Generally Civi mis-uses this for perceived php4 conformance, avoid. + * + * @deprecated + * @param string $tpl_var + * @param mixed $value + * + * @return mixed|null|void + */ + public function assign_by_ref($tpl_var, &$value) { + if (method_exists(get_parent_class(), 'assign_by_ref')) { + parent::assign_by_ref($tpl_var, $value); + return; + } + return parent::assignByRef($tpl_var, $value); + } + + /** + * Generally Civi mis-uses this for perceived php4 conformance, avoid. + * + * @deprecated + * @param string $tpl_var + * + * @return mixed|null|void + */ + public function clear_assign($tpl_var) { + if (method_exists(get_parent_class(), 'clear_assign')) { + parent::clear_assign($tpl_var); + return; + } + return parent::clearAssign($tpl_var); + } + + /** + * Checks whether requested template exists. + * + * @param string $tpl_file + * + * @return bool + * @throws \SmartyException + */ + public function template_exists($tpl_file) { + if (method_exists(get_parent_class(), 'template_exists')) { + return parent::template_exists($tpl_file); + } + return parent::templateExists($tpl_file); + } + + /** + * Check if a template resource exists + * + * @param string $resource_name template name + * + * @return bool status + * @throws \SmartyException + */ + public function templateExists($resource_name) { + if (method_exists(get_parent_class(), 'templateExists')) { + return parent::templateExists($resource_name); + } + return parent::template_exists($resource_name); + } + +} diff --git a/templates/CRM/common/civicrm.settings.php.template b/templates/CRM/common/civicrm.settings.php.template index b2dcf4056b..17dc2a9c4d 100644 --- a/templates/CRM/common/civicrm.settings.php.template +++ b/templates/CRM/common/civicrm.settings.php.template @@ -201,9 +201,24 @@ if (!defined('CIVICRM_TEMPLATE_COMPILEDIR')) { // define('CIVICRM_TEMPLATE_COMPILE_CHECK', FALSE); //} +/** + * Specify a Smarty 3 autoload file. + * + * If you wish to experiment with Smarty3 you can do so by installing it + * to a directory using composer (composer require smarty/smarty:^3) + * and then defining the path to that + * autoload. file. + */ +//if (!defined('CIVICRM_SMARTY3_AUTOLOAD_PATH')) { +// define('CIVICRM_SMARTY3_AUTOLOAD_PATH', '/my/path/vendor/autoload.php'); +//} + /** * Smarty escape on output. * + * This does not currently work if you have a Smarty 3 path defined + * (see above). + * * This tells smarty to pass all variables through the escape function * unless they are piped to smarty:nodefaults (eg. {$myScript|smarty:nodefaults} * At this stage it should only be enabled on development sites. -- 2.25.1