/
serendipity_smarty_class.inc.php
289 lines (240 loc) · 13.1 KB
/
serendipity_smarty_class.inc.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
<?php
// serendipity_smarty_class.inc.php lm 2014-12-10 Ian
// define secure_dir and trusted_dirs for Serendipity_Smarty_Security_Policy class.
@define('S9Y_TEMPLATE_FALLBACK', $serendipity['serendipityPath'] . $serendipity['templatePath'] . 'default');
@define('S9Y_TEMPLATE_USERDEFAULT', $serendipity['serendipityPath'] . $serendipity['templatePath'] . $serendipity['template']);
@define('S9Y_TEMPLATE_USERDEFAULT_BACKEND', $serendipity['serendipityPath'] . $serendipity['templatePath'] . $serendipity['template_backend']);
@define('S9Y_TEMPLATE_SECUREDIR', $serendipity['serendipityPath'] . $serendipity['templatePath']);
// Create a wrapper class extended from Smarty_Security - which allows access to S9Y-plugin and S9Y-template dirs
class Serendipity_Smarty_Security_Policy extends Smarty_Security
{
// these are the allowed functions only. - default as is
public $php_functions = array('isset', 'empty', 'sizeof', 'count', 'in_array', 'is_array', 'time', 'nl2br', 'class_exists');
// to disable all PHP functions
#public $php_functions = null;
// set allowed modifiers only. (default = array( 'escape', 'count' );)
public $php_modifiers = array('escape', 'rand', 'str_repeat', 'nl2br');
public $allow_constants = true;
public $allow_super_globals = true;
// array of template directories that are considered secure. No need, as ...TemplateDir concidered secure implicitly. (unproofed)
public $secure_dir = array(S9Y_TEMPLATE_SECUREDIR); // do we need this then?
// actually no need, as template dirs are explicit defined as trusted_dirs. (unproofed)
public $trusted_dir = array(S9Y_TEMPLATE_USERDEFAULT, S9Y_TEMPLATE_USERDEFAULT_BACKEND, S9Y_TEMPLATE_FALLBACK); // do we need this then?
#public $modifiers = array(); // can be omitted, when all allowed
// to test this - overwrites Serendipity_Smarty::default_modifiers and Serendipity_Smarty_Security_Policy::php_modifiers - modifier 'escape' not allowed by security setting
#public $allowed_modifiers = array('escape:"htmlall"');
// This allows the fetch() and include calls to pull .tpl files from any directory,
// so that symlinked plugin directories outside the s9y path can be included properly.
// TODO / FUTURE: If Smarty will implement a separation option to dissect fetch() from
// {include} calls, we should only apply this workaround to fetch() calls.
// Redirecting fetch() as our custom function is too risky and has too high a performance
// impact.
public function isTrustedResourceDir($path, $isConfig = NULL) {
return true;
}
public static function test()
{
var_dump(get_called_class());
}
}
// Create a wrapper class extended from Smarty
class Serendipity_Smarty extends Smarty
{
// bc mode for plugins Smarty2 compat INCLUDE_ANY fetch() calls - to avoid an undefinied property error.
public $security_settings = false;
public function __set($name, $value) {
if ($name == 'security') {
if ($value) {
$this->enableSecurity('Serendipity_Smarty_Security_Policy');
} else {
$this->disableSecurity();
}
} else {
parent::__set($name, $value);
}
}
/**
* It is often helpful to access the Smarty object from anywhere in your code, e.g in Plugins.
* Enables the Smarty object by instance always. The singleton pattern ensures that there is only one instance of the object available.
* To obtain an instance of this class use:
* $serendipity['smarty'] = Serendipity_Smarty::getInstance();
* The first time this is called a new instance will be created. Thereafter, the same instance is handed back.
**/
public static function getInstance($newInstance = null)
{
static $instance = null;
if(isset($newInstance)) {
$instance = $newInstance;
}
if ( $instance == null ) {
$instance = new Serendipity_Smarty();
}
return $instance;
}
public function __construct()
{
// Class Constructor. These automatically get set with each new instance.
parent::__construct();
// call the objects initialization parameters
self::setParams();
}
// smarty (3.1.x) object main parameter setup
private function setParams()
{
global $serendipity;
// some documentary from the smarty forum
/*
Addressing a specific $template_dir (see 3.1 release notes)
Smarty 3.1 introduces the $template_dir index notation.
$smarty->fetch('[foo]bar.tpl') and {include file="[foo]bar.tpl"} require the template bar.tpl to be loaded from $template_dir['foo'];
Smarty::setTemplateDir() and Smarty::addTemplateDir() offer ways to define indexes along with the actual directories.
*/
/* +++++++++++++++++++++++++++++++++++++++++++
Set all directory setters
Smarty will always use the first template found in order of the given array. Move the least significant directory to the end.
*/
$template_engine = serendipity_get_config_var('template_engine');
$template_dirs = array();
// first add template path
$template_dirs[] = $serendipity['serendipityPath'] . $serendipity['templatePath'] . ($serendipity['template'] ?? null);
// then fallback engines (which should never be a comma separated list)
if ($template_engine) {
$p = explode(',', $template_engine);
foreach($p AS $te) {
$template_dirs[] = $serendipity['serendipityPath'] . $serendipity['templatePath'] . trim($te) . '/';
}
}
$template_dirs[] = $serendipity['serendipityPath'] . $serendipity['templatePath'] . $serendipity['defaultTemplate'];
$template_dirs[] = $serendipity['serendipityPath'] . $serendipity['templatePath'] . $serendipity['template_backend'];
// add secure dir to template path, in case engine did have entries
if (S9Y_TEMPLATE_SECUREDIR != $serendipity['serendipityPath'] . $serendipity['templatePath']) {
$template_dirs[] = S9Y_TEMPLATE_SECUREDIR;
}
// disable plugin dir as added template dir is not advised, if set security is enabled (backend & frontend need access to fetch plugin templates)
$template_dirs[] = $serendipity['serendipityPath'] . 'plugins';
// add default template to addTemplate array, if not already set in engine
$template_dirs[] = S9Y_TEMPLATE_FALLBACK;
$this->setTemplateDir($template_dirs);
if (defined('S9Y_DATA_PATH')) {
$this->setCompileDir(S9Y_DATA_PATH . PATH_SMARTY_COMPILE);
} else {
$this->setCompileDir($serendipity['serendipityPath'] . PATH_SMARTY_COMPILE);
}
$this->setConfigDir(array(S9Y_TEMPLATE_USERDEFAULT));
if ( ( !is_dir($this->getCompileDir()) || !is_writable($this->getCompileDir()) ) && IS_installed === true) {
if(ini_get('display_errors') == 0 || ini_get('display_errors') == 'off') printf(DIRECTORY_WRITE_ERROR, $this->getCompileDir());
trigger_error(sprintf(DIRECTORY_WRITE_ERROR, $this->getCompileDir()), E_USER_ERROR);
}
/*
here we go with our other Smarty class properties, which can all be called by their property name (recommended)
$smarty->use_sub_dirs = true; or by $smarty->setUseSubDirs(true); and echo $smarty->getUseSubDirs();
as the latter's would run through an internal __call() wrapper function.
Note: rodneyrehm - From within the Smarty class context you can safely access properties like Smarty::$use_sub_dirs directly.
*/
/* +++++++++++++++++++++++++++++++++++
Set Smarty caching - outsourced to README_SMARTY_CACHING_PURPOSES.txt
Not usable in Serendipity with current template structure!
*/
/* ++++++++++++++++++++++++++++++++++++++++++++++
Set all other needed Smarty class properties
*/
#$this->merge_compiled_includes = true; // $this->setMergeCompiledIncludes(true); // With this option the subtemplate code is stored together with the calling template.
// default here to be overwritten by $serendipity['production'] == 'debug' - see below!
$this->debugging = false; // $this->setDebugging(false);
// Smarty will create subdirectories under the compiled templates and cache directories if $use_sub_dirs is set to TRUE, default is FALSE.
$this->use_sub_dirs = ( ini_get('safe_mode') ? false : true ); // $this->setUseSubDirs(false); // cache and compile dir only
// Smarty should update the cache files automatically if $smarty->compile_check is true.
$this->compile_check = true; // $this->setCompileCheck(true);
#$this->compile_check = COMPILECHECK_OFF (false) - template files will not be checked
#$this->compile_check = COMPILECHECK_ON (true) - template files will always be checked
#$this->compile_check = COMPILECHECK_CACHEMISS - template files will be checked, if caching is enabled and there is no existing cache file, or it has expired
/*
Note: rodneyrehm
If you actually manage to build a page from a single template (with inclusions and plugins and stuff)
in a way that allows smarty to do 304 handling - or implement the serve-stale-while-update approach,
you should go with CACHEMISS.
*/
$this->compile_id = &$serendipity['template']; // $this->setCompileId(&$serendipity['template'])
/*
Note: rodneyrehm
Please only specify the compile_id if you really need to.
That means if you pre-process templates for say internationalization.
Otherwise you don't need this and are better off ignoring it (performance-wise).
*/
$this->config_overwrite = true; // $this->setConfigOverwrite(true);
// production == debug extends from s9y version information (alpha|beta|cvs) is always debug | USE ===
if ($serendipity['production'] === 'debug') {
$this->force_compile = true; // $this->setForceCompile(true);
$this->caching = false; // $this->setCaching(false);
$this->debugging = true; // $this->setDebugging(true);
}
// set smarty error reporting. General error_reporting is set in serendipity/serendipity_config.inc.php
$this->error_reporting = E_ALL & ~(E_NOTICE|E_STRICT);
// Avoid a multitude of warnings when using Smarty on a newer PHP version, liek when we jumped
// to PHP 8:
$this->muteUndefinedOrNullWarnings();
}
/*
Note: Ian
These BC methods are to be kept as long as not converted to new syntax in additional plugins
Search "$serendipity['smarty']->register_" (11 hits in 6 files) in additional_plugins
serendipity_event_communityrating.php, serendipity_event_customarchive.php, serendipity_event_microformats.php,
serendipity_event_multilingual.php, serendipity_event_smartymarkup.php, serendipity_event_staticpage.php
*/
/**
* Registers custom function to be used in templates - BC mode Smarty 2 -> 3
*
* @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 mixed $cache_attrs
*/
public function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null)
{
$this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs);
}
/**
* Registers modifier to be used in templates - BC mode Smarty 2 -> 3
*
* @param string $modifier name of template modifier
* @param string $modifier_impl name of PHP function to register
*/
public function register_modifier($modifier, $modifier_impl)
{
$this->registerPlugin('modifier', $modifier, $modifier_impl);
}
/**
* Registers a resource to fetch a template - BC mode Smarty 2 -> 3
*
* @param string $type name of resource
* @param array $functions array of functions to handle resource
*/
public function register_resource($type, $functions)
{
$this->registerResource($type, $functions);
}
/**
* wrapper for assign_by_ref - BC mode Smarty 2 -> 3 (Serendipity core uses assignByRef already - and nearly no occurrences in additional plugins)
*
* @param string $tpl_var the template variable name
* @param mixed &$value the referenced value to assign
*/
public function assign_by_ref($tpl_var, &$value)
{
$this->assignByRef($tpl_var, $value);
}
/**
* Returns an array containing template variables- BC mode Smarty 2 -> 3
*
* @param string $name
* @return array
*/
public function get_template_vars($name=null)
{
return $this->getTemplateVars($name);
}
public static function test()
{
var_dump(get_called_class());
}
}