Fix token metadata to be clearable outside tests
[civicrm-core.git] / CRM / Core / Smarty.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
19 * Fix for bug CRM-392. Not sure if this is the best fix or it will impact
20 * other similar PEAR packages. doubt it
21 */
22if (!class_exists('Smarty')) {
23 require_once 'Smarty/Smarty.class.php';
24}
25
26/**
27 *
28 */
29class CRM_Core_Smarty extends Smarty {
7da04cde 30 const
6a488035
TO
31 // use print.tpl and bypass the CMS. Civi prints a valid html file
32 PRINT_PAGE = 1,
da3c7979 33 // this and all the below bypasses the CMS html surrounding it and assumes we will embed this within other pages
6a488035
TO
34 PRINT_SNIPPET = 2,
35 // sends the generated html to the chosen pdf engine
36 PRINT_PDF = 3,
37 // this options also skips the enclosing form html and does not
38 // generate any of the hidden fields, most notably qfKey
39 // this is typically used in ajax scripts to embed form snippets based on user choices
40 PRINT_NOFORM = 4,
41 // this prints a complete form and also generates a qfKey, can we replace this with
42 // snippet = 2?? Does the constant _NOFFORM do anything?
43 PRINT_QFKEY = 5,
fc05b8da
CW
44 // Note: added in v 4.3 with the value '6'
45 // Value changed in 4.5 to 'json' for better readability
46 // @see CRM_Core_Page_AJAX::returnJsonResponse
47 PRINT_JSON = 'json';
6a488035
TO
48
49 /**
50 * We only need one instance of this object. So we use the singleton
51 * pattern and cache the instance in this variable
52 *
53 * @var object
6a488035
TO
54 */
55 static private $_singleton = NULL;
56
17f267d6 57 /**
e97c66ff 58 * Backup frames.
59 *
60 * A list of variables ot save temporarily in format (string $name => mixed $value).
61 *
62 * @var array
17f267d6 63 */
be2fb01f 64 private $backupFrames = [];
17f267d6 65
6a488035 66 /**
f9e31d7f 67 * Class constructor.
6a488035
TO
68 *
69 * @return CRM_Core_Smarty
6a488035 70 */
6ef04c72 71 public function __construct() {
6a488035
TO
72 parent::__construct();
73 }
74
2aa397bc 75 private function initialize() {
6a488035
TO
76 $config = CRM_Core_Config::singleton();
77
78 if (isset($config->customTemplateDir) && $config->customTemplateDir) {
be2fb01f 79 $this->template_dir = array_merge([$config->customTemplateDir],
6a488035
TO
80 $config->templateDir
81 );
82 }
83 else {
84 $this->template_dir = $config->templateDir;
85 }
635f0b86
TO
86 $this->compile_dir = CRM_Utils_File::addTrailingSlash(CRM_Utils_File::addTrailingSlash($config->templateCompileDir) . $this->getLocale());
87 CRM_Utils_File::createDir($this->compile_dir);
88 CRM_Utils_File::restrictAccess($this->compile_dir);
6a488035
TO
89
90 // check and ensure it is writable
91 // else we sometime suppress errors quietly and this results
92 // in blank emails etc
93 if (!is_writable($this->compile_dir)) {
94 echo "CiviCRM does not have permission to write temp files in {$this->compile_dir}, Exiting";
95 exit();
96 }
97
4d03ddb9 98 $this->use_sub_dirs = TRUE;
6a488035
TO
99
100 $customPluginsDir = NULL;
101 if (isset($config->customPHPPathDir)) {
e7483cbe
J
102 $customPluginsDir
103 = $config->customPHPPathDir . DIRECTORY_SEPARATOR .
353ffa53
TO
104 'CRM' . DIRECTORY_SEPARATOR .
105 'Core' . DIRECTORY_SEPARATOR .
106 'Smarty' . DIRECTORY_SEPARATOR .
107 'plugins' . DIRECTORY_SEPARATOR;
6a488035
TO
108 if (!file_exists($customPluginsDir)) {
109 $customPluginsDir = NULL;
110 }
111 }
112
39c2241c
TO
113 $pkgsDir = Civi::paths()->getVariable('civicrm.packages', 'path');
114 $smartyDir = $pkgsDir . DIRECTORY_SEPARATOR . 'Smarty' . DIRECTORY_SEPARATOR;
94dbed1f
TO
115 $pluginsDir = __DIR__ . DIRECTORY_SEPARATOR . 'Smarty' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR;
116
6a488035 117 if ($customPluginsDir) {
be2fb01f 118 $this->plugins_dir = [$customPluginsDir, $smartyDir . 'plugins', $pluginsDir];
6a488035
TO
119 }
120 else {
be2fb01f 121 $this->plugins_dir = [$smartyDir . 'plugins', $pluginsDir];
6a488035
TO
122 }
123
d938f496
LS
124 $this->compile_check = $this->isCheckSmartyIsCompiled();
125
6a488035
TO
126 // add the session and the config here
127 $session = CRM_Core_Session::singleton();
128
129 $this->assign_by_ref('config', $config);
130 $this->assign_by_ref('session', $session);
131
98466ff9 132 $tsLocale = CRM_Core_I18n::getLocale();
6a488035
TO
133 $this->assign('tsLocale', $tsLocale);
134
135 // CRM-7163 hack: we don’t display langSwitch on upgrades anyway
136 if (!CRM_Core_Config::isUpgradeMode()) {
921ed8ae 137 $this->assign('langSwitch', CRM_Core_I18n::uiLanguages());
6a488035
TO
138 }
139
be2fb01f 140 $this->register_function('crmURL', ['CRM_Utils_System', 'crmURL']);
d9aa1c6b 141 $this->load_filter('pre', 'resetExtScope');
abbf7b48
TO
142
143 $this->assign('crmPermissions', new CRM_Core_Smarty_Permissions());
66b33745
CW
144
145 if ($config->debug) {
146 $this->error_reporting = E_ALL;
147 }
6a488035
TO
148 }
149
150 /**
151 * Static instance provider.
152 *
153 * Method providing static instance of SmartTemplate, as
154 * in Singleton pattern.
01a5461d 155 *
156 * @return \CRM_Core_Smarty
6a488035 157 */
00be9182 158 public static function &singleton() {
6a488035 159 if (!isset(self::$_singleton)) {
481a74f4
TO
160 self::$_singleton = new CRM_Core_Smarty();
161 self::$_singleton->initialize();
6a488035
TO
162
163 self::registerStringResource();
164 }
165 return self::$_singleton;
166 }
167
168 /**
100fef9d 169 * Executes & returns or displays the template results
6a488035
TO
170 *
171 * @param string $resource_name
172 * @param string $cache_id
173 * @param string $compile_id
6a0b768e 174 * @param bool $display
77b97be7
EM
175 *
176 * @return bool|mixed|string
6a488035 177 */
00be9182 178 public function fetch($resource_name, $cache_id = NULL, $compile_id = NULL, $display = FALSE) {
481a74f4 179 if (preg_match('/^(\s+)?string:/', $resource_name)) {
7155133f
ND
180 $old_security = $this->security;
181 $this->security = TRUE;
182 }
183 $output = parent::fetch($resource_name, $cache_id, $compile_id, $display);
184 if (isset($old_security)) {
185 $this->security = $old_security;
186 }
187 return $output;
6a488035
TO
188 }
189
9b7526a8
TO
190 /**
191 * Fetch a template (while using certain variables)
192 *
193 * @param string $resource_name
6a0b768e
TO
194 * @param array $vars
195 * (string $name => mixed $value) variables to export to Smarty.
9b7526a8
TO
196 * @throws Exception
197 * @return bool|mixed|string
198 */
00be9182 199 public function fetchWith($resource_name, $vars) {
9b7526a8
TO
200 $this->pushScope($vars);
201 try {
202 $result = $this->fetch($resource_name);
0db6c3e1
TO
203 }
204 catch (Exception $e) {
9b7526a8
TO
205 // simulate try { ... } finally { ... }
206 $this->popScope();
207 throw $e;
208 }
209 $this->popScope();
210 return $result;
211 }
212
a0ee3941 213 /**
100fef9d 214 * @param string $name
a0ee3941
EM
215 * @param $value
216 */
00be9182 217 public function appendValue($name, $value) {
6a488035
TO
218 $currentValue = $this->get_template_vars($name);
219 if (!$currentValue) {
220 $this->assign($name, $value);
221 }
222 else {
223 if (strpos($currentValue, $value) === FALSE) {
224 $this->assign($name, $currentValue . $value);
225 }
226 }
227 }
228
00be9182 229 public function clearTemplateVars() {
6a488035
TO
230 foreach (array_keys($this->_tpl_vars) as $key) {
231 if ($key == 'config' || $key == 'session') {
232 continue;
233 }
234 unset($this->_tpl_vars[$key]);
235 }
236 }
237
00be9182 238 public static function registerStringResource() {
6a488035
TO
239 require_once 'CRM/Core/Smarty/resources/String.php';
240 civicrm_smarty_register_string_resource();
241 }
242
a0ee3941
EM
243 /**
244 * @param $path
245 */
00be9182 246 public function addTemplateDir($path) {
481a74f4
TO
247 if (is_array($this->template_dir)) {
248 array_unshift($this->template_dir, $path);
0db6c3e1
TO
249 }
250 else {
be2fb01f 251 $this->template_dir = [$path, $this->template_dir];
6a488035
TO
252 }
253
254 }
17f267d6
TO
255
256 /**
257 * Temporarily assign a list of variables.
258 *
0b882a86 259 * ```
17f267d6
TO
260 * $smarty->pushScope(array(
261 * 'first_name' => 'Alice',
262 * 'last_name' => 'roberts',
263 * ));
264 * $html = $smarty->fetch('view-contact.tpl');
265 * $smarty->popScope();
0b882a86 266 * ```
17f267d6 267 *
6a0b768e
TO
268 * @param array $vars
269 * (string $name => mixed $value).
17f267d6
TO
270 * @return CRM_Core_Smarty
271 * @see popScope
272 */
273 public function pushScope($vars) {
274 $oldVars = $this->get_template_vars();
be2fb01f 275 $backupFrame = [];
17f267d6 276 foreach ($vars as $key => $value) {
2e1f50d6 277 $backupFrame[$key] = $oldVars[$key] ?? NULL;
17f267d6
TO
278 }
279 $this->backupFrames[] = $backupFrame;
280
281 $this->assignAll($vars);
282
283 return $this;
284 }
285
286 /**
287 * Remove any values that were previously pushed.
288 *
289 * @return CRM_Core_Smarty
290 * @see pushScope
291 */
292 public function popScope() {
293 $this->assignAll(array_pop($this->backupFrames));
294 return $this;
295 }
296
297 /**
6a0b768e
TO
298 * @param array $vars
299 * (string $name => mixed $value).
17f267d6
TO
300 * @return CRM_Core_Smarty
301 */
302 public function assignAll($vars) {
303 foreach ($vars as $key => $value) {
304 $this->assign($key, $value);
305 }
306 return $this;
307 }
96025800 308
f2ac86d1 309 /**
310 * Get the locale for translation.
311 *
312 * @return string
313 */
635f0b86 314 private function getLocale() {
98466ff9 315 $tsLocale = CRM_Core_I18n::getLocale();
635f0b86
TO
316 if (!empty($tsLocale)) {
317 return $tsLocale;
318 }
319
320 $config = CRM_Core_Config::singleton();
321 if (!empty($config->lcMessages)) {
322 return $config->lcMessages;
323 }
324
325 return 'en_US';
326 }
327
d938f496
LS
328 /**
329 * Get the compile_check value.
330 *
331 * @return bool
332 */
333 private function isCheckSmartyIsCompiled() {
334 // check for define in civicrm.settings.php as FALSE, otherwise returns TRUE
335 return CRM_Utils_Constant::value('CIVICRM_TEMPLATE_COMPILE_CHECK', TRUE);
336 }
337
6a488035 338}