[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Configuration Class and generic setting classes 4 * 5 * @author Chris Smith <chris@jalakai.co.uk> 6 * @author Ben Coburn <btcoburn@silicodon.net> 7 */ 8 9 10 if(!defined('CM_KEYMARKER')) define('CM_KEYMARKER','____'); 11 12 if (!class_exists('configuration')) { 13 /** 14 * Class configuration 15 */ 16 class configuration { 17 18 var $_name = 'conf'; // name of the config variable found in the files (overridden by $config['varname']) 19 var $_format = 'php'; // format of the config file, supported formats - php (overridden by $config['format']) 20 var $_heading = ''; // heading string written at top of config file - don't include comment indicators 21 var $_loaded = false; // set to true after configuration files are loaded 22 var $_metadata = array(); // holds metadata describing the settings 23 /** @var setting[] */ 24 var $setting = array(); // array of setting objects 25 var $locked = false; // configuration is considered locked if it can't be updated 26 var $show_disabled_plugins = false; 27 28 // configuration filenames 29 var $_default_files = array(); 30 var $_local_files = array(); // updated configuration is written to the first file 31 var $_protected_files = array(); 32 33 var $_plugin_list = null; 34 35 /** 36 * constructor 37 * 38 * @param string $datafile path to config metadata file 39 */ 40 public function __construct($datafile) { 41 global $conf, $config_cascade; 42 43 if (!file_exists($datafile)) { 44 msg('No configuration metadata found at - '.htmlspecialchars($datafile),-1); 45 return; 46 } 47 $meta = array(); 48 include($datafile); 49 50 if (isset($config['varname'])) $this->_name = $config['varname']; 51 if (isset($config['format'])) $this->_format = $config['format']; 52 if (isset($config['heading'])) $this->_heading = $config['heading']; 53 54 $this->_default_files = $config_cascade['main']['default']; 55 $this->_local_files = $config_cascade['main']['local']; 56 $this->_protected_files = $config_cascade['main']['protected']; 57 58 $this->locked = $this->_is_locked(); 59 $this->_metadata = array_merge($meta, $this->get_plugintpl_metadata($conf['template'])); 60 $this->retrieve_settings(); 61 } 62 63 /** 64 * Retrieve and stores settings in setting[] attribute 65 */ 66 public function retrieve_settings() { 67 global $conf; 68 $no_default_check = array('setting_fieldset', 'setting_undefined', 'setting_no_class'); 69 70 if (!$this->_loaded) { 71 $default = array_merge($this->get_plugintpl_default($conf['template']), $this->_read_config_group($this->_default_files)); 72 $local = $this->_read_config_group($this->_local_files); 73 $protected = $this->_read_config_group($this->_protected_files); 74 75 $keys = array_merge(array_keys($this->_metadata),array_keys($default), array_keys($local), array_keys($protected)); 76 $keys = array_unique($keys); 77 78 $param = null; 79 foreach ($keys as $key) { 80 if (isset($this->_metadata[$key])) { 81 $class = $this->_metadata[$key][0]; 82 83 if($class && class_exists('setting_'.$class)){ 84 $class = 'setting_'.$class; 85 } else { 86 if($class != '') { 87 $this->setting[] = new setting_no_class($key,$param); 88 } 89 $class = 'setting'; 90 } 91 92 $param = $this->_metadata[$key]; 93 array_shift($param); 94 } else { 95 $class = 'setting_undefined'; 96 $param = null; 97 } 98 99 if (!in_array($class, $no_default_check) && !isset($default[$key])) { 100 $this->setting[] = new setting_no_default($key,$param); 101 } 102 103 $this->setting[$key] = new $class($key,$param); 104 105 $d = array_key_exists($key, $default) ? $default[$key] : null; 106 $l = array_key_exists($key, $local) ? $local[$key] : null; 107 $p = array_key_exists($key, $protected) ? $protected[$key] : null; 108 109 $this->setting[$key]->initialize($d,$l,$p); 110 } 111 112 $this->_loaded = true; 113 } 114 } 115 116 /** 117 * Stores setting[] array to file 118 * 119 * @param string $id Name of plugin, which saves the settings 120 * @param string $header Text at the top of the rewritten settings file 121 * @param bool $backup backup current file? (remove any existing backup) 122 * @return bool succesful? 123 */ 124 public function save_settings($id, $header='', $backup=true) { 125 global $conf; 126 127 if ($this->locked) return false; 128 129 // write back to the last file in the local config cascade 130 $file = end($this->_local_files); 131 132 // backup current file (remove any existing backup) 133 if (file_exists($file) && $backup) { 134 if (file_exists($file.'.bak')) @unlink($file.'.bak'); 135 if (!io_rename($file, $file.'.bak')) return false; 136 } 137 138 if (!$fh = @fopen($file, 'wb')) { 139 io_rename($file.'.bak', $file); // problem opening, restore the backup 140 return false; 141 } 142 143 if (empty($header)) $header = $this->_heading; 144 145 $out = $this->_out_header($id,$header); 146 147 foreach ($this->setting as $setting) { 148 $out .= $setting->out($this->_name, $this->_format); 149 } 150 151 $out .= $this->_out_footer(); 152 153 @fwrite($fh, $out); 154 fclose($fh); 155 if($conf['fperm']) chmod($file, $conf['fperm']); 156 return true; 157 } 158 159 /** 160 * Update last modified time stamp of the config file 161 * 162 * @return bool 163 */ 164 public function touch_settings(){ 165 if ($this->locked) return false; 166 $file = end($this->_local_files); 167 return @touch($file); 168 } 169 170 /** 171 * Read and merge given config files 172 * 173 * @param array $files file paths 174 * @return array config settings 175 */ 176 protected function _read_config_group($files) { 177 $config = array(); 178 foreach ($files as $file) { 179 $config = array_merge($config, $this->_read_config($file)); 180 } 181 182 return $config; 183 } 184 185 /** 186 * Return an array of config settings 187 * 188 * @param string $file file path 189 * @return array config settings 190 */ 191 function _read_config($file) { 192 193 if (!$file) return array(); 194 195 $config = array(); 196 197 if ($this->_format == 'php') { 198 199 if(file_exists($file)){ 200 $contents = @php_strip_whitespace($file); 201 }else{ 202 $contents = ''; 203 } 204 $pattern = '/\$'.$this->_name.'\[[\'"]([^=]+)[\'"]\] ?= ?(.*?);(?=[^;]*(?:\$'.$this->_name.'|$))/s'; 205 $matches=array(); 206 preg_match_all($pattern,$contents,$matches,PREG_SET_ORDER); 207 208 for ($i=0; $i<count($matches); $i++) { 209 $value = $matches[$i][2]; 210 211 // correct issues with the incoming data 212 // FIXME ... for now merge multi-dimensional array indices using ____ 213 $key = preg_replace('/.\]\[./',CM_KEYMARKER,$matches[$i][1]); 214 215 // handle arrays 216 if(preg_match('/^array ?\((.*)\)/', $value, $match)){ 217 $arr = explode(',', $match[1]); 218 219 // remove quotes from quoted strings & unescape escaped data 220 $len = count($arr); 221 for($j=0; $j<$len; $j++){ 222 $arr[$j] = trim($arr[$j]); 223 $arr[$j] = $this->_readValue($arr[$j]); 224 } 225 226 $value = $arr; 227 }else{ 228 $value = $this->_readValue($value); 229 } 230 231 $config[$key] = $value; 232 } 233 } 234 235 return $config; 236 } 237 238 /** 239 * Convert php string into value 240 * 241 * @param string $value 242 * @return bool|string 243 */ 244 protected function _readValue($value) { 245 $removequotes_pattern = '/^(\'|")(.*)(?<!\\\\)\1$/s'; 246 $unescape_pairs = array( 247 '\\\\' => '\\', 248 '\\\'' => '\'', 249 '\\"' => '"' 250 ); 251 252 if($value == 'true') { 253 $value = true; 254 } elseif($value == 'false') { 255 $value = false; 256 } else { 257 // remove quotes from quoted strings & unescape escaped data 258 $value = preg_replace($removequotes_pattern,'$2',$value); 259 $value = strtr($value, $unescape_pairs); 260 } 261 return $value; 262 } 263 264 /** 265 * Returns header of rewritten settings file 266 * 267 * @param string $id plugin name of which generated this output 268 * @param string $header additional text for at top of the file 269 * @return string text of header 270 */ 271 protected function _out_header($id, $header) { 272 $out = ''; 273 if ($this->_format == 'php') { 274 $out .= '<'.'?php'."\n". 275 "/*\n". 276 " * ".$header."\n". 277 " * Auto-generated by ".$id." plugin\n". 278 " * Run for user: ".$_SERVER['REMOTE_USER']."\n". 279 " * Date: ".date('r')."\n". 280 " */\n\n"; 281 } 282 283 return $out; 284 } 285 286 /** 287 * Returns footer of rewritten settings file 288 * 289 * @return string text of footer 290 */ 291 protected function _out_footer() { 292 $out = ''; 293 if ($this->_format == 'php') { 294 $out .= "\n// end auto-generated content\n"; 295 } 296 297 return $out; 298 } 299 300 /** 301 * Configuration is considered locked if there is no local settings filename 302 * or the directory its in is not writable or the file exists and is not writable 303 * 304 * @return bool true: locked, false: writable 305 */ 306 protected function _is_locked() { 307 if (!$this->_local_files) return true; 308 309 $local = $this->_local_files[0]; 310 311 if (!is_writable(dirname($local))) return true; 312 if (file_exists($local) && !is_writable($local)) return true; 313 314 return false; 315 } 316 317 /** 318 * not used ... conf's contents are an array! 319 * reduce any multidimensional settings to one dimension using CM_KEYMARKER 320 * 321 * @param $conf 322 * @param string $prefix 323 * @return array 324 */ 325 protected function _flatten($conf,$prefix='') { 326 327 $out = array(); 328 329 foreach($conf as $key => $value) { 330 if (!is_array($value)) { 331 $out[$prefix.$key] = $value; 332 continue; 333 } 334 335 $tmp = $this->_flatten($value,$prefix.$key.CM_KEYMARKER); 336 $out = array_merge($out,$tmp); 337 } 338 339 return $out; 340 } 341 342 /** 343 * Returns array of plugin names 344 * 345 * @return array plugin names 346 * @triggers PLUGIN_CONFIG_PLUGINLIST event 347 */ 348 function get_plugin_list() { 349 if (is_null($this->_plugin_list)) { 350 $list = plugin_list('',$this->show_disabled_plugins); 351 352 // remove this plugin from the list 353 $idx = array_search('config',$list); 354 unset($list[$idx]); 355 sort($list); // Sort plugin list alphabetically for display 356 357 trigger_event('PLUGIN_CONFIG_PLUGINLIST',$list); 358 $this->_plugin_list = $list; 359 } 360 361 return $this->_plugin_list; 362 } 363 364 /** 365 * load metadata for plugin and template settings 366 * 367 * @param string $tpl name of active template 368 * @return array metadata of settings 369 */ 370 function get_plugintpl_metadata($tpl){ 371 $file = '/conf/metadata.php'; 372 $class = '/conf/settings.class.php'; 373 $metadata = array(); 374 375 foreach ($this->get_plugin_list() as $plugin) { 376 $plugin_dir = plugin_directory($plugin); 377 if (file_exists(DOKU_PLUGIN.$plugin_dir.$file)){ 378 $meta = array(); 379 @include(DOKU_PLUGIN.$plugin_dir.$file); 380 @include(DOKU_PLUGIN.$plugin_dir.$class); 381 if (!empty($meta)) { 382 $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'] = array('fieldset'); 383 } 384 foreach ($meta as $key => $value){ 385 if ($value[0]=='fieldset') { continue; } //plugins only get one fieldset 386 $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value; 387 } 388 } 389 } 390 391 // the same for the active template 392 if (file_exists(tpl_incdir().$file)){ 393 $meta = array(); 394 @include(tpl_incdir().$file); 395 @include(tpl_incdir().$class); 396 if (!empty($meta)) { 397 $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] = array('fieldset'); 398 } 399 foreach ($meta as $key => $value){ 400 if ($value[0]=='fieldset') { continue; } //template only gets one fieldset 401 $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value; 402 } 403 } 404 405 return $metadata; 406 } 407 408 /** 409 * Load default settings for plugins and templates 410 * 411 * @param string $tpl name of active template 412 * @return array default settings 413 */ 414 function get_plugintpl_default($tpl){ 415 $file = '/conf/default.php'; 416 $default = array(); 417 418 foreach ($this->get_plugin_list() as $plugin) { 419 $plugin_dir = plugin_directory($plugin); 420 if (file_exists(DOKU_PLUGIN.$plugin_dir.$file)){ 421 $conf = $this->_read_config(DOKU_PLUGIN.$plugin_dir.$file); 422 foreach ($conf as $key => $value){ 423 $default['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value; 424 } 425 } 426 } 427 428 // the same for the active template 429 if (file_exists(tpl_incdir().$file)){ 430 $conf = $this->_read_config(tpl_incdir().$file); 431 foreach ($conf as $key => $value){ 432 $default['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value; 433 } 434 } 435 436 return $default; 437 } 438 439 } 440 } 441 442 if (!class_exists('setting')) { 443 /** 444 * Class setting 445 */ 446 class setting { 447 448 var $_key = ''; 449 var $_default = null; 450 var $_local = null; 451 var $_protected = null; 452 453 var $_pattern = ''; 454 var $_error = false; // only used by those classes which error check 455 var $_input = null; // only used by those classes which error check 456 var $_caution = null; // used by any setting to provide an alert along with the setting 457 // valid alerts, 'warning', 'danger', 'security' 458 // images matching the alerts are in the plugin's images directory 459 460 static protected $_validCautions = array('warning','danger','security'); 461 462 /** 463 * @param string $key 464 * @param array|null $params array with metadata of setting 465 */ 466 public function __construct($key, $params=null) { 467 $this->_key = $key; 468 469 if (is_array($params)) { 470 foreach($params as $property => $value) { 471 $this->$property = $value; 472 } 473 } 474 } 475 476 /** 477 * Receives current values for the setting $key 478 * 479 * @param mixed $default default setting value 480 * @param mixed $local local setting value 481 * @param mixed $protected protected setting value 482 */ 483 public function initialize($default, $local, $protected) { 484 if (isset($default)) $this->_default = $default; 485 if (isset($local)) $this->_local = $local; 486 if (isset($protected)) $this->_protected = $protected; 487 } 488 489 /** 490 * update changed setting with user provided value $input 491 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 492 * - if changed value passes error check, set $this->_local to the new value 493 * 494 * @param mixed $input the new value 495 * @return boolean true if changed, false otherwise (also on error) 496 */ 497 public function update($input) { 498 if (is_null($input)) return false; 499 if ($this->is_protected()) return false; 500 501 $value = is_null($this->_local) ? $this->_default : $this->_local; 502 if ($value == $input) return false; 503 504 if ($this->_pattern && !preg_match($this->_pattern,$input)) { 505 $this->_error = true; 506 $this->_input = $input; 507 return false; 508 } 509 510 $this->_local = $input; 511 return true; 512 } 513 514 /** 515 * Build html for label and input of setting 516 * 517 * @param admin_plugin_config $plugin object of config plugin 518 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 519 * @return string[] with content array(string $label_html, string $input_html) 520 */ 521 public function html(admin_plugin_config $plugin, $echo=false) { 522 $disable = ''; 523 524 if ($this->is_protected()) { 525 $value = $this->_protected; 526 $disable = 'disabled="disabled"'; 527 } else { 528 if ($echo && $this->_error) { 529 $value = $this->_input; 530 } else { 531 $value = is_null($this->_local) ? $this->_default : $this->_local; 532 } 533 } 534 535 $key = htmlspecialchars($this->_key); 536 $value = formText($value); 537 538 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 539 $input = '<textarea rows="3" cols="40" id="config___'.$key.'" name="config['.$key.']" class="edit" '.$disable.'>'.$value.'</textarea>'; 540 return array($label,$input); 541 } 542 543 /** 544 * Generate string to save setting value to file according to $fmt 545 * 546 * @param string $var name of variable 547 * @param string $fmt save format 548 * @return string 549 */ 550 public function out($var, $fmt='php') { 551 552 if ($this->is_protected()) return ''; 553 if (is_null($this->_local) || ($this->_default == $this->_local)) return ''; 554 555 $out = ''; 556 557 if ($fmt=='php') { 558 $tr = array("\\" => '\\\\', "'" => '\\\''); 559 560 $out = '$'.$var."['".$this->_out_key()."'] = '".strtr( cleanText($this->_local), $tr)."';\n"; 561 } 562 563 return $out; 564 } 565 566 /** 567 * Returns the localized prompt 568 * 569 * @param admin_plugin_config $plugin object of config plugin 570 * @return string text 571 */ 572 public function prompt(admin_plugin_config $plugin) { 573 $prompt = $plugin->getLang($this->_key); 574 if (!$prompt) $prompt = htmlspecialchars(str_replace(array('____','_'),' ',$this->_key)); 575 return $prompt; 576 } 577 578 /** 579 * Is setting protected 580 * 581 * @return bool 582 */ 583 public function is_protected() { return !is_null($this->_protected); } 584 585 /** 586 * Is setting the default? 587 * 588 * @return bool 589 */ 590 public function is_default() { return !$this->is_protected() && is_null($this->_local); } 591 592 /** 593 * Has an error? 594 * 595 * @return bool 596 */ 597 public function error() { return $this->_error; } 598 599 /** 600 * Returns caution 601 * 602 * @return false|string caution string, otherwise false for invalid caution 603 */ 604 public function caution() { 605 if (!empty($this->_caution)) { 606 if (!in_array($this->_caution, setting::$_validCautions)) { 607 trigger_error('Invalid caution string ('.$this->_caution.') in metadata for setting "'.$this->_key.'"', E_USER_WARNING); 608 return false; 609 } 610 return $this->_caution; 611 } 612 // compatibility with previous cautionList 613 // TODO: check if any plugins use; remove 614 if (!empty($this->_cautionList[$this->_key])) { 615 $this->_caution = $this->_cautionList[$this->_key]; 616 unset($this->_cautionList); 617 618 return $this->caution(); 619 } 620 return false; 621 } 622 623 /** 624 * Returns setting key, eventually with referer to config: namespace at dokuwiki.org 625 * 626 * @param bool $pretty create nice key 627 * @param bool $url provide url to config: namespace 628 * @return string key 629 */ 630 public function _out_key($pretty=false,$url=false) { 631 if($pretty){ 632 $out = str_replace(CM_KEYMARKER,"»",$this->_key); 633 if ($url && !strstr($out,'»')) {//provide no urls for plugins, etc. 634 if ($out == 'start') //one exception 635 return '<a href="http://www.dokuwiki.org/config:startpage">'.$out.'</a>'; 636 else 637 return '<a href="http://www.dokuwiki.org/config:'.$out.'">'.$out.'</a>'; 638 } 639 return $out; 640 }else{ 641 return str_replace(CM_KEYMARKER,"']['",$this->_key); 642 } 643 } 644 } 645 } 646 647 648 if (!class_exists('setting_array')) { 649 /** 650 * Class setting_array 651 */ 652 class setting_array extends setting { 653 654 /** 655 * Create an array from a string 656 * 657 * @param string $string 658 * @return array 659 */ 660 protected function _from_string($string){ 661 $array = explode(',', $string); 662 $array = array_map('trim', $array); 663 $array = array_filter($array); 664 $array = array_unique($array); 665 return $array; 666 } 667 668 /** 669 * Create a string from an array 670 * 671 * @param array $array 672 * @return string 673 */ 674 protected function _from_array($array){ 675 return join(', ', (array) $array); 676 } 677 678 /** 679 * update setting with user provided value $input 680 * if value fails error check, save it 681 * 682 * @param string $input 683 * @return bool true if changed, false otherwise (incl. on error) 684 */ 685 function update($input) { 686 if (is_null($input)) return false; 687 if ($this->is_protected()) return false; 688 689 $input = $this->_from_string($input); 690 691 $value = is_null($this->_local) ? $this->_default : $this->_local; 692 if ($value == $input) return false; 693 694 foreach($input as $item){ 695 if ($this->_pattern && !preg_match($this->_pattern,$item)) { 696 $this->_error = true; 697 $this->_input = $input; 698 return false; 699 } 700 } 701 702 $this->_local = $input; 703 return true; 704 } 705 706 /** 707 * Escaping 708 * 709 * @param string $string 710 * @return string 711 */ 712 protected function _escape($string) { 713 $tr = array("\\" => '\\\\', "'" => '\\\''); 714 return "'".strtr( cleanText($string), $tr)."'"; 715 } 716 717 /** 718 * Generate string to save setting value to file according to $fmt 719 * 720 * @param string $var name of variable 721 * @param string $fmt save format 722 * @return string 723 */ 724 function out($var, $fmt='php') { 725 726 if ($this->is_protected()) return ''; 727 if (is_null($this->_local) || ($this->_default == $this->_local)) return ''; 728 729 $out = ''; 730 731 if ($fmt=='php') { 732 $vals = array_map(array($this, '_escape'), $this->_local); 733 $out = '$'.$var."['".$this->_out_key()."'] = array(".join(', ',$vals).");\n"; 734 } 735 736 return $out; 737 } 738 739 /** 740 * Build html for label and input of setting 741 * 742 * @param admin_plugin_config $plugin object of config plugin 743 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 744 * @return string[] with content array(string $label_html, string $input_html) 745 */ 746 function html(admin_plugin_config $plugin, $echo=false) { 747 $disable = ''; 748 749 if ($this->is_protected()) { 750 $value = $this->_protected; 751 $disable = 'disabled="disabled"'; 752 } else { 753 if ($echo && $this->_error) { 754 $value = $this->_input; 755 } else { 756 $value = is_null($this->_local) ? $this->_default : $this->_local; 757 } 758 } 759 760 $key = htmlspecialchars($this->_key); 761 $value = htmlspecialchars($this->_from_array($value)); 762 763 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 764 $input = '<input id="config___'.$key.'" name="config['.$key.']" type="text" class="edit" value="'.$value.'" '.$disable.'/>'; 765 return array($label,$input); 766 } 767 } 768 } 769 770 if (!class_exists('setting_string')) { 771 /** 772 * Class setting_string 773 */ 774 class setting_string extends setting { 775 /** 776 * Build html for label and input of setting 777 * 778 * @param admin_plugin_config $plugin object of config plugin 779 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 780 * @return string[] with content array(string $label_html, string $input_html) 781 */ 782 function html(admin_plugin_config $plugin, $echo=false) { 783 $disable = ''; 784 785 if ($this->is_protected()) { 786 $value = $this->_protected; 787 $disable = 'disabled="disabled"'; 788 } else { 789 if ($echo && $this->_error) { 790 $value = $this->_input; 791 } else { 792 $value = is_null($this->_local) ? $this->_default : $this->_local; 793 } 794 } 795 796 $key = htmlspecialchars($this->_key); 797 $value = htmlspecialchars($value); 798 799 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 800 $input = '<input id="config___'.$key.'" name="config['.$key.']" type="text" class="edit" value="'.$value.'" '.$disable.'/>'; 801 return array($label,$input); 802 } 803 } 804 } 805 806 if (!class_exists('setting_password')) { 807 /** 808 * Class setting_password 809 */ 810 class setting_password extends setting_string { 811 812 var $_code = 'plain'; // mechanism to be used to obscure passwords 813 814 /** 815 * update changed setting with user provided value $input 816 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 817 * - if changed value passes error check, set $this->_local to the new value 818 * 819 * @param mixed $input the new value 820 * @return boolean true if changed, false otherwise (also on error) 821 */ 822 function update($input) { 823 if ($this->is_protected()) return false; 824 if (!$input) return false; 825 826 if ($this->_pattern && !preg_match($this->_pattern,$input)) { 827 $this->_error = true; 828 $this->_input = $input; 829 return false; 830 } 831 832 $this->_local = conf_encodeString($input,$this->_code); 833 return true; 834 } 835 836 /** 837 * Build html for label and input of setting 838 * 839 * @param admin_plugin_config $plugin object of config plugin 840 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 841 * @return string[] with content array(string $label_html, string $input_html) 842 */ 843 function html(admin_plugin_config $plugin, $echo=false) { 844 845 $disable = $this->is_protected() ? 'disabled="disabled"' : ''; 846 847 $key = htmlspecialchars($this->_key); 848 849 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 850 $input = '<input id="config___'.$key.'" name="config['.$key.']" autocomplete="off" type="password" class="edit" value="" '.$disable.' />'; 851 return array($label,$input); 852 } 853 } 854 } 855 856 if (!class_exists('setting_email')) { 857 /** 858 * Class setting_email 859 */ 860 class setting_email extends setting_string { 861 var $_multiple = false; 862 var $_placeholders = false; 863 864 /** 865 * update setting with user provided value $input 866 * if value fails error check, save it 867 * 868 * @param mixed $input 869 * @return boolean true if changed, false otherwise (incl. on error) 870 */ 871 function update($input) { 872 if (is_null($input)) return false; 873 if ($this->is_protected()) return false; 874 875 $value = is_null($this->_local) ? $this->_default : $this->_local; 876 if ($value == $input) return false; 877 if($input === ''){ 878 $this->_local = $input; 879 return true; 880 } 881 $mail = $input; 882 883 if($this->_placeholders){ 884 // replace variables with pseudo values 885 $mail = str_replace('@USER@','joe',$mail); 886 $mail = str_replace('@NAME@','Joe Schmoe',$mail); 887 $mail = str_replace('@MAIL@','joe@example.com',$mail); 888 } 889 890 // multiple mail addresses? 891 if ($this->_multiple) { 892 $mails = array_filter(array_map('trim', explode(',', $mail))); 893 } else { 894 $mails = array($mail); 895 } 896 897 // check them all 898 foreach ($mails as $mail) { 899 // only check the address part 900 if(preg_match('#(.*?)<(.*?)>#', $mail, $matches)){ 901 $addr = $matches[2]; 902 }else{ 903 $addr = $mail; 904 } 905 906 if (!mail_isvalid($addr)) { 907 $this->_error = true; 908 $this->_input = $input; 909 return false; 910 } 911 } 912 913 $this->_local = $input; 914 return true; 915 } 916 } 917 } 918 919 if (!class_exists('setting_numeric')) { 920 /** 921 * Class setting_numeric 922 */ 923 class setting_numeric extends setting_string { 924 // This allows for many PHP syntax errors... 925 // var $_pattern = '/^[-+\/*0-9 ]*$/'; 926 // much more restrictive, but should eliminate syntax errors. 927 var $_pattern = '/^[-+]? *[0-9]+ *(?:[-+*] *[0-9]+ *)*$/'; 928 var $_min = null; 929 var $_max = null; 930 931 /** 932 * update changed setting with user provided value $input 933 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 934 * - if changed value passes error check, set $this->_local to the new value 935 * 936 * @param mixed $input the new value 937 * @return boolean true if changed, false otherwise (also on error) 938 */ 939 function update($input) { 940 $local = $this->_local; 941 $valid = parent::update($input); 942 if ($valid && !(is_null($this->_min) && is_null($this->_max))) { 943 $numeric_local = (int) eval('return '.$this->_local.';'); 944 if ((!is_null($this->_min) && $numeric_local < $this->_min) || 945 (!is_null($this->_max) && $numeric_local > $this->_max)) { 946 $this->_error = true; 947 $this->_input = $input; 948 $this->_local = $local; 949 $valid = false; 950 } 951 } 952 return $valid; 953 } 954 955 /** 956 * Generate string to save setting value to file according to $fmt 957 * 958 * @param string $var name of variable 959 * @param string $fmt save format 960 * @return string 961 */ 962 function out($var, $fmt='php') { 963 964 if ($this->is_protected()) return ''; 965 if (is_null($this->_local) || ($this->_default == $this->_local)) return ''; 966 967 $out = ''; 968 969 if ($fmt=='php') { 970 $local = $this->_local === '' ? "''" : $this->_local; 971 $out .= '$'.$var."['".$this->_out_key()."'] = ".$local.";\n"; 972 } 973 974 return $out; 975 } 976 } 977 } 978 979 if (!class_exists('setting_numericopt')) { 980 /** 981 * Class setting_numericopt 982 */ 983 class setting_numericopt extends setting_numeric { 984 // just allow an empty config 985 var $_pattern = '/^(|[-]?[0-9]+(?:[-+*][0-9]+)*)$/'; 986 987 988 /** 989 * Empty string is valid for numericopt 990 * 991 * @param mixed $input 992 * 993 * @return bool 994 */ 995 function update($input) { 996 if ($input === '') { 997 return true; 998 } 999 1000 return parent::update($input); 1001 } 1002 } 1003 } 1004 1005 if (!class_exists('setting_onoff')) { 1006 /** 1007 * Class setting_onoff 1008 */ 1009 class setting_onoff extends setting_numeric { 1010 /** 1011 * Build html for label and input of setting 1012 * 1013 * @param admin_plugin_config $plugin object of config plugin 1014 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 1015 * @return string[] with content array(string $label_html, string $input_html) 1016 */ 1017 function html(admin_plugin_config $plugin, $echo = false) { 1018 $disable = ''; 1019 1020 if ($this->is_protected()) { 1021 $value = $this->_protected; 1022 $disable = ' disabled="disabled"'; 1023 } else { 1024 $value = is_null($this->_local) ? $this->_default : $this->_local; 1025 } 1026 1027 $key = htmlspecialchars($this->_key); 1028 $checked = ($value) ? ' checked="checked"' : ''; 1029 1030 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 1031 $input = '<div class="input"><input id="config___'.$key.'" name="config['.$key.']" type="checkbox" class="checkbox" value="1"'.$checked.$disable.'/></div>'; 1032 return array($label,$input); 1033 } 1034 1035 /** 1036 * update changed setting with user provided value $input 1037 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 1038 * - if changed value passes error check, set $this->_local to the new value 1039 * 1040 * @param mixed $input the new value 1041 * @return boolean true if changed, false otherwise (also on error) 1042 */ 1043 function update($input) { 1044 if ($this->is_protected()) return false; 1045 1046 $input = ($input) ? 1 : 0; 1047 $value = is_null($this->_local) ? $this->_default : $this->_local; 1048 if ($value == $input) return false; 1049 1050 $this->_local = $input; 1051 return true; 1052 } 1053 } 1054 } 1055 1056 if (!class_exists('setting_multichoice')) { 1057 /** 1058 * Class setting_multichoice 1059 */ 1060 class setting_multichoice extends setting_string { 1061 var $_choices = array(); 1062 var $lang; //some custom language strings are stored in setting 1063 1064 /** 1065 * Build html for label and input of setting 1066 * 1067 * @param admin_plugin_config $plugin object of config plugin 1068 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 1069 * @return string[] with content array(string $label_html, string $input_html) 1070 */ 1071 function html(admin_plugin_config $plugin, $echo = false) { 1072 $disable = ''; 1073 $nochoice = ''; 1074 1075 if ($this->is_protected()) { 1076 $value = $this->_protected; 1077 $disable = ' disabled="disabled"'; 1078 } else { 1079 $value = is_null($this->_local) ? $this->_default : $this->_local; 1080 } 1081 1082 // ensure current value is included 1083 if (!in_array($value, $this->_choices)) { 1084 $this->_choices[] = $value; 1085 } 1086 // disable if no other choices 1087 if (!$this->is_protected() && count($this->_choices) <= 1) { 1088 $disable = ' disabled="disabled"'; 1089 $nochoice = $plugin->getLang('nochoice'); 1090 } 1091 1092 $key = htmlspecialchars($this->_key); 1093 1094 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 1095 1096 $input = "<div class=\"input\">\n"; 1097 $input .= '<select class="edit" id="config___'.$key.'" name="config['.$key.']"'.$disable.'>'."\n"; 1098 foreach ($this->_choices as $choice) { 1099 $selected = ($value == $choice) ? ' selected="selected"' : ''; 1100 $option = $plugin->getLang($this->_key.'_o_'.$choice); 1101 if (!$option && isset($this->lang[$this->_key.'_o_'.$choice])) $option = $this->lang[$this->_key.'_o_'.$choice]; 1102 if (!$option) $option = $choice; 1103 1104 $choice = htmlspecialchars($choice); 1105 $option = htmlspecialchars($option); 1106 $input .= ' <option value="'.$choice.'"'.$selected.' >'.$option.'</option>'."\n"; 1107 } 1108 $input .= "</select> $nochoice \n"; 1109 $input .= "</div>\n"; 1110 1111 return array($label,$input); 1112 } 1113 1114 /** 1115 * update changed setting with user provided value $input 1116 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 1117 * - if changed value passes error check, set $this->_local to the new value 1118 * 1119 * @param mixed $input the new value 1120 * @return boolean true if changed, false otherwise (also on error) 1121 */ 1122 function update($input) { 1123 if (is_null($input)) return false; 1124 if ($this->is_protected()) return false; 1125 1126 $value = is_null($this->_local) ? $this->_default : $this->_local; 1127 if ($value == $input) return false; 1128 1129 if (!in_array($input, $this->_choices)) return false; 1130 1131 $this->_local = $input; 1132 return true; 1133 } 1134 } 1135 } 1136 1137 1138 if (!class_exists('setting_dirchoice')) { 1139 /** 1140 * Class setting_dirchoice 1141 */ 1142 class setting_dirchoice extends setting_multichoice { 1143 1144 var $_dir = ''; 1145 1146 /** 1147 * Receives current values for the setting $key 1148 * 1149 * @param mixed $default default setting value 1150 * @param mixed $local local setting value 1151 * @param mixed $protected protected setting value 1152 */ 1153 function initialize($default,$local,$protected) { 1154 1155 // populate $this->_choices with a list of directories 1156 $list = array(); 1157 1158 if ($dh = @opendir($this->_dir)) { 1159 while (false !== ($entry = readdir($dh))) { 1160 if ($entry == '.' || $entry == '..') continue; 1161 if ($this->_pattern && !preg_match($this->_pattern,$entry)) continue; 1162 1163 $file = (is_link($this->_dir.$entry)) ? readlink($this->_dir.$entry) : $this->_dir.$entry; 1164 if (is_dir($file)) $list[] = $entry; 1165 } 1166 closedir($dh); 1167 } 1168 sort($list); 1169 $this->_choices = $list; 1170 1171 parent::initialize($default,$local,$protected); 1172 } 1173 } 1174 } 1175 1176 1177 if (!class_exists('setting_hidden')) { 1178 /** 1179 * Class setting_hidden 1180 */ 1181 class setting_hidden extends setting { 1182 // Used to explicitly ignore a setting in the configuration manager. 1183 } 1184 } 1185 1186 if (!class_exists('setting_fieldset')) { 1187 /** 1188 * Class setting_fieldset 1189 */ 1190 class setting_fieldset extends setting { 1191 // A do-nothing class used to detect the 'fieldset' type. 1192 // Used to start a new settings "display-group". 1193 } 1194 } 1195 1196 if (!class_exists('setting_undefined')) { 1197 /** 1198 * Class setting_undefined 1199 */ 1200 class setting_undefined extends setting_hidden { 1201 // A do-nothing class used to detect settings with no metadata entry. 1202 // Used internaly to hide undefined settings, and generate the undefined settings list. 1203 } 1204 } 1205 1206 if (!class_exists('setting_no_class')) { 1207 /** 1208 * Class setting_no_class 1209 */ 1210 class setting_no_class extends setting_undefined { 1211 // A do-nothing class used to detect settings with a missing setting class. 1212 // Used internaly to hide undefined settings, and generate the undefined settings list. 1213 } 1214 } 1215 1216 if (!class_exists('setting_no_default')) { 1217 /** 1218 * Class setting_no_default 1219 */ 1220 class setting_no_default extends setting_undefined { 1221 // A do-nothing class used to detect settings with no default value. 1222 // Used internaly to hide undefined settings, and generate the undefined settings list. 1223 } 1224 } 1225 1226 if (!class_exists('setting_multicheckbox')) { 1227 /** 1228 * Class setting_multicheckbox 1229 */ 1230 class setting_multicheckbox extends setting_string { 1231 1232 var $_choices = array(); 1233 var $_combine = array(); 1234 var $_other = 'always'; 1235 1236 /** 1237 * update changed setting with user provided value $input 1238 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 1239 * - if changed value passes error check, set $this->_local to the new value 1240 * 1241 * @param mixed $input the new value 1242 * @return boolean true if changed, false otherwise (also on error) 1243 */ 1244 function update($input) { 1245 if ($this->is_protected()) return false; 1246 1247 // split any combined values + convert from array to comma separated string 1248 $input = ($input) ? $input : array(); 1249 $input = $this->_array2str($input); 1250 1251 $value = is_null($this->_local) ? $this->_default : $this->_local; 1252 if ($value == $input) return false; 1253 1254 if ($this->_pattern && !preg_match($this->_pattern,$input)) { 1255 $this->_error = true; 1256 $this->_input = $input; 1257 return false; 1258 } 1259 1260 $this->_local = $input; 1261 return true; 1262 } 1263 1264 /** 1265 * Build html for label and input of setting 1266 * 1267 * @param admin_plugin_config $plugin object of config plugin 1268 * @param bool $echo true: show input value, when error occurred, otherwise the stored setting 1269 * @return string[] with content array(string $label_html, string $input_html) 1270 */ 1271 function html(admin_plugin_config $plugin, $echo=false) { 1272 1273 $disable = ''; 1274 1275 if ($this->is_protected()) { 1276 $value = $this->_protected; 1277 $disable = 'disabled="disabled"'; 1278 } else { 1279 if ($echo && $this->_error) { 1280 $value = $this->_input; 1281 } else { 1282 $value = is_null($this->_local) ? $this->_default : $this->_local; 1283 } 1284 } 1285 1286 $key = htmlspecialchars($this->_key); 1287 1288 // convert from comma separated list into array + combine complimentary actions 1289 $value = $this->_str2array($value); 1290 $default = $this->_str2array($this->_default); 1291 1292 $input = ''; 1293 foreach ($this->_choices as $choice) { 1294 $idx = array_search($choice, $value); 1295 $idx_default = array_search($choice,$default); 1296 1297 $checked = ($idx !== false) ? 'checked="checked"' : ''; 1298 1299 // @todo ideally this would be handled using a second class of "default" 1300 $class = (($idx !== false) == (false !== $idx_default)) ? " selectiondefault" : ""; 1301 1302 $prompt = ($plugin->getLang($this->_key.'_'.$choice) ? 1303 $plugin->getLang($this->_key.'_'.$choice) : htmlspecialchars($choice)); 1304 1305 $input .= '<div class="selection'.$class.'">'."\n"; 1306 $input .= '<label for="config___'.$key.'_'.$choice.'">'.$prompt."</label>\n"; 1307 $input .= '<input id="config___'.$key.'_'.$choice.'" name="config['.$key.'][]" type="checkbox" class="checkbox" value="'.$choice.'" '.$disable.' '.$checked."/>\n"; 1308 $input .= "</div>\n"; 1309 1310 // remove this action from the disabledactions array 1311 if ($idx !== false) unset($value[$idx]); 1312 if ($idx_default !== false) unset($default[$idx_default]); 1313 } 1314 1315 // handle any remaining values 1316 if ($this->_other != 'never'){ 1317 $other = join(',',$value); 1318 // test equivalent to ($this->_other == 'always' || ($other && $this->_other == 'exists') 1319 // use != 'exists' rather than == 'always' to ensure invalid values default to 'always' 1320 if ($this->_other != 'exists' || $other) { 1321 1322 $class = ((count($default) == count($value)) && (count($value) == count(array_intersect($value,$default)))) ? 1323 " selectiondefault" : ""; 1324 1325 $input .= '<div class="other'.$class.'">'."\n"; 1326 $input .= '<label for="config___'.$key.'_other">'.$plugin->getLang($key.'_other')."</label>\n"; 1327 $input .= '<input id="config___'.$key.'_other" name="config['.$key.'][other]" type="text" class="edit" value="'.htmlspecialchars($other).'" '.$disable." />\n"; 1328 $input .= "</div>\n"; 1329 } 1330 } 1331 $label = '<label>'.$this->prompt($plugin).'</label>'; 1332 return array($label,$input); 1333 } 1334 1335 /** 1336 * convert comma separated list to an array and combine any complimentary values 1337 * 1338 * @param string $str 1339 * @return array 1340 */ 1341 function _str2array($str) { 1342 $array = explode(',',$str); 1343 1344 if (!empty($this->_combine)) { 1345 foreach ($this->_combine as $key => $combinators) { 1346 $idx = array(); 1347 foreach ($combinators as $val) { 1348 if (($idx[] = array_search($val, $array)) === false) break; 1349 } 1350 1351 if (count($idx) && $idx[count($idx)-1] !== false) { 1352 foreach ($idx as $i) unset($array[$i]); 1353 $array[] = $key; 1354 } 1355 } 1356 } 1357 1358 return $array; 1359 } 1360 1361 /** 1362 * convert array of values + other back to a comma separated list, incl. splitting any combined values 1363 * 1364 * @param array $input 1365 * @return string 1366 */ 1367 function _array2str($input) { 1368 1369 // handle other 1370 $other = trim($input['other']); 1371 $other = !empty($other) ? explode(',',str_replace(' ','',$input['other'])) : array(); 1372 unset($input['other']); 1373 1374 $array = array_unique(array_merge($input, $other)); 1375 1376 // deconstruct any combinations 1377 if (!empty($this->_combine)) { 1378 foreach ($this->_combine as $key => $combinators) { 1379 1380 $idx = array_search($key,$array); 1381 if ($idx !== false) { 1382 unset($array[$idx]); 1383 $array = array_merge($array, $combinators); 1384 } 1385 } 1386 } 1387 1388 return join(',',array_unique($array)); 1389 } 1390 } 1391 } 1392 1393 if (!class_exists('setting_regex')){ 1394 /** 1395 * Class setting_regex 1396 */ 1397 class setting_regex extends setting_string { 1398 1399 var $_delimiter = '/'; // regex delimiter to be used in testing input 1400 var $_pregflags = 'ui'; // regex pattern modifiers to be used in testing input 1401 1402 /** 1403 * update changed setting with user provided value $input 1404 * - if changed value fails error check, save it to $this->_input (to allow echoing later) 1405 * - if changed value passes error check, set $this->_local to the new value 1406 * 1407 * @param mixed $input the new value 1408 * @return boolean true if changed, false otherwise (incl. on error) 1409 */ 1410 function update($input) { 1411 1412 // let parent do basic checks, value, not changed, etc. 1413 $local = $this->_local; 1414 if (!parent::update($input)) return false; 1415 $this->_local = $local; 1416 1417 // see if the regex compiles and runs (we don't check for effectiveness) 1418 $regex = $this->_delimiter . $input . $this->_delimiter . $this->_pregflags; 1419 $lastError = error_get_last(); 1420 @preg_match($regex,'testdata'); 1421 if (preg_last_error() != PREG_NO_ERROR || error_get_last() != $lastError) { 1422 $this->_input = $input; 1423 $this->_error = true; 1424 return false; 1425 } 1426 1427 $this->_local = $input; 1428 return true; 1429 } 1430 } 1431 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body