Merge pull request #6068 from eileenmcnaughton/4.6
[civicrm-core.git] / CRM / Core / Smarty.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2015
32 * $Id$
33 *
34 */
35
36 /**
37 * Fix for bug CRM-392. Not sure if this is the best fix or it will impact
38 * other similar PEAR packages. doubt it
39 */
40 if (!class_exists('Smarty')) {
41 require_once 'Smarty/Smarty.class.php';
42 }
43
44 /**
45 *
46 */
47 class CRM_Core_Smarty extends Smarty {
48 const
49 // use print.tpl and bypass the CMS. Civi prints a valid html file
50 PRINT_PAGE = 1,
51 // this and all the below bypasses the CMS html surrounding it and assumes we will embed this within other pages
52 PRINT_SNIPPET = 2,
53 // sends the generated html to the chosen pdf engine
54 PRINT_PDF = 3,
55 // this options also skips the enclosing form html and does not
56 // generate any of the hidden fields, most notably qfKey
57 // this is typically used in ajax scripts to embed form snippets based on user choices
58 PRINT_NOFORM = 4,
59 // this prints a complete form and also generates a qfKey, can we replace this with
60 // snippet = 2?? Does the constant _NOFFORM do anything?
61 PRINT_QFKEY = 5,
62 // Note: added in v 4.3 with the value '6'
63 // Value changed in 4.5 to 'json' for better readability
64 // @see CRM_Core_Page_AJAX::returnJsonResponse
65 PRINT_JSON = 'json';
66
67 /**
68 * We only need one instance of this object. So we use the singleton
69 * pattern and cache the instance in this variable
70 *
71 * @var object
72 */
73 static private $_singleton = NULL;
74
75 /**
76 * @var array (string $name => mixed $value) a list of variables ot save temporarily
77 */
78 private $backupFrames = array();
79
80 /**
81 * Class constructor.
82 *
83 * @return CRM_Core_Smarty
84 */
85 private function __construct() {
86 parent::__construct();
87 }
88
89 private function initialize() {
90 $config = CRM_Core_Config::singleton();
91
92 if (isset($config->customTemplateDir) && $config->customTemplateDir) {
93 $this->template_dir = array_merge(array($config->customTemplateDir),
94 $config->templateDir
95 );
96 }
97 else {
98 $this->template_dir = $config->templateDir;
99 }
100 $this->compile_dir = $config->templateCompileDir;
101
102 // check and ensure it is writable
103 // else we sometime suppress errors quietly and this results
104 // in blank emails etc
105 if (!is_writable($this->compile_dir)) {
106 echo "CiviCRM does not have permission to write temp files in {$this->compile_dir}, Exiting";
107 exit();
108 }
109
110 //Check for safe mode CRM-2207
111 if (ini_get('safe_mode')) {
112 $this->use_sub_dirs = FALSE;
113 }
114 else {
115 $this->use_sub_dirs = TRUE;
116 }
117
118 $customPluginsDir = NULL;
119 if (isset($config->customPHPPathDir)) {
120 $customPluginsDir
121 = $config->customPHPPathDir . DIRECTORY_SEPARATOR .
122 'CRM' . DIRECTORY_SEPARATOR .
123 'Core' . DIRECTORY_SEPARATOR .
124 'Smarty' . DIRECTORY_SEPARATOR .
125 'plugins' . DIRECTORY_SEPARATOR;
126 if (!file_exists($customPluginsDir)) {
127 $customPluginsDir = NULL;
128 }
129 }
130
131 if ($customPluginsDir) {
132 $this->plugins_dir = array($customPluginsDir, $config->smartyDir . 'plugins', $config->pluginsDir);
133 }
134 else {
135 $this->plugins_dir = array($config->smartyDir . 'plugins', $config->pluginsDir);
136 }
137
138 // add the session and the config here
139 $session = CRM_Core_Session::singleton();
140
141 $this->assign_by_ref('config', $config);
142 $this->assign_by_ref('session', $session);
143
144 // check default editor and assign to template
145 $defaultWysiwygEditor = $session->get('defaultWysiwygEditor');
146 if (!$defaultWysiwygEditor && !CRM_Core_Config::isUpgradeMode()) {
147 $defaultWysiwygEditor = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
148 'editor_id'
149 );
150 // For logged-in users, store it in session to reduce db calls
151 if ($session->get('userID')) {
152 $session->set('defaultWysiwygEditor', $defaultWysiwygEditor);
153 }
154 }
155
156 $this->assign('defaultWysiwygEditor', $defaultWysiwygEditor);
157
158 global $tsLocale;
159 $this->assign('tsLocale', $tsLocale);
160
161 // CRM-7163 hack: we don’t display langSwitch on upgrades anyway
162 if (!CRM_Core_Config::isUpgradeMode()) {
163 $this->assign('langSwitch', CRM_Core_I18n::languages(TRUE));
164 }
165
166 $this->register_function('crmURL', array('CRM_Utils_System', 'crmURL'));
167 $this->load_filter('pre', 'resetExtScope');
168
169 $this->assign('crmPermissions', new CRM_Core_Smarty_Permissions());
170 }
171
172 /**
173 * Static instance provider.
174 *
175 * Method providing static instance of SmartTemplate, as
176 * in Singleton pattern.
177 */
178 public static function &singleton() {
179 if (!isset(self::$_singleton)) {
180 self::$_singleton = new CRM_Core_Smarty();
181 self::$_singleton->initialize();
182
183 self::registerStringResource();
184 }
185 return self::$_singleton;
186 }
187
188 /**
189 * Executes & returns or displays the template results
190 *
191 * @param string $resource_name
192 * @param string $cache_id
193 * @param string $compile_id
194 * @param bool $display
195 *
196 * @return bool|mixed|string
197 */
198 public function fetch($resource_name, $cache_id = NULL, $compile_id = NULL, $display = FALSE) {
199 if (preg_match('/^(\s+)?string:/', $resource_name)) {
200 $old_security = $this->security;
201 $this->security = TRUE;
202 }
203 $output = parent::fetch($resource_name, $cache_id, $compile_id, $display);
204 if (isset($old_security)) {
205 $this->security = $old_security;
206 }
207 return $output;
208 }
209
210 /**
211 * Fetch a template (while using certain variables)
212 *
213 * @param string $resource_name
214 * @param array $vars
215 * (string $name => mixed $value) variables to export to Smarty.
216 * @throws Exception
217 * @return bool|mixed|string
218 */
219 public function fetchWith($resource_name, $vars) {
220 $this->pushScope($vars);
221 try {
222 $result = $this->fetch($resource_name);
223 }
224 catch (Exception $e) {
225 // simulate try { ... } finally { ... }
226 $this->popScope();
227 throw $e;
228 }
229 $this->popScope();
230 return $result;
231 }
232
233 /**
234 * @param string $name
235 * @param $value
236 */
237 public function appendValue($name, $value) {
238 $currentValue = $this->get_template_vars($name);
239 if (!$currentValue) {
240 $this->assign($name, $value);
241 }
242 else {
243 if (strpos($currentValue, $value) === FALSE) {
244 $this->assign($name, $currentValue . $value);
245 }
246 }
247 }
248
249 public function clearTemplateVars() {
250 foreach (array_keys($this->_tpl_vars) as $key) {
251 if ($key == 'config' || $key == 'session') {
252 continue;
253 }
254 unset($this->_tpl_vars[$key]);
255 }
256 }
257
258 public static function registerStringResource() {
259 require_once 'CRM/Core/Smarty/resources/String.php';
260 civicrm_smarty_register_string_resource();
261 }
262
263 /**
264 * @param $path
265 */
266 public function addTemplateDir($path) {
267 if (is_array($this->template_dir)) {
268 array_unshift($this->template_dir, $path);
269 }
270 else {
271 $this->template_dir = array($path, $this->template_dir);
272 }
273
274 }
275
276 /**
277 * Temporarily assign a list of variables.
278 *
279 * @code
280 * $smarty->pushScope(array(
281 * 'first_name' => 'Alice',
282 * 'last_name' => 'roberts',
283 * ));
284 * $html = $smarty->fetch('view-contact.tpl');
285 * $smarty->popScope();
286 * @endcode
287 *
288 * @param array $vars
289 * (string $name => mixed $value).
290 * @return CRM_Core_Smarty
291 * @see popScope
292 */
293 public function pushScope($vars) {
294 $oldVars = $this->get_template_vars();
295 $backupFrame = array();
296 foreach ($vars as $key => $value) {
297 $backupFrame[$key] = isset($oldVars[$key]) ? $oldVars[$key] : NULL;
298 }
299 $this->backupFrames[] = $backupFrame;
300
301 $this->assignAll($vars);
302
303 return $this;
304 }
305
306 /**
307 * Remove any values that were previously pushed.
308 *
309 * @return CRM_Core_Smarty
310 * @see pushScope
311 */
312 public function popScope() {
313 $this->assignAll(array_pop($this->backupFrames));
314 return $this;
315 }
316
317 /**
318 * @param array $vars
319 * (string $name => mixed $value).
320 * @return CRM_Core_Smarty
321 */
322 public function assignAll($vars) {
323 foreach ($vars as $key => $value) {
324 $this->assign($key, $value);
325 }
326 return $this;
327 }
328
329 }