PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/wiki/lib/plugins/config/settings/config.class.php

https://bitbucket.org/mariocesar/solmujeres
PHP | 987 lines | 679 code | 198 blank | 110 comment | 162 complexity | ec217ec83a6620816372695cc271396a MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause
  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. if (!class_exists('configuration')) {
  9. class configuration {
  10. var $_name = 'conf'; // name of the config variable found in the files (overridden by $config['varname'])
  11. var $_format = 'php'; // format of the config file, supported formats - php (overridden by $config['format'])
  12. var $_heading = ''; // heading string written at top of config file - don't include comment indicators
  13. var $_loaded = false; // set to true after configuration files are loaded
  14. var $_metadata = array(); // holds metadata describing the settings
  15. var $setting = array(); // array of setting objects
  16. var $locked = false; // configuration is considered locked if it can't be updated
  17. // configuration filenames
  18. var $_default_files = array();
  19. var $_local_files = array(); // updated configuration is written to the first file
  20. var $_protected_files = array();
  21. var $_plugin_list = null;
  22. /**
  23. * constructor
  24. */
  25. function configuration($datafile) {
  26. global $conf, $config_cascade;
  27. if (!@file_exists($datafile)) {
  28. msg('No configuration metadata found at - '.htmlspecialchars($datafile),-1);
  29. return;
  30. }
  31. include($datafile);
  32. if (isset($config['varname'])) $this->_name = $config['varname'];
  33. if (isset($config['format'])) $this->_format = $config['format'];
  34. if (isset($config['heading'])) $this->_heading = $config['heading'];
  35. $this->_default_files = $config_cascade['main']['default'];
  36. $this->_local_files = $config_cascade['main']['local'];
  37. $this->_protected_files = $config_cascade['main']['protected'];
  38. # if (isset($file['default'])) $this->_default_file = $file['default'];
  39. # if (isset($file['local'])) $this->_local_file = $file['local'];
  40. # if (isset($file['protected'])) $this->_protected_file = $file['protected'];
  41. $this->locked = $this->_is_locked();
  42. $this->_metadata = array_merge($meta, $this->get_plugintpl_metadata($conf['template']));
  43. $this->retrieve_settings();
  44. }
  45. function retrieve_settings() {
  46. global $conf;
  47. $no_default_check = array('setting_fieldset', 'setting_undefined', 'setting_no_class');
  48. if (!$this->_loaded) {
  49. $default = array_merge($this->get_plugintpl_default($conf['template']), $this->_read_config_group($this->_default_files));
  50. $local = $this->_read_config_group($this->_local_files);
  51. $protected = $this->_read_config_group($this->_protected_files);
  52. $keys = array_merge(array_keys($this->_metadata),array_keys($default), array_keys($local), array_keys($protected));
  53. $keys = array_unique($keys);
  54. foreach ($keys as $key) {
  55. if (isset($this->_metadata[$key])) {
  56. $class = $this->_metadata[$key][0];
  57. $class = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting';
  58. if ($class=='setting') {
  59. $this->setting[] = new setting_no_class($key,$param);
  60. }
  61. $param = $this->_metadata[$key];
  62. array_shift($param);
  63. } else {
  64. $class = 'setting_undefined';
  65. $param = NULL;
  66. }
  67. if (!in_array($class, $no_default_check) && !isset($default[$key])) {
  68. $this->setting[] = new setting_no_default($key,$param);
  69. }
  70. $this->setting[$key] = new $class($key,$param);
  71. $this->setting[$key]->initialize($default[$key],$local[$key],$protected[$key]);
  72. }
  73. $this->_loaded = true;
  74. }
  75. }
  76. function save_settings($id, $header='', $backup=true) {
  77. global $conf;
  78. if ($this->locked) return false;
  79. # $file = eval('return '.$this->_local_file.';');
  80. $file = $this->_local_files[0];
  81. // backup current file (remove any existing backup)
  82. if (@file_exists($file) && $backup) {
  83. if (@file_exists($file.'.bak')) @unlink($file.'.bak');
  84. if (!io_rename($file, $file.'.bak')) return false;
  85. }
  86. if (!$fh = @fopen($file, 'wb')) {
  87. io_rename($file.'.bak', $file); // problem opening, restore the backup
  88. return false;
  89. }
  90. if (empty($header)) $header = $this->_heading;
  91. $out = $this->_out_header($id,$header);
  92. foreach ($this->setting as $setting) {
  93. $out .= $setting->out($this->_name, $this->_format);
  94. }
  95. $out .= $this->_out_footer();
  96. @fwrite($fh, $out);
  97. fclose($fh);
  98. if($conf['fperm']) chmod($file, $conf['fperm']);
  99. return true;
  100. }
  101. function _read_config_group($files) {
  102. $config = array();
  103. foreach ($files as $file) {
  104. $config = array_merge($config, $this->_read_config($file));
  105. }
  106. return $config;
  107. }
  108. /**
  109. * return an array of config settings
  110. */
  111. function _read_config($file) {
  112. if (!$file) return array();
  113. $config = array();
  114. # $file = eval('return '.$file.';');
  115. if ($this->_format == 'php') {
  116. if(@file_exists($file)){
  117. $contents = @php_strip_whitespace($file);
  118. }else{
  119. $contents = '';
  120. }
  121. $pattern = '/\$'.$this->_name.'\[[\'"]([^=]+)[\'"]\] ?= ?(.*?);(?=[^;]*(?:\$'.$this->_name.'|@include|$))/s';
  122. $matches=array();
  123. preg_match_all($pattern,$contents,$matches,PREG_SET_ORDER);
  124. for ($i=0; $i<count($matches); $i++) {
  125. // correct issues with the incoming data
  126. // FIXME ... for now merge multi-dimensional array indices using ____
  127. $key = preg_replace('/.\]\[./',CM_KEYMARKER,$matches[$i][1]);
  128. // remove quotes from quoted strings & unescape escaped data
  129. $value = preg_replace('/^(\'|")(.*)(?<!\\\\)\1$/s','$2',$matches[$i][2]);
  130. $value = strtr($value, array('\\\\'=>'\\','\\\''=>'\'','\\"'=>'"'));
  131. $config[$key] = $value;
  132. }
  133. }
  134. return $config;
  135. }
  136. function _out_header($id, $header) {
  137. $out = '';
  138. if ($this->_format == 'php') {
  139. $out .= '<'.'?php'."\n".
  140. "/*\n".
  141. " * ".$header." \n".
  142. " * Auto-generated by ".$id." plugin \n".
  143. " * Run for user: ".$_SERVER['REMOTE_USER']."\n".
  144. " * Date: ".date('r')."\n".
  145. " */\n\n";
  146. }
  147. return $out;
  148. }
  149. function _out_footer() {
  150. $out = '';
  151. if ($this->_format == 'php') {
  152. # if ($this->_protected_file) {
  153. # $out .= "\n@include(".$this->_protected_file.");\n";
  154. # }
  155. $out .= "\n// end auto-generated content\n";
  156. }
  157. return $out;
  158. }
  159. // configuration is considered locked if there is no local settings filename
  160. // or the directory its in is not writable or the file exists and is not writable
  161. function _is_locked() {
  162. if (!$this->_local_files) return true;
  163. # $local = eval('return '.$this->_local_file.';');
  164. $local = $this->_local_files[0];
  165. if (!is_writable(dirname($local))) return true;
  166. if (@file_exists($local) && !is_writable($local)) return true;
  167. return false;
  168. }
  169. /**
  170. * not used ... conf's contents are an array!
  171. * reduce any multidimensional settings to one dimension using CM_KEYMARKER
  172. */
  173. function _flatten($conf,$prefix='') {
  174. $out = array();
  175. foreach($conf as $key => $value) {
  176. if (!is_array($value)) {
  177. $out[$prefix.$key] = $value;
  178. continue;
  179. }
  180. $tmp = $this->_flatten($value,$prefix.$key.CM_KEYMARKER);
  181. $out = array_merge($out,$tmp);
  182. }
  183. return $out;
  184. }
  185. function get_plugin_list() {
  186. if (is_null($this->_plugin_list)) {
  187. $list = plugin_list('',true); // all plugins, including disabled ones
  188. // remove this plugin from the list
  189. $idx = array_search('config',$list);
  190. unset($list[$idx]);
  191. trigger_event('PLUGIN_CONFIG_PLUGINLIST',$list);
  192. $this->_plugin_list = $list;
  193. }
  194. return $this->_plugin_list;
  195. }
  196. /**
  197. * load metadata for plugin and template settings
  198. */
  199. function get_plugintpl_metadata($tpl){
  200. $file = '/conf/metadata.php';
  201. $class = '/conf/settings.class.php';
  202. $metadata = array();
  203. foreach ($this->get_plugin_list() as $plugin) {
  204. $plugin_dir = plugin_directory($plugin);
  205. if (@file_exists(DOKU_PLUGIN.$plugin_dir.$file)){
  206. $meta = array();
  207. @include(DOKU_PLUGIN.$plugin_dir.$file);
  208. @include(DOKU_PLUGIN.$plugin_dir.$class);
  209. if (!empty($meta)) {
  210. $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'] = array('fieldset');
  211. }
  212. foreach ($meta as $key => $value){
  213. if ($value[0]=='fieldset') { continue; } //plugins only get one fieldset
  214. $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value;
  215. }
  216. }
  217. }
  218. // the same for the active template
  219. if (@file_exists(DOKU_TPLINC.$file)){
  220. $meta = array();
  221. @include(DOKU_TPLINC.$file);
  222. @include(DOKU_TPLINC.$class);
  223. if (!empty($meta)) {
  224. $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] = array('fieldset');
  225. }
  226. foreach ($meta as $key => $value){
  227. if ($value[0]=='fieldset') { continue; } //template only gets one fieldset
  228. $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value;
  229. }
  230. }
  231. return $metadata;
  232. }
  233. /**
  234. * load default settings for plugins and templates
  235. */
  236. function get_plugintpl_default($tpl){
  237. $file = '/conf/default.php';
  238. $default = array();
  239. foreach ($this->get_plugin_list() as $plugin) {
  240. $plugin_dir = plugin_directory($plugin);
  241. if (@file_exists(DOKU_PLUGIN.$plugin_dir.$file)){
  242. $conf = array();
  243. @include(DOKU_PLUGIN.$plugin_dir.$file);
  244. foreach ($conf as $key => $value){
  245. $default['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value;
  246. }
  247. }
  248. }
  249. // the same for the active template
  250. if (@file_exists(DOKU_TPLINC.$file)){
  251. $conf = array();
  252. @include(DOKU_TPLINC.$file);
  253. foreach ($conf as $key => $value){
  254. $default['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value;
  255. }
  256. }
  257. return $default;
  258. }
  259. }
  260. }
  261. if (!class_exists('setting')) {
  262. class setting {
  263. var $_key = '';
  264. var $_default = NULL;
  265. var $_local = NULL;
  266. var $_protected = NULL;
  267. var $_pattern = '';
  268. var $_error = false; // only used by those classes which error check
  269. var $_input = NULL; // only used by those classes which error check
  270. function setting($key, $params=NULL) {
  271. $this->_key = $key;
  272. if (is_array($params)) {
  273. foreach($params as $property => $value) {
  274. $this->$property = $value;
  275. }
  276. }
  277. }
  278. /**
  279. * receives current values for the setting $key
  280. */
  281. function initialize($default, $local, $protected) {
  282. if (isset($default)) $this->_default = $default;
  283. if (isset($local)) $this->_local = $local;
  284. if (isset($protected)) $this->_protected = $protected;
  285. }
  286. /**
  287. * update setting with user provided value $input
  288. * if value fails error check, save it
  289. *
  290. * @return true if changed, false otherwise (incl. on error)
  291. */
  292. function update($input) {
  293. if (is_null($input)) return false;
  294. if ($this->is_protected()) return false;
  295. $value = is_null($this->_local) ? $this->_default : $this->_local;
  296. if ($value == $input) return false;
  297. if ($this->_pattern && !preg_match($this->_pattern,$input)) {
  298. $this->_error = true;
  299. $this->_input = $input;
  300. return false;
  301. }
  302. $this->_local = $input;
  303. return true;
  304. }
  305. /**
  306. * @return array(string $label_html, string $input_html)
  307. */
  308. function html(&$plugin, $echo=false) {
  309. $value = '';
  310. $disable = '';
  311. if ($this->is_protected()) {
  312. $value = $this->_protected;
  313. $disable = 'disabled="disabled"';
  314. } else {
  315. if ($echo && $this->_error) {
  316. $value = $this->_input;
  317. } else {
  318. $value = is_null($this->_local) ? $this->_default : $this->_local;
  319. }
  320. }
  321. $key = htmlspecialchars($this->_key);
  322. $value = htmlspecialchars($value);
  323. $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
  324. $input = '<textarea rows="3" cols="40" id="config___'.$key.'" name="config['.$key.']" class="edit" '.$disable.'>'.$value.'</textarea>';
  325. return array($label,$input);
  326. }
  327. /**
  328. * generate string to save setting value to file according to $fmt
  329. */
  330. function out($var, $fmt='php') {
  331. if ($this->is_protected()) return '';
  332. if (is_null($this->_local) || ($this->_default == $this->_local)) return '';
  333. $out = '';
  334. if ($fmt=='php') {
  335. // translation string needs to be improved FIXME
  336. $tr = array("\n"=>'\n', "\r"=>'\r', "\t"=>'\t', "\\" => '\\\\', "'" => '\\\'');
  337. $tr = array("\\" => '\\\\', "'" => '\\\'');
  338. $out = '$'.$var."['".$this->_out_key()."'] = '".strtr($this->_local, $tr)."';\n";
  339. }
  340. return $out;
  341. }
  342. function prompt(&$plugin) {
  343. $prompt = $plugin->getLang($this->_key);
  344. if (!$prompt) $prompt = htmlspecialchars(str_replace(array('____','_'),' ',$this->_key));
  345. return $prompt;
  346. }
  347. function is_protected() { return !is_null($this->_protected); }
  348. function is_default() { return !$this->is_protected() && is_null($this->_local); }
  349. function error() { return $this->_error; }
  350. function _out_key($pretty=false) {
  351. if($pretty){
  352. return str_replace(CM_KEYMARKER,"&raquo;",$this->_key);
  353. }else{
  354. return str_replace(CM_KEYMARKER,"']['",$this->_key);
  355. }
  356. }
  357. }
  358. }
  359. if (!class_exists('setting_string')) {
  360. class setting_string extends setting {
  361. function html(&$plugin, $echo=false) {
  362. $value = '';
  363. $disable = '';
  364. if ($this->is_protected()) {
  365. $value = $this->_protected;
  366. $disable = 'disabled="disabled"';
  367. } else {
  368. if ($echo && $this->_error) {
  369. $value = $this->_input;
  370. } else {
  371. $value = is_null($this->_local) ? $this->_default : $this->_local;
  372. }
  373. }
  374. $key = htmlspecialchars($this->_key);
  375. $value = htmlspecialchars($value);
  376. $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
  377. $input = '<input id="config___'.$key.'" name="config['.$key.']" type="text" class="edit" value="'.$value.'" '.$disable.'/>';
  378. return array($label,$input);
  379. }
  380. }
  381. }
  382. if (!class_exists('setting_password')) {
  383. class setting_password extends setting_string {
  384. var $_code = 'plain'; // mechanism to be used to obscure passwords
  385. function update($input) {
  386. if ($this->is_protected()) return false;
  387. if (!$input) return false;
  388. if ($this->_pattern && !preg_match($this->_pattern,$input)) {
  389. $this->_error = true;
  390. $this->_input = $input;
  391. return false;
  392. }
  393. $this->_local = conf_encodeString($input,$this->_code);
  394. return true;
  395. }
  396. function html(&$plugin, $echo=false) {
  397. $value = '';
  398. $disable = $this->is_protected() ? 'disabled="disabled"' : '';
  399. $key = htmlspecialchars($this->_key);
  400. $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
  401. $input = '<input id="config___'.$key.'" name="config['.$key.']" autocomplete="off" type="password" class="edit" value="" '.$disable.' />';
  402. return array($label,$input);
  403. }
  404. }
  405. }
  406. if (!class_exists('setting_email')) {
  407. class setting_email extends setting_string {
  408. var $_pattern = '#^\s*(([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)(,\s*([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+))*)?\s*$#i';
  409. }
  410. }
  411. if (!class_exists('setting_richemail')) {
  412. class setting_richemail extends setting_email {
  413. /**
  414. * update setting with user provided value $input
  415. * if value fails error check, save it
  416. *
  417. * @return true if changed, false otherwise (incl. on error)
  418. */
  419. function update($input) {
  420. if (is_null($input)) return false;
  421. if ($this->is_protected()) return false;
  422. $value = is_null($this->_local) ? $this->_default : $this->_local;
  423. if ($value == $input) return false;
  424. // replace variables with pseudo values
  425. $test = $input;
  426. $test = str_replace('@USER@','joe',$test);
  427. $test = str_replace('@NAME@','Joe Schmoe',$test);
  428. $test = str_replace('@MAIL@','joe@example.com',$test);
  429. // now only check the address part
  430. if(preg_match('#(.*?)<(.*?)>#',$test,$matches)){
  431. $text = trim($matches[1]);
  432. $addr = $matches[2];
  433. }else{
  434. $addr = $test;
  435. }
  436. if ($this->_pattern && !preg_match($this->_pattern,$addr)) {
  437. $this->_error = true;
  438. $this->_input = $input;
  439. return false;
  440. }
  441. $this->_local = $input;
  442. return true;
  443. }
  444. }
  445. }
  446. if (!class_exists('setting_numeric')) {
  447. class setting_numeric extends setting_string {
  448. // This allows for many PHP syntax errors...
  449. // var $_pattern = '/^[-+\/*0-9 ]*$/';
  450. // much more restrictive, but should eliminate syntax errors.
  451. var $_pattern = '/^[-]?[0-9]+(?:[-+*][0-9]+)*$/';
  452. //FIXME - make the numeric error checking better.
  453. function out($var, $fmt='php') {
  454. if ($this->is_protected()) return '';
  455. if (is_null($this->_local) || ($this->_default == $this->_local)) return '';
  456. $out = '';
  457. if ($fmt=='php') {
  458. $out .= '$'.$var."['".$this->_out_key()."'] = ".$this->_local.";\n";
  459. }
  460. return $out;
  461. }
  462. }
  463. }
  464. if (!class_exists('setting_numericopt')) {
  465. class setting_numericopt extends setting_numeric {
  466. // just allow an empty config
  467. var $_pattern = '/^(|[-]?[0-9]+(?:[-+*][0-9]+)*)$/';
  468. }
  469. }
  470. if (!class_exists('setting_onoff')) {
  471. class setting_onoff extends setting_numeric {
  472. function html(&$plugin) {
  473. $value = '';
  474. $disable = '';
  475. if ($this->is_protected()) {
  476. $value = $this->_protected;
  477. $disable = ' disabled="disabled"';
  478. } else {
  479. $value = is_null($this->_local) ? $this->_default : $this->_local;
  480. }
  481. $key = htmlspecialchars($this->_key);
  482. $checked = ($value) ? ' checked="checked"' : '';
  483. $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
  484. $input = '<div class="input"><input id="config___'.$key.'" name="config['.$key.']" type="checkbox" class="checkbox" value="1"'.$checked.$disable.'/></div>';
  485. return array($label,$input);
  486. }
  487. function update($input) {
  488. if ($this->is_protected()) return false;
  489. $input = ($input) ? 1 : 0;
  490. $value = is_null($this->_local) ? $this->_default : $this->_local;
  491. if ($value == $input) return false;
  492. $this->_local = $input;
  493. return true;
  494. }
  495. }
  496. }
  497. if (!class_exists('setting_multichoice')) {
  498. class setting_multichoice extends setting_string {
  499. var $_choices = array();
  500. function html(&$plugin) {
  501. $value = '';
  502. $disable = '';
  503. $nochoice = '';
  504. if ($this->is_protected()) {
  505. $value = $this->_protected;
  506. $disable = ' disabled="disabled"';
  507. } else {
  508. $value = is_null($this->_local) ? $this->_default : $this->_local;
  509. }
  510. // ensure current value is included
  511. if (!in_array($value, $this->_choices)) {
  512. $this->_choices[] = $value;
  513. }
  514. // disable if no other choices
  515. if (!$this->is_protected() && count($this->_choices) <= 1) {
  516. $disable = ' disabled="disabled"';
  517. $nochoice = $plugin->getLang('nochoice');
  518. }
  519. $key = htmlspecialchars($this->_key);
  520. $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>';
  521. $input = "<div class=\"input\">\n";
  522. $input .= '<select class="edit" id="config___'.$key.'" name="config['.$key.']"'.$disable.'>'."\n";
  523. foreach ($this->_choices as $choice) {
  524. $selected = ($value == $choice) ? ' selected="selected"' : '';
  525. $option = $plugin->getLang($this->_key.'_o_'.$choice);
  526. if (!$option && isset($this->lang[$this->_key.'_o_'.$choice])) $option = $this->lang[$this->_key.'_o_'.$choice];
  527. if (!$option) $option = $choice;
  528. $choice = htmlspecialchars($choice);
  529. $option = htmlspecialchars($option);
  530. $input .= ' <option value="'.$choice.'"'.$selected.' >'.$option.'</option>'."\n";
  531. }
  532. $input .= "</select> $nochoice \n";
  533. $input .= "</div>\n";
  534. return array($label,$input);
  535. }
  536. function update($input) {
  537. if (is_null($input)) return false;
  538. if ($this->is_protected()) return false;
  539. $value = is_null($this->_local) ? $this->_default : $this->_local;
  540. if ($value == $input) return false;
  541. if (!in_array($input, $this->_choices)) return false;
  542. $this->_local = $input;
  543. return true;
  544. }
  545. }
  546. }
  547. if (!class_exists('setting_dirchoice')) {
  548. class setting_dirchoice extends setting_multichoice {
  549. var $_dir = '';
  550. function initialize($default,$local,$protected) {
  551. // populate $this->_choices with a list of directories
  552. $list = array();
  553. if ($dh = @opendir($this->_dir)) {
  554. while (false !== ($entry = readdir($dh))) {
  555. if ($entry == '.' || $entry == '..') continue;
  556. if ($this->_pattern && !preg_match($this->_pattern,$entry)) continue;
  557. $file = (is_link($this->_dir.$entry)) ? readlink($this->_dir.$entry) : $entry;
  558. if (is_dir($this->_dir.$file)) $list[] = $entry;
  559. }
  560. closedir($dh);
  561. }
  562. sort($list);
  563. $this->_choices = $list;
  564. parent::initialize($default,$local,$protected);
  565. }
  566. }
  567. }
  568. if (!class_exists('setting_hidden')) {
  569. class setting_hidden extends setting {
  570. // Used to explicitly ignore a setting in the configuration manager.
  571. }
  572. }
  573. if (!class_exists('setting_fieldset')) {
  574. class setting_fieldset extends setting {
  575. // A do-nothing class used to detect the 'fieldset' type.
  576. // Used to start a new settings "display-group".
  577. }
  578. }
  579. if (!class_exists('setting_undefined')) {
  580. class setting_undefined extends setting_hidden {
  581. // A do-nothing class used to detect settings with no metadata entry.
  582. // Used internaly to hide undefined settings, and generate the undefined settings list.
  583. }
  584. }
  585. if (!class_exists('setting_no_class')) {
  586. class setting_no_class extends setting_undefined {
  587. // A do-nothing class used to detect settings with a missing setting class.
  588. // Used internaly to hide undefined settings, and generate the undefined settings list.
  589. }
  590. }
  591. if (!class_exists('setting_no_default')) {
  592. class setting_no_default extends setting_undefined {
  593. // A do-nothing class used to detect settings with no default value.
  594. // Used internaly to hide undefined settings, and generate the undefined settings list.
  595. }
  596. }
  597. if (!class_exists('setting_multicheckbox')) {
  598. class setting_multicheckbox extends setting_string {
  599. var $_choices = array();
  600. var $_combine = array();
  601. function update($input) {
  602. if ($this->is_protected()) return false;
  603. // split any combined values + convert from array to comma separated string
  604. $input = ($input) ? $input : array();
  605. $input = $this->_array2str($input);
  606. $value = is_null($this->_local) ? $this->_default : $this->_local;
  607. if ($value == $input) return false;
  608. if ($this->_pattern && !preg_match($this->_pattern,$input)) {
  609. $this->_error = true;
  610. $this->_input = $input;
  611. return false;
  612. }
  613. $this->_local = $input;
  614. return true;
  615. }
  616. function html(&$plugin, $echo=false) {
  617. $value = '';
  618. $disable = '';
  619. if ($this->is_protected()) {
  620. $value = $this->_protected;
  621. $disable = 'disabled="disabled"';
  622. } else {
  623. if ($echo && $this->_error) {
  624. $value = $this->_input;
  625. } else {
  626. $value = is_null($this->_local) ? $this->_default : $this->_local;
  627. }
  628. }
  629. $key = htmlspecialchars($this->_key);
  630. // convert from comma separated list into array + combine complimentary actions
  631. $value = $this->_str2array($value);
  632. $default = $this->_str2array($this->_default);
  633. $input = '';
  634. foreach ($this->_choices as $choice) {
  635. $idx = array_search($choice, $value);
  636. $idx_default = array_search($choice,$default);
  637. $checked = ($idx !== false) ? 'checked="checked"' : '';
  638. // ideally this would be handled using a second class of "default", however IE6 does not
  639. // correctly support CSS selectors referencing multiple class names on the same element
  640. // (e.g. .default.selection).
  641. $class = (($idx !== false) == (false !== $idx_default)) ? " selectiondefault" : "";
  642. $prompt = ($plugin->getLang($this->_key.'_'.$choice) ?
  643. $plugin->getLang($this->_key.'_'.$choice) : htmlspecialchars($choice));
  644. $input .= '<div class="selection'.$class.'">'."\n";
  645. $input .= '<label for="config___'.$key.'_'.$choice.'">'.$prompt."</label>\n";
  646. $input .= '<input id="config___'.$key.'_'.$choice.'" name="config['.$key.'][]" type="checkbox" class="checkbox" value="'.$choice.'" '.$disable.' '.$checked."/>\n";
  647. $input .= "</div>\n";
  648. // remove this action from the disabledactions array
  649. if ($idx !== false) unset($value[$idx]);
  650. if ($idx_default !== false) unset($default[$idx_default]);
  651. }
  652. // handle any remaining values
  653. $other = join(',',$value);
  654. $class = (count($default == count($value)) && (count($value) == count(array_intersect($value,$default)))) ?
  655. " selectiondefault" : "";
  656. $input .= '<div class="other'.$class.'">'."\n";
  657. $input .= '<label for="config___'.$key.'_other">'.$plugin->getLang($key.'_other')."</label>\n";
  658. $input .= '<input id="config___'.$key.'_other" name="config['.$key.'][other]" type="text" class="edit" value="'.htmlspecialchars($other).'" '.$disable." />\n";
  659. $input .= "</div>\n";
  660. $label = '<label>'.$this->prompt($plugin).'</label>';
  661. return array($label,$input);
  662. }
  663. /**
  664. * convert comma separated list to an array and combine any complimentary values
  665. */
  666. function _str2array($str) {
  667. $array = explode(',',$str);
  668. if (!empty($this->_combine)) {
  669. foreach ($this->_combine as $key => $combinators) {
  670. $idx = array();
  671. foreach ($combinators as $val) {
  672. if (($idx[] = array_search($val, $array)) === false) break;
  673. }
  674. if (count($idx) && $idx[count($idx)-1] !== false) {
  675. foreach ($idx as $i) unset($array[$i]);
  676. $array[] = $key;
  677. }
  678. }
  679. }
  680. return $array;
  681. }
  682. /**
  683. * convert array of values + other back to a comma separated list, incl. splitting any combined values
  684. */
  685. function _array2str($input) {
  686. // handle other
  687. $other = trim($input['other']);
  688. $other = !empty($other) ? explode(',',str_replace(' ','',$input['other'])) : array();
  689. unset($input['other']);
  690. $array = array_unique(array_merge($input, $other));
  691. // deconstruct any combinations
  692. if (!empty($this->_combine)) {
  693. foreach ($this->_combine as $key => $combinators) {
  694. $idx = array_search($key,$array);
  695. if ($idx !== false) {
  696. unset($array[$idx]);
  697. $array = array_merge($array, $combinators);
  698. }
  699. }
  700. }
  701. return join(',',array_unique($array));
  702. }
  703. }
  704. }
  705. /**
  706. * Provide php_strip_whitespace (php5 function) functionality
  707. *
  708. * @author Chris Smith <chris@jalakai.co.uk>
  709. */
  710. if (!function_exists('php_strip_whitespace')) {
  711. if (function_exists('token_get_all')) {
  712. if (!defined('T_ML_COMMENT')) {
  713. define('T_ML_COMMENT', T_COMMENT);
  714. } else {
  715. define('T_DOC_COMMENT', T_ML_COMMENT);
  716. }
  717. /**
  718. * modified from original
  719. * source Google Groups, php.general, by David Otton
  720. */
  721. function php_strip_whitespace($file) {
  722. if (!@is_readable($file)) return '';
  723. $in = join('',@file($file));
  724. $out = '';
  725. $tokens = token_get_all($in);
  726. foreach ($tokens as $token) {
  727. if (is_string ($token)) {
  728. $out .= $token;
  729. } else {
  730. list ($id, $text) = $token;
  731. switch ($id) {
  732. case T_COMMENT : // fall thru
  733. case T_ML_COMMENT : // fall thru
  734. case T_DOC_COMMENT : // fall thru
  735. case T_WHITESPACE :
  736. break;
  737. default : $out .= $text; break;
  738. }
  739. }
  740. }
  741. return ($out);
  742. }
  743. } else {
  744. function is_whitespace($c) { return (strpos("\t\n\r ",$c) !== false); }
  745. function is_quote($c) { return (strpos("\"'",$c) !== false); }
  746. function is_escaped($s,$i) {
  747. $idx = $i-1;
  748. while(($idx>=0) && ($s{$idx} == '\\')) $idx--;
  749. return (($i - $idx + 1) % 2);
  750. }
  751. function is_commentopen($str, $i) {
  752. if ($str{$i} == '#') return "\n";
  753. if ($str{$i} == '/') {
  754. if ($str{$i+1} == '/') return "\n";
  755. if ($str{$i+1} == '*') return "*/";
  756. }
  757. return false;
  758. }
  759. function php_strip_whitespace($file) {
  760. if (!@is_readable($file)) return '';
  761. $contents = join('',@file($file));
  762. $out = '';
  763. $state = 0;
  764. for ($i=0; $i<strlen($contents); $i++) {
  765. if (!$state && is_whitespace($contents{$i})) continue;
  766. if (!$state && ($c_close = is_commentopen($contents, $i))) {
  767. $c_open_len = ($contents{$i} == '/') ? 2 : 1;
  768. $i = strpos($contents, $c_close, $i+$c_open_len)+strlen($c_close)-1;
  769. continue;
  770. }
  771. $out .= $contents{$i};
  772. if (is_quote($contents{$i})) {
  773. if (($state == $contents{$i}) && !is_escaped($contents, $i)) { $state = 0; continue; }
  774. if (!$state) {$state = $contents{$i}; continue; }
  775. }
  776. }
  777. return $out;
  778. }
  779. }
  780. }