1 | <?php
|
---|
2 | /**
|
---|
3 | * Configuration Class and generic setting classes
|
---|
4 | *
|
---|
5 | * @author Chris Smith <[email protected]>
|
---|
6 | * @author Ben Coburn <[email protected]>
|
---|
7 | */
|
---|
8 |
|
---|
9 | if (!class_exists('configuration')) {
|
---|
10 |
|
---|
11 | class configuration {
|
---|
12 |
|
---|
13 | var $_name = 'conf'; // name of the config variable found in the files (overridden by $config['varname'])
|
---|
14 | var $_format = 'php'; // format of the config file, supported formats - php (overridden by $config['format'])
|
---|
15 | var $_heading = ''; // heading string written at top of config file - don't include comment indicators
|
---|
16 | var $_loaded = false; // set to true after configuration files are loaded
|
---|
17 | var $_metadata = array(); // holds metadata describing the settings
|
---|
18 | var $setting = array(); // array of setting objects
|
---|
19 | var $locked = false; // configuration is considered locked if it can't be updated
|
---|
20 |
|
---|
21 | // configuration filenames
|
---|
22 | var $_default_files = array();
|
---|
23 | var $_local_files = array(); // updated configuration is written to the first file
|
---|
24 | var $_protected_files = array();
|
---|
25 |
|
---|
26 | var $_plugin_list = null;
|
---|
27 |
|
---|
28 | /**
|
---|
29 | * constructor
|
---|
30 | */
|
---|
31 | function configuration($datafile) {
|
---|
32 | global $conf, $config_cascade;
|
---|
33 |
|
---|
34 | if (!@file_exists($datafile)) {
|
---|
35 | msg('No configuration metadata found at - '.htmlspecialchars($datafile),-1);
|
---|
36 | return;
|
---|
37 | }
|
---|
38 | include($datafile);
|
---|
39 |
|
---|
40 | if (isset($config['varname'])) $this->_name = $config['varname'];
|
---|
41 | if (isset($config['format'])) $this->_format = $config['format'];
|
---|
42 | if (isset($config['heading'])) $this->_heading = $config['heading'];
|
---|
43 |
|
---|
44 | $this->_default_files = $config_cascade['main']['default'];
|
---|
45 | $this->_local_files = $config_cascade['main']['local'];
|
---|
46 | $this->_protected_files = $config_cascade['main']['protected'];
|
---|
47 |
|
---|
48 | # if (isset($file['default'])) $this->_default_file = $file['default'];
|
---|
49 | # if (isset($file['local'])) $this->_local_file = $file['local'];
|
---|
50 | # if (isset($file['protected'])) $this->_protected_file = $file['protected'];
|
---|
51 |
|
---|
52 | $this->locked = $this->_is_locked();
|
---|
53 |
|
---|
54 | $this->_metadata = array_merge($meta, $this->get_plugintpl_metadata($conf['template']));
|
---|
55 |
|
---|
56 | $this->retrieve_settings();
|
---|
57 | }
|
---|
58 |
|
---|
59 | function retrieve_settings() {
|
---|
60 | global $conf;
|
---|
61 | $no_default_check = array('setting_fieldset', 'setting_undefined', 'setting_no_class');
|
---|
62 |
|
---|
63 | if (!$this->_loaded) {
|
---|
64 | $default = array_merge($this->get_plugintpl_default($conf['template']), $this->_read_config_group($this->_default_files));
|
---|
65 | $local = $this->_read_config_group($this->_local_files);
|
---|
66 | $protected = $this->_read_config_group($this->_protected_files);
|
---|
67 |
|
---|
68 | $keys = array_merge(array_keys($this->_metadata),array_keys($default), array_keys($local), array_keys($protected));
|
---|
69 | $keys = array_unique($keys);
|
---|
70 |
|
---|
71 | foreach ($keys as $key) {
|
---|
72 | if (isset($this->_metadata[$key])) {
|
---|
73 | $class = $this->_metadata[$key][0];
|
---|
74 | $class = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting';
|
---|
75 | if ($class=='setting') {
|
---|
76 | $this->setting[] = new setting_no_class($key,$param);
|
---|
77 | }
|
---|
78 |
|
---|
79 | $param = $this->_metadata[$key];
|
---|
80 | array_shift($param);
|
---|
81 | } else {
|
---|
82 | $class = 'setting_undefined';
|
---|
83 | $param = NULL;
|
---|
84 | }
|
---|
85 |
|
---|
86 | if (!in_array($class, $no_default_check) && !isset($default[$key])) {
|
---|
87 | $this->setting[] = new setting_no_default($key,$param);
|
---|
88 | }
|
---|
89 |
|
---|
90 | $this->setting[$key] = new $class($key,$param);
|
---|
91 | $this->setting[$key]->initialize($default[$key],$local[$key],$protected[$key]);
|
---|
92 | }
|
---|
93 |
|
---|
94 | $this->_loaded = true;
|
---|
95 | }
|
---|
96 | }
|
---|
97 |
|
---|
98 | function save_settings($id, $header='', $backup=true) {
|
---|
99 | global $conf;
|
---|
100 |
|
---|
101 | if ($this->locked) return false;
|
---|
102 |
|
---|
103 | # $file = eval('return '.$this->_local_file.';');
|
---|
104 | $file = $this->_local_files[0];
|
---|
105 |
|
---|
106 | // backup current file (remove any existing backup)
|
---|
107 | if (@file_exists($file) && $backup) {
|
---|
108 | if (@file_exists($file.'.bak')) @unlink($file.'.bak');
|
---|
109 | if (!io_rename($file, $file.'.bak')) return false;
|
---|
110 | }
|
---|
111 |
|
---|
112 | if (!$fh = @fopen($file, 'wb')) {
|
---|
113 | io_rename($file.'.bak', $file); // problem opening, restore the backup
|
---|
114 | return false;
|
---|
115 | }
|
---|
116 |
|
---|
117 | if (empty($header)) $header = $this->_heading;
|
---|
118 |
|
---|
119 | $out = $this->_out_header($id,$header);
|
---|
120 |
|
---|
121 | foreach ($this->setting as $setting) {
|
---|
122 | $out .= $setting->out($this->_name, $this->_format);
|
---|
123 | }
|
---|
124 |
|
---|
125 | $out .= $this->_out_footer();
|
---|
126 |
|
---|
127 | @fwrite($fh, $out);
|
---|
128 | fclose($fh);
|
---|
129 | if($conf['fperm']) chmod($file, $conf['fperm']);
|
---|
130 | return true;
|
---|
131 | }
|
---|
132 |
|
---|
133 | function _read_config_group($files) {
|
---|
134 | $config = array();
|
---|
135 | foreach ($files as $file) {
|
---|
136 | $config = array_merge($config, $this->_read_config($file));
|
---|
137 | }
|
---|
138 |
|
---|
139 | return $config;
|
---|
140 | }
|
---|
141 |
|
---|
142 | /**
|
---|
143 | * return an array of config settings
|
---|
144 | */
|
---|
145 | function _read_config($file) {
|
---|
146 |
|
---|
147 | if (!$file) return array();
|
---|
148 |
|
---|
149 | $config = array();
|
---|
150 | # $file = eval('return '.$file.';');
|
---|
151 |
|
---|
152 | if ($this->_format == 'php') {
|
---|
153 |
|
---|
154 | if(@file_exists($file)){
|
---|
155 | $contents = @php_strip_whitespace($file);
|
---|
156 | }else{
|
---|
157 | $contents = '';
|
---|
158 | }
|
---|
159 | $pattern = '/\$'.$this->_name.'\[[\'"]([^=]+)[\'"]\] ?= ?(.*?);(?=[^;]*(?:\$'.$this->_name.'|@include|$))/s';
|
---|
160 | $matches=array();
|
---|
161 | preg_match_all($pattern,$contents,$matches,PREG_SET_ORDER);
|
---|
162 |
|
---|
163 | for ($i=0; $i<count($matches); $i++) {
|
---|
164 |
|
---|
165 | // correct issues with the incoming data
|
---|
166 | // FIXME ... for now merge multi-dimensional array indices using ____
|
---|
167 | $key = preg_replace('/.\]\[./',CM_KEYMARKER,$matches[$i][1]);
|
---|
168 |
|
---|
169 | // remove quotes from quoted strings & unescape escaped data
|
---|
170 | $value = preg_replace('/^(\'|")(.*)(?<!\\\\)\1$/s','$2',$matches[$i][2]);
|
---|
171 | $value = strtr($value, array('\\\\'=>'\\','\\\''=>'\'','\\"'=>'"'));
|
---|
172 |
|
---|
173 | $config[$key] = $value;
|
---|
174 | }
|
---|
175 | }
|
---|
176 |
|
---|
177 | return $config;
|
---|
178 | }
|
---|
179 |
|
---|
180 | function _out_header($id, $header) {
|
---|
181 | $out = '';
|
---|
182 | if ($this->_format == 'php') {
|
---|
183 | $out .= '<'.'?php'."\n".
|
---|
184 | "/*\n".
|
---|
185 | " * ".$header."\n".
|
---|
186 | " * Auto-generated by ".$id." plugin\n".
|
---|
187 | " * Run for user: ".$_SERVER['REMOTE_USER']."\n".
|
---|
188 | " * Date: ".date('r')."\n".
|
---|
189 | " */\n\n";
|
---|
190 | }
|
---|
191 |
|
---|
192 | return $out;
|
---|
193 | }
|
---|
194 |
|
---|
195 | function _out_footer() {
|
---|
196 | $out = '';
|
---|
197 | if ($this->_format == 'php') {
|
---|
198 | # if ($this->_protected_file) {
|
---|
199 | # $out .= "\n@include(".$this->_protected_file.");\n";
|
---|
200 | # }
|
---|
201 | $out .= "\n// end auto-generated content\n";
|
---|
202 | }
|
---|
203 |
|
---|
204 | return $out;
|
---|
205 | }
|
---|
206 |
|
---|
207 | // configuration is considered locked if there is no local settings filename
|
---|
208 | // or the directory its in is not writable or the file exists and is not writable
|
---|
209 | function _is_locked() {
|
---|
210 | if (!$this->_local_files) return true;
|
---|
211 |
|
---|
212 | # $local = eval('return '.$this->_local_file.';');
|
---|
213 | $local = $this->_local_files[0];
|
---|
214 |
|
---|
215 | if (!is_writable(dirname($local))) return true;
|
---|
216 | if (@file_exists($local) && !is_writable($local)) return true;
|
---|
217 |
|
---|
218 | return false;
|
---|
219 | }
|
---|
220 |
|
---|
221 | /**
|
---|
222 | * not used ... conf's contents are an array!
|
---|
223 | * reduce any multidimensional settings to one dimension using CM_KEYMARKER
|
---|
224 | */
|
---|
225 | function _flatten($conf,$prefix='') {
|
---|
226 |
|
---|
227 | $out = array();
|
---|
228 |
|
---|
229 | foreach($conf as $key => $value) {
|
---|
230 | if (!is_array($value)) {
|
---|
231 | $out[$prefix.$key] = $value;
|
---|
232 | continue;
|
---|
233 | }
|
---|
234 |
|
---|
235 | $tmp = $this->_flatten($value,$prefix.$key.CM_KEYMARKER);
|
---|
236 | $out = array_merge($out,$tmp);
|
---|
237 | }
|
---|
238 |
|
---|
239 | return $out;
|
---|
240 | }
|
---|
241 |
|
---|
242 | function get_plugin_list() {
|
---|
243 | if (is_null($this->_plugin_list)) {
|
---|
244 | $list = plugin_list('',true); // all plugins, including disabled ones
|
---|
245 |
|
---|
246 | // remove this plugin from the list
|
---|
247 | $idx = array_search('config',$list);
|
---|
248 | unset($list[$idx]);
|
---|
249 |
|
---|
250 | trigger_event('PLUGIN_CONFIG_PLUGINLIST',$list);
|
---|
251 | $this->_plugin_list = $list;
|
---|
252 | }
|
---|
253 |
|
---|
254 | return $this->_plugin_list;
|
---|
255 | }
|
---|
256 |
|
---|
257 | /**
|
---|
258 | * load metadata for plugin and template settings
|
---|
259 | */
|
---|
260 | function get_plugintpl_metadata($tpl){
|
---|
261 | $file = '/conf/metadata.php';
|
---|
262 | $class = '/conf/settings.class.php';
|
---|
263 | $metadata = array();
|
---|
264 |
|
---|
265 | foreach ($this->get_plugin_list() as $plugin) {
|
---|
266 | $plugin_dir = plugin_directory($plugin);
|
---|
267 | if (@file_exists(DOKU_PLUGIN.$plugin_dir.$file)){
|
---|
268 | $meta = array();
|
---|
269 | @include(DOKU_PLUGIN.$plugin_dir.$file);
|
---|
270 | @include(DOKU_PLUGIN.$plugin_dir.$class);
|
---|
271 | if (!empty($meta)) {
|
---|
272 | $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'] = array('fieldset');
|
---|
273 | }
|
---|
274 | foreach ($meta as $key => $value){
|
---|
275 | if ($value[0]=='fieldset') { continue; } //plugins only get one fieldset
|
---|
276 | $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value;
|
---|
277 | }
|
---|
278 | }
|
---|
279 | }
|
---|
280 |
|
---|
281 | // the same for the active template
|
---|
282 | if (@file_exists(DOKU_TPLINC.$file)){
|
---|
283 | $meta = array();
|
---|
284 | @include(DOKU_TPLINC.$file);
|
---|
285 | @include(DOKU_TPLINC.$class);
|
---|
286 | if (!empty($meta)) {
|
---|
287 | $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] = array('fieldset');
|
---|
288 | }
|
---|
289 | foreach ($meta as $key => $value){
|
---|
290 | if ($value[0]=='fieldset') { continue; } //template only gets one fieldset
|
---|
291 | $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value;
|
---|
292 | }
|
---|
293 | }
|
---|
294 |
|
---|
295 | return $metadata;
|
---|
296 | }
|
---|
297 |
|
---|
298 | /**
|
---|
299 | * load default settings for plugins and templates
|
---|
300 | */
|
---|
301 | function get_plugintpl_default($tpl){
|
---|
302 | $file = '/conf/default.php';
|
---|
303 | $default = array();
|
---|
304 |
|
---|
305 | foreach ($this->get_plugin_list() as $plugin) {
|
---|
306 | $plugin_dir = plugin_directory($plugin);
|
---|
307 | if (@file_exists(DOKU_PLUGIN.$plugin_dir.$file)){
|
---|
308 | $conf = array();
|
---|
309 | @include(DOKU_PLUGIN.$plugin_dir.$file);
|
---|
310 | foreach ($conf as $key => $value){
|
---|
311 | $default['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value;
|
---|
312 | }
|
---|
313 | }
|
---|
314 | }
|
---|
315 |
|
---|
316 | // the same for the active template
|
---|
317 | if (@file_exists(DOKU_TPLINC.$file)){
|
---|
318 | $conf = array();
|
---|
319 | @include(DOKU_TPLINC.$file);
|
---|
320 | foreach ($conf as $key => $value){
|
---|
321 | $default['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value;
|
---|
322 | }
|
---|
323 | }
|
---|
324 |
|
---|
325 | return $default;
|
---|
326 | }
|
---|
327 |
|
---|
328 | }
|
---|
329 | }
|
---|
330 |
|
---|
331 | if (!class_exists('setting')) {
|
---|
332 | class setting {
|
---|
333 |
|
---|
334 | var $_key = '';
|
---|
335 | var $_default = NULL;
|
---|
336 | var $_local = NULL;
|
---|
337 | var $_protected = NULL;
|
---|
338 |
|
---|
339 | var $_pattern = '';
|
---|
340 | var $_error = false; // only used by those classes which error check
|
---|
341 | var $_input = NULL; // only used by those classes which error check
|
---|
342 |
|
---|
343 | var $_cautionList = array(
|
---|
344 | 'basedir' => 'danger', 'baseurl' => 'danger', 'savedir' => 'danger', 'useacl' => 'danger', 'authtype' => 'danger', 'superuser' => 'danger', 'userewrite' => 'danger',
|
---|
345 | 'start' => 'warning', 'camelcase' => 'warning', 'deaccent' => 'warning', 'sepchar' => 'warning', 'compression' => 'warning', 'xsendfile' => 'warning', 'renderer_xhtml' => 'warning', 'fnencode' => 'warning',
|
---|
346 | 'allowdebug' => 'security', 'htmlok' => 'security', 'phpok' => 'security', 'iexssprotect' => 'security', 'xmlrpc' => 'security', 'fullpath' => 'security'
|
---|
347 | );
|
---|
348 |
|
---|
349 | function setting($key, $params=NULL) {
|
---|
350 | $this->_key = $key;
|
---|
351 |
|
---|
352 | if (is_array($params)) {
|
---|
353 | foreach($params as $property => $value) {
|
---|
354 | $this->$property = $value;
|
---|
355 | }
|
---|
356 | }
|
---|
357 | }
|
---|
358 |
|
---|
359 | /**
|
---|
360 | * receives current values for the setting $key
|
---|
361 | */
|
---|
362 | function initialize($default, $local, $protected) {
|
---|
363 | if (isset($default)) $this->_default = $default;
|
---|
364 | if (isset($local)) $this->_local = $local;
|
---|
365 | if (isset($protected)) $this->_protected = $protected;
|
---|
366 | }
|
---|
367 |
|
---|
368 | /**
|
---|
369 | * update setting with user provided value $input
|
---|
370 | * if value fails error check, save it
|
---|
371 | *
|
---|
372 | * @return true if changed, false otherwise (incl. on error)
|
---|
373 | */
|
---|
374 | function update($input) {
|
---|
375 | if (is_null($input)) return false;
|
---|
376 | if ($this->is_protected()) return false;
|
---|
377 |
|
---|
378 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
379 | if ($value == $input) return false;
|
---|
380 |
|
---|
381 | if ($this->_pattern && !preg_match($this->_pattern,$input)) {
|
---|
382 | $this->_error = true;
|
---|
383 | $this->_input = $input;
|
---|
384 | return false;
|
---|
385 | }
|
---|
386 |
|
---|
387 | $this->_local = $input;
|
---|
388 | return true;
|
---|
389 | }
|
---|
390 |
|
---|
391 | /**
|
---|
392 | * @return array(string $label_html, string $input_html)
|
---|
393 | */
|
---|
394 | function html(&$plugin, $echo=false) {
|
---|
395 | $value = '';
|
---|
396 | $disable = '';
|
---|
397 |
|
---|
398 | if ($this->is_protected()) {
|
---|
399 | $value = $this->_protected;
|
---|
400 | $disable = 'disabled="disabled"';
|
---|
401 | } else {
|
---|
402 | if ($echo && $this->_error) {
|
---|
403 | $value = $this->_input;
|
---|
404 | } else {
|
---|
405 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
406 | }
|
---|
407 | }
|
---|
408 |
|
---|
409 | $key = htmlspecialchars($this->_key);
|
---|
410 | $value = htmlspecialchars($value);
|
---|
411 |
|
---|
412 | $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
|
---|
413 | $input = '<textarea rows="3" cols="40" id="config___'.$key.'" name="config['.$key.']" class="edit" '.$disable.'>'.$value.'</textarea>';
|
---|
414 | return array($label,$input);
|
---|
415 | }
|
---|
416 |
|
---|
417 | /**
|
---|
418 | * generate string to save setting value to file according to $fmt
|
---|
419 | */
|
---|
420 | function out($var, $fmt='php') {
|
---|
421 |
|
---|
422 | if ($this->is_protected()) return '';
|
---|
423 | if (is_null($this->_local) || ($this->_default == $this->_local)) return '';
|
---|
424 |
|
---|
425 | $out = '';
|
---|
426 |
|
---|
427 | if ($fmt=='php') {
|
---|
428 | // translation string needs to be improved FIXME
|
---|
429 | $tr = array("\n"=>'\n', "\r"=>'\r', "\t"=>'\t', "\\" => '\\\\', "'" => '\\\'');
|
---|
430 | $tr = array("\\" => '\\\\', "'" => '\\\'');
|
---|
431 |
|
---|
432 | $out = '$'.$var."['".$this->_out_key()."'] = '".strtr($this->_local, $tr)."';\n";
|
---|
433 | }
|
---|
434 |
|
---|
435 | return $out;
|
---|
436 | }
|
---|
437 |
|
---|
438 | function prompt(&$plugin) {
|
---|
439 | $prompt = $plugin->getLang($this->_key);
|
---|
440 | if (!$prompt) $prompt = htmlspecialchars(str_replace(array('____','_'),' ',$this->_key));
|
---|
441 | return $prompt;
|
---|
442 | }
|
---|
443 |
|
---|
444 | function is_protected() { return !is_null($this->_protected); }
|
---|
445 | function is_default() { return !$this->is_protected() && is_null($this->_local); }
|
---|
446 | function error() { return $this->_error; }
|
---|
447 |
|
---|
448 | function caution() {
|
---|
449 | if (!array_key_exists($this->_key, $this->_cautionList)) return false;
|
---|
450 | return $this->_cautionList[$this->_key];
|
---|
451 | }
|
---|
452 |
|
---|
453 | function _out_key($pretty=false,$url=false) {
|
---|
454 | if($pretty){
|
---|
455 | $out = str_replace(CM_KEYMARKER,"»",$this->_key);
|
---|
456 | if ($url && !strstr($out,'»')) {//provide no urls for plugins, etc.
|
---|
457 | if ($out == 'start') //one exception
|
---|
458 | return '<a href="http://www.dokuwiki.org/config:startpage">'.$out.'</a>';
|
---|
459 | else
|
---|
460 | return '<a href="http://www.dokuwiki.org/config:'.$out.'">'.$out.'</a>';
|
---|
461 | }
|
---|
462 | return $out;
|
---|
463 | }else{
|
---|
464 | return str_replace(CM_KEYMARKER,"']['",$this->_key);
|
---|
465 | }
|
---|
466 | }
|
---|
467 | }
|
---|
468 | }
|
---|
469 |
|
---|
470 | if (!class_exists('setting_string')) {
|
---|
471 | class setting_string extends setting {
|
---|
472 | function html(&$plugin, $echo=false) {
|
---|
473 | $value = '';
|
---|
474 | $disable = '';
|
---|
475 |
|
---|
476 | if ($this->is_protected()) {
|
---|
477 | $value = $this->_protected;
|
---|
478 | $disable = 'disabled="disabled"';
|
---|
479 | } else {
|
---|
480 | if ($echo && $this->_error) {
|
---|
481 | $value = $this->_input;
|
---|
482 | } else {
|
---|
483 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
484 | }
|
---|
485 | }
|
---|
486 |
|
---|
487 | $key = htmlspecialchars($this->_key);
|
---|
488 | $value = htmlspecialchars($value);
|
---|
489 |
|
---|
490 | $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
|
---|
491 | $input = '<input id="config___'.$key.'" name="config['.$key.']" type="text" class="edit" value="'.$value.'" '.$disable.'/>';
|
---|
492 | return array($label,$input);
|
---|
493 | }
|
---|
494 | }
|
---|
495 | }
|
---|
496 |
|
---|
497 | if (!class_exists('setting_password')) {
|
---|
498 | class setting_password extends setting_string {
|
---|
499 |
|
---|
500 | var $_code = 'plain'; // mechanism to be used to obscure passwords
|
---|
501 |
|
---|
502 | function update($input) {
|
---|
503 | if ($this->is_protected()) return false;
|
---|
504 | if (!$input) return false;
|
---|
505 |
|
---|
506 | if ($this->_pattern && !preg_match($this->_pattern,$input)) {
|
---|
507 | $this->_error = true;
|
---|
508 | $this->_input = $input;
|
---|
509 | return false;
|
---|
510 | }
|
---|
511 |
|
---|
512 | $this->_local = conf_encodeString($input,$this->_code);
|
---|
513 | return true;
|
---|
514 | }
|
---|
515 |
|
---|
516 | function html(&$plugin, $echo=false) {
|
---|
517 |
|
---|
518 | $value = '';
|
---|
519 | $disable = $this->is_protected() ? 'disabled="disabled"' : '';
|
---|
520 |
|
---|
521 | $key = htmlspecialchars($this->_key);
|
---|
522 |
|
---|
523 | $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
|
---|
524 | $input = '<input id="config___'.$key.'" name="config['.$key.']" autocomplete="off" type="password" class="edit" value="" '.$disable.' />';
|
---|
525 | return array($label,$input);
|
---|
526 | }
|
---|
527 | }
|
---|
528 | }
|
---|
529 |
|
---|
530 | if (!class_exists('setting_email')) {
|
---|
531 | if (!defined('SETTING_EMAIL_PATTERN')) define('SETTING_EMAIL_PATTERN','<^'.PREG_PATTERN_VALID_EMAIL.'$>');
|
---|
532 |
|
---|
533 | class setting_email extends setting_string {
|
---|
534 | var $_pattern = SETTING_EMAIL_PATTERN; // no longer required, retained for backward compatibility - FIXME, may not be necessary
|
---|
535 | var $_multiple = false;
|
---|
536 |
|
---|
537 | /**
|
---|
538 | * update setting with user provided value $input
|
---|
539 | * if value fails error check, save it
|
---|
540 | *
|
---|
541 | * @return true if changed, false otherwise (incl. on error)
|
---|
542 | */
|
---|
543 | function update($input) {
|
---|
544 | if (is_null($input)) return false;
|
---|
545 | if ($this->is_protected()) return false;
|
---|
546 |
|
---|
547 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
548 | if ($value == $input) return false;
|
---|
549 |
|
---|
550 | if ($this->_multiple) {
|
---|
551 | $mails = array_filter(array_map('trim', split(',', $input)));
|
---|
552 | } else {
|
---|
553 | $mails = array($input);
|
---|
554 | }
|
---|
555 |
|
---|
556 | foreach ($mails as $mail) {
|
---|
557 | if (!mail_isvalid($mail)) {
|
---|
558 | $this->_error = true;
|
---|
559 | $this->_input = $input;
|
---|
560 | return false;
|
---|
561 | }
|
---|
562 | }
|
---|
563 |
|
---|
564 | $this->_local = $input;
|
---|
565 | return true;
|
---|
566 | }
|
---|
567 | }
|
---|
568 | }
|
---|
569 |
|
---|
570 | if (!class_exists('setting_richemail')) {
|
---|
571 | class setting_richemail extends setting_email {
|
---|
572 |
|
---|
573 | /**
|
---|
574 | * update setting with user provided value $input
|
---|
575 | * if value fails error check, save it
|
---|
576 | *
|
---|
577 | * @return true if changed, false otherwise (incl. on error)
|
---|
578 | */
|
---|
579 | function update($input) {
|
---|
580 | if (is_null($input)) return false;
|
---|
581 | if ($this->is_protected()) return false;
|
---|
582 |
|
---|
583 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
584 | if ($value == $input) return false;
|
---|
585 |
|
---|
586 | // replace variables with pseudo values
|
---|
587 | $test = $input;
|
---|
588 | $test = str_replace('@USER@','joe',$test);
|
---|
589 | $test = str_replace('@NAME@','Joe Schmoe',$test);
|
---|
590 | $test = str_replace('@MAIL@','[email protected]',$test);
|
---|
591 |
|
---|
592 | // now only check the address part
|
---|
593 | if(preg_match('#(.*?)<(.*?)>#',$test,$matches)){
|
---|
594 | $text = trim($matches[1]);
|
---|
595 | $addr = $matches[2];
|
---|
596 | }else{
|
---|
597 | $addr = $test;
|
---|
598 | }
|
---|
599 |
|
---|
600 | if ($test !== '' && !mail_isvalid($addr)) {
|
---|
601 | $this->_error = true;
|
---|
602 | $this->_input = $input;
|
---|
603 | return false;
|
---|
604 | }
|
---|
605 |
|
---|
606 | $this->_local = $input;
|
---|
607 | return true;
|
---|
608 | }
|
---|
609 |
|
---|
610 | }
|
---|
611 | }
|
---|
612 |
|
---|
613 |
|
---|
614 | if (!class_exists('setting_numeric')) {
|
---|
615 | class setting_numeric extends setting_string {
|
---|
616 | // This allows for many PHP syntax errors...
|
---|
617 | // var $_pattern = '/^[-+\/*0-9 ]*$/';
|
---|
618 | // much more restrictive, but should eliminate syntax errors.
|
---|
619 | var $_pattern = '/^[-+]? *[0-9]+ *(?:[-+*] *[0-9]+ *)*$/';
|
---|
620 | var $_min = null;
|
---|
621 | var $_max = null;
|
---|
622 |
|
---|
623 | function update($input) {
|
---|
624 | $local = $this->_local;
|
---|
625 | $valid = parent::update($input);
|
---|
626 | if ($valid && !(is_null($this->_min) && is_null($this->_max))) {
|
---|
627 | $numeric_local = (int) eval('return '.$this->_local.';');
|
---|
628 | if ((!is_null($this->_min) && $numeric_local < $this->_min) ||
|
---|
629 | (!is_null($this->_max) && $numeric_local > $this->_max)) {
|
---|
630 | $this->_error = true;
|
---|
631 | $this->_input = $input;
|
---|
632 | $this->_local = $local;
|
---|
633 | $valid = false;
|
---|
634 | }
|
---|
635 | }
|
---|
636 | return $valid;
|
---|
637 | }
|
---|
638 |
|
---|
639 | function out($var, $fmt='php') {
|
---|
640 |
|
---|
641 | if ($this->is_protected()) return '';
|
---|
642 | if (is_null($this->_local) || ($this->_default == $this->_local)) return '';
|
---|
643 |
|
---|
644 | $out = '';
|
---|
645 |
|
---|
646 | if ($fmt=='php') {
|
---|
647 | $local = $this->_local === '' ? "''" : $this->_local;
|
---|
648 | $out .= '$'.$var."['".$this->_out_key()."'] = ".$local.";\n";
|
---|
649 | }
|
---|
650 |
|
---|
651 | return $out;
|
---|
652 | }
|
---|
653 | }
|
---|
654 | }
|
---|
655 |
|
---|
656 | if (!class_exists('setting_numericopt')) {
|
---|
657 | class setting_numericopt extends setting_numeric {
|
---|
658 | // just allow an empty config
|
---|
659 | var $_pattern = '/^(|[-]?[0-9]+(?:[-+*][0-9]+)*)$/';
|
---|
660 | }
|
---|
661 | }
|
---|
662 |
|
---|
663 | if (!class_exists('setting_onoff')) {
|
---|
664 | class setting_onoff extends setting_numeric {
|
---|
665 |
|
---|
666 | function html(&$plugin) {
|
---|
667 | $value = '';
|
---|
668 | $disable = '';
|
---|
669 |
|
---|
670 | if ($this->is_protected()) {
|
---|
671 | $value = $this->_protected;
|
---|
672 | $disable = ' disabled="disabled"';
|
---|
673 | } else {
|
---|
674 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
675 | }
|
---|
676 |
|
---|
677 | $key = htmlspecialchars($this->_key);
|
---|
678 | $checked = ($value) ? ' checked="checked"' : '';
|
---|
679 |
|
---|
680 | $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
|
---|
681 | $input = '<div class="input"><input id="config___'.$key.'" name="config['.$key.']" type="checkbox" class="checkbox" value="1"'.$checked.$disable.'/></div>';
|
---|
682 | return array($label,$input);
|
---|
683 | }
|
---|
684 |
|
---|
685 | function update($input) {
|
---|
686 | if ($this->is_protected()) return false;
|
---|
687 |
|
---|
688 | $input = ($input) ? 1 : 0;
|
---|
689 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
690 | if ($value == $input) return false;
|
---|
691 |
|
---|
692 | $this->_local = $input;
|
---|
693 | return true;
|
---|
694 | }
|
---|
695 | }
|
---|
696 | }
|
---|
697 |
|
---|
698 | if (!class_exists('setting_multichoice')) {
|
---|
699 | class setting_multichoice extends setting_string {
|
---|
700 | var $_choices = array();
|
---|
701 |
|
---|
702 | function html(&$plugin) {
|
---|
703 | $value = '';
|
---|
704 | $disable = '';
|
---|
705 | $nochoice = '';
|
---|
706 |
|
---|
707 | if ($this->is_protected()) {
|
---|
708 | $value = $this->_protected;
|
---|
709 | $disable = ' disabled="disabled"';
|
---|
710 | } else {
|
---|
711 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
712 | }
|
---|
713 |
|
---|
714 | // ensure current value is included
|
---|
715 | if (!in_array($value, $this->_choices)) {
|
---|
716 | $this->_choices[] = $value;
|
---|
717 | }
|
---|
718 | // disable if no other choices
|
---|
719 | if (!$this->is_protected() && count($this->_choices) <= 1) {
|
---|
720 | $disable = ' disabled="disabled"';
|
---|
721 | $nochoice = $plugin->getLang('nochoice');
|
---|
722 | }
|
---|
723 |
|
---|
724 | $key = htmlspecialchars($this->_key);
|
---|
725 |
|
---|
726 | $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
|
---|
727 |
|
---|
728 | $input = "<div class=\"input\">\n";
|
---|
729 | $input .= '<select class="edit" id="config___'.$key.'" name="config['.$key.']"'.$disable.'>'."\n";
|
---|
730 | foreach ($this->_choices as $choice) {
|
---|
731 | $selected = ($value == $choice) ? ' selected="selected"' : '';
|
---|
732 | $option = $plugin->getLang($this->_key.'_o_'.$choice);
|
---|
733 | if (!$option && isset($this->lang[$this->_key.'_o_'.$choice])) $option = $this->lang[$this->_key.'_o_'.$choice];
|
---|
734 | if (!$option) $option = $choice;
|
---|
735 |
|
---|
736 | $choice = htmlspecialchars($choice);
|
---|
737 | $option = htmlspecialchars($option);
|
---|
738 | $input .= ' <option value="'.$choice.'"'.$selected.' >'.$option.'</option>'."\n";
|
---|
739 | }
|
---|
740 | $input .= "</select> $nochoice \n";
|
---|
741 | $input .= "</div>\n";
|
---|
742 |
|
---|
743 | return array($label,$input);
|
---|
744 | }
|
---|
745 |
|
---|
746 | function update($input) {
|
---|
747 | if (is_null($input)) return false;
|
---|
748 | if ($this->is_protected()) return false;
|
---|
749 |
|
---|
750 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
751 | if ($value == $input) return false;
|
---|
752 |
|
---|
753 | if (!in_array($input, $this->_choices)) return false;
|
---|
754 |
|
---|
755 | $this->_local = $input;
|
---|
756 | return true;
|
---|
757 | }
|
---|
758 | }
|
---|
759 | }
|
---|
760 |
|
---|
761 |
|
---|
762 | if (!class_exists('setting_dirchoice')) {
|
---|
763 | class setting_dirchoice extends setting_multichoice {
|
---|
764 |
|
---|
765 | var $_dir = '';
|
---|
766 |
|
---|
767 | function initialize($default,$local,$protected) {
|
---|
768 |
|
---|
769 | // populate $this->_choices with a list of directories
|
---|
770 | $list = array();
|
---|
771 |
|
---|
772 | if ($dh = @opendir($this->_dir)) {
|
---|
773 | while (false !== ($entry = readdir($dh))) {
|
---|
774 | if ($entry == '.' || $entry == '..') continue;
|
---|
775 | if ($this->_pattern && !preg_match($this->_pattern,$entry)) continue;
|
---|
776 |
|
---|
777 | $file = (is_link($this->_dir.$entry)) ? readlink($this->_dir.$entry) : $entry;
|
---|
778 | if (is_dir($this->_dir.$file)) $list[] = $entry;
|
---|
779 | }
|
---|
780 | closedir($dh);
|
---|
781 | }
|
---|
782 | sort($list);
|
---|
783 | $this->_choices = $list;
|
---|
784 |
|
---|
785 | parent::initialize($default,$local,$protected);
|
---|
786 | }
|
---|
787 | }
|
---|
788 | }
|
---|
789 |
|
---|
790 |
|
---|
791 | if (!class_exists('setting_hidden')) {
|
---|
792 | class setting_hidden extends setting {
|
---|
793 | // Used to explicitly ignore a setting in the configuration manager.
|
---|
794 | }
|
---|
795 | }
|
---|
796 |
|
---|
797 | if (!class_exists('setting_fieldset')) {
|
---|
798 | class setting_fieldset extends setting {
|
---|
799 | // A do-nothing class used to detect the 'fieldset' type.
|
---|
800 | // Used to start a new settings "display-group".
|
---|
801 | }
|
---|
802 | }
|
---|
803 |
|
---|
804 | if (!class_exists('setting_undefined')) {
|
---|
805 | class setting_undefined extends setting_hidden {
|
---|
806 | // A do-nothing class used to detect settings with no metadata entry.
|
---|
807 | // Used internaly to hide undefined settings, and generate the undefined settings list.
|
---|
808 | }
|
---|
809 | }
|
---|
810 |
|
---|
811 | if (!class_exists('setting_no_class')) {
|
---|
812 | class setting_no_class extends setting_undefined {
|
---|
813 | // A do-nothing class used to detect settings with a missing setting class.
|
---|
814 | // Used internaly to hide undefined settings, and generate the undefined settings list.
|
---|
815 | }
|
---|
816 | }
|
---|
817 |
|
---|
818 | if (!class_exists('setting_no_default')) {
|
---|
819 | class setting_no_default extends setting_undefined {
|
---|
820 | // A do-nothing class used to detect settings with no default value.
|
---|
821 | // Used internaly to hide undefined settings, and generate the undefined settings list.
|
---|
822 | }
|
---|
823 | }
|
---|
824 |
|
---|
825 | if (!class_exists('setting_multicheckbox')) {
|
---|
826 | class setting_multicheckbox extends setting_string {
|
---|
827 |
|
---|
828 | var $_choices = array();
|
---|
829 | var $_combine = array();
|
---|
830 |
|
---|
831 | function update($input) {
|
---|
832 | if ($this->is_protected()) return false;
|
---|
833 |
|
---|
834 | // split any combined values + convert from array to comma separated string
|
---|
835 | $input = ($input) ? $input : array();
|
---|
836 | $input = $this->_array2str($input);
|
---|
837 |
|
---|
838 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
839 | if ($value == $input) return false;
|
---|
840 |
|
---|
841 | if ($this->_pattern && !preg_match($this->_pattern,$input)) {
|
---|
842 | $this->_error = true;
|
---|
843 | $this->_input = $input;
|
---|
844 | return false;
|
---|
845 | }
|
---|
846 |
|
---|
847 | $this->_local = $input;
|
---|
848 | return true;
|
---|
849 | }
|
---|
850 |
|
---|
851 | function html(&$plugin, $echo=false) {
|
---|
852 |
|
---|
853 | $value = '';
|
---|
854 | $disable = '';
|
---|
855 |
|
---|
856 | if ($this->is_protected()) {
|
---|
857 | $value = $this->_protected;
|
---|
858 | $disable = 'disabled="disabled"';
|
---|
859 | } else {
|
---|
860 | if ($echo && $this->_error) {
|
---|
861 | $value = $this->_input;
|
---|
862 | } else {
|
---|
863 | $value = is_null($this->_local) ? $this->_default : $this->_local;
|
---|
864 | }
|
---|
865 | }
|
---|
866 |
|
---|
867 | $key = htmlspecialchars($this->_key);
|
---|
868 |
|
---|
869 | // convert from comma separated list into array + combine complimentary actions
|
---|
870 | $value = $this->_str2array($value);
|
---|
871 | $default = $this->_str2array($this->_default);
|
---|
872 |
|
---|
873 | $input = '';
|
---|
874 | foreach ($this->_choices as $choice) {
|
---|
875 | $idx = array_search($choice, $value);
|
---|
876 | $idx_default = array_search($choice,$default);
|
---|
877 |
|
---|
878 | $checked = ($idx !== false) ? 'checked="checked"' : '';
|
---|
879 |
|
---|
880 | // ideally this would be handled using a second class of "default", however IE6 does not
|
---|
881 | // correctly support CSS selectors referencing multiple class names on the same element
|
---|
882 | // (e.g. .default.selection).
|
---|
883 | $class = (($idx !== false) == (false !== $idx_default)) ? " selectiondefault" : "";
|
---|
884 |
|
---|
885 | $prompt = ($plugin->getLang($this->_key.'_'.$choice) ?
|
---|
886 | $plugin->getLang($this->_key.'_'.$choice) : htmlspecialchars($choice));
|
---|
887 |
|
---|
888 | $input .= '<div class="selection'.$class.'">'."\n";
|
---|
889 | $input .= '<label for="config___'.$key.'_'.$choice.'">'.$prompt."</label>\n";
|
---|
890 | $input .= '<input id="config___'.$key.'_'.$choice.'" name="config['.$key.'][]" type="checkbox" class="checkbox" value="'.$choice.'" '.$disable.' '.$checked."/>\n";
|
---|
891 | $input .= "</div>\n";
|
---|
892 |
|
---|
893 | // remove this action from the disabledactions array
|
---|
894 | if ($idx !== false) unset($value[$idx]);
|
---|
895 | if ($idx_default !== false) unset($default[$idx_default]);
|
---|
896 | }
|
---|
897 |
|
---|
898 | // handle any remaining values
|
---|
899 | $other = join(',',$value);
|
---|
900 |
|
---|
901 | $class = (count($default == count($value)) && (count($value) == count(array_intersect($value,$default)))) ?
|
---|
902 | " selectiondefault" : "";
|
---|
903 |
|
---|
904 | $input .= '<div class="other'.$class.'">'."\n";
|
---|
905 | $input .= '<label for="config___'.$key.'_other">'.$plugin->getLang($key.'_other')."</label>\n";
|
---|
906 | $input .= '<input id="config___'.$key.'_other" name="config['.$key.'][other]" type="text" class="edit" value="'.htmlspecialchars($other).'" '.$disable." />\n";
|
---|
907 | $input .= "</div>\n";
|
---|
908 |
|
---|
909 | $label = '<label>'.$this->prompt($plugin).'</label>';
|
---|
910 | return array($label,$input);
|
---|
911 | }
|
---|
912 |
|
---|
913 | /**
|
---|
914 | * convert comma separated list to an array and combine any complimentary values
|
---|
915 | */
|
---|
916 | function _str2array($str) {
|
---|
917 | $array = explode(',',$str);
|
---|
918 |
|
---|
919 | if (!empty($this->_combine)) {
|
---|
920 | foreach ($this->_combine as $key => $combinators) {
|
---|
921 | $idx = array();
|
---|
922 | foreach ($combinators as $val) {
|
---|
923 | if (($idx[] = array_search($val, $array)) === false) break;
|
---|
924 | }
|
---|
925 |
|
---|
926 | if (count($idx) && $idx[count($idx)-1] !== false) {
|
---|
927 | foreach ($idx as $i) unset($array[$i]);
|
---|
928 | $array[] = $key;
|
---|
929 | }
|
---|
930 | }
|
---|
931 | }
|
---|
932 |
|
---|
933 | return $array;
|
---|
934 | }
|
---|
935 |
|
---|
936 | /**
|
---|
937 | * convert array of values + other back to a comma separated list, incl. splitting any combined values
|
---|
938 | */
|
---|
939 | function _array2str($input) {
|
---|
940 |
|
---|
941 | // handle other
|
---|
942 | $other = trim($input['other']);
|
---|
943 | $other = !empty($other) ? explode(',',str_replace(' ','',$input['other'])) : array();
|
---|
944 | unset($input['other']);
|
---|
945 |
|
---|
946 | $array = array_unique(array_merge($input, $other));
|
---|
947 |
|
---|
948 | // deconstruct any combinations
|
---|
949 | if (!empty($this->_combine)) {
|
---|
950 | foreach ($this->_combine as $key => $combinators) {
|
---|
951 |
|
---|
952 | $idx = array_search($key,$array);
|
---|
953 | if ($idx !== false) {
|
---|
954 | unset($array[$idx]);
|
---|
955 | $array = array_merge($array, $combinators);
|
---|
956 | }
|
---|
957 | }
|
---|
958 | }
|
---|
959 |
|
---|
960 | return join(',',array_unique($array));
|
---|
961 | }
|
---|
962 | }
|
---|
963 | }
|
---|
964 |
|
---|
965 | /**
|
---|
966 | * Provide php_strip_whitespace (php5 function) functionality
|
---|
967 | *
|
---|
968 | * @author Chris Smith <[email protected]>
|
---|
969 | */
|
---|
970 | if (!function_exists('php_strip_whitespace')) {
|
---|
971 |
|
---|
972 | if (function_exists('token_get_all')) {
|
---|
973 |
|
---|
974 | if (!defined('T_ML_COMMENT')) {
|
---|
975 | define('T_ML_COMMENT', T_COMMENT);
|
---|
976 | } else {
|
---|
977 | define('T_DOC_COMMENT', T_ML_COMMENT);
|
---|
978 | }
|
---|
979 |
|
---|
980 | /**
|
---|
981 | * modified from original
|
---|
982 | * source Google Groups, php.general, by David Otton
|
---|
983 | */
|
---|
984 | function php_strip_whitespace($file) {
|
---|
985 | if (!@is_readable($file)) return '';
|
---|
986 |
|
---|
987 | $in = join('',@file($file));
|
---|
988 | $out = '';
|
---|
989 |
|
---|
990 | $tokens = token_get_all($in);
|
---|
991 |
|
---|
992 | foreach ($tokens as $token) {
|
---|
993 | if (is_string ($token)) {
|
---|
994 | $out .= $token;
|
---|
995 | } else {
|
---|
996 | list ($id, $text) = $token;
|
---|
997 | switch ($id) {
|
---|
998 | case T_COMMENT : // fall thru
|
---|
999 | case T_ML_COMMENT : // fall thru
|
---|
1000 | case T_DOC_COMMENT : // fall thru
|
---|
1001 | case T_WHITESPACE :
|
---|
1002 | break;
|
---|
1003 | default : $out .= $text; break;
|
---|
1004 | }
|
---|
1005 | }
|
---|
1006 | }
|
---|
1007 | return ($out);
|
---|
1008 | }
|
---|
1009 |
|
---|
1010 | } else {
|
---|
1011 |
|
---|
1012 | function is_whitespace($c) { return (strpos("\t\n\r ",$c) !== false); }
|
---|
1013 | function is_quote($c) { return (strpos("\"'",$c) !== false); }
|
---|
1014 | function is_escaped($s,$i) {
|
---|
1015 | $idx = $i-1;
|
---|
1016 | while(($idx>=0) && ($s{$idx} == '\\')) $idx--;
|
---|
1017 | return (($i - $idx + 1) % 2);
|
---|
1018 | }
|
---|
1019 |
|
---|
1020 | function is_commentopen($str, $i) {
|
---|
1021 | if ($str{$i} == '#') return "\n";
|
---|
1022 | if ($str{$i} == '/') {
|
---|
1023 | if ($str{$i+1} == '/') return "\n";
|
---|
1024 | if ($str{$i+1} == '*') return "*/";
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | return false;
|
---|
1028 | }
|
---|
1029 |
|
---|
1030 | function php_strip_whitespace($file) {
|
---|
1031 |
|
---|
1032 | if (!@is_readable($file)) return '';
|
---|
1033 |
|
---|
1034 | $contents = join('',@file($file));
|
---|
1035 | $out = '';
|
---|
1036 |
|
---|
1037 | $state = 0;
|
---|
1038 | for ($i=0; $i<strlen($contents); $i++) {
|
---|
1039 | if (!$state && is_whitespace($contents{$i})) continue;
|
---|
1040 |
|
---|
1041 | if (!$state && ($c_close = is_commentopen($contents, $i))) {
|
---|
1042 | $c_open_len = ($contents{$i} == '/') ? 2 : 1;
|
---|
1043 | $i = strpos($contents, $c_close, $i+$c_open_len)+strlen($c_close)-1;
|
---|
1044 | continue;
|
---|
1045 | }
|
---|
1046 |
|
---|
1047 | $out .= $contents{$i};
|
---|
1048 | if (is_quote($contents{$i})) {
|
---|
1049 | if (($state == $contents{$i}) && !is_escaped($contents, $i)) { $state = 0; continue; }
|
---|
1050 | if (!$state) {$state = $contents{$i}; continue; }
|
---|
1051 | }
|
---|
1052 | }
|
---|
1053 |
|
---|
1054 | return $out;
|
---|
1055 | }
|
---|
1056 | }
|
---|
1057 | }
|
---|