| [ 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 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 // filenames, these will be eval()'d prior to use so maintain any constants in output 22 var $_default_file = ''; 23 var $_local_file = ''; 24 var $_protected_file = ''; 25 26 /** 27 * constructor 28 */ 29 function configuration($datafile) { 30 global $conf; 31 32 if (!@file_exists($datafile)) { 33 msg('No configuration metadata found at - '.htmlspecialchars($datafile),-1); 34 return; 35 } 36 include($datafile); 37 38 if (isset($config['varname'])) $this->_name = $config['varname']; 39 if (isset($config['format'])) $this->_format = $config['format']; 40 if (isset($config['heading'])) $this->_heading = $config['heading']; 41 42 if (isset($file['default'])) $this->_default_file = $file['default']; 43 if (isset($file['local'])) $this->_local_file = $file['local']; 44 if (isset($file['protected'])) $this->_protected_file = $file['protected']; 45 46 $this->locked = $this->_is_locked(); 47 48 $this->_metadata = array_merge($meta, $this->get_plugintpl_metadata($conf['template'])); 49 50 $this->retrieve_settings(); 51 } 52 53 function retrieve_settings() { 54 global $conf; 55 $no_default_check = array('setting_fieldset', 'setting_undefined', 'setting_no_class'); 56 57 if (!$this->_loaded) { 58 $default = array_merge($this->_read_config($this->_default_file), $this->get_plugintpl_default($conf['template'])); 59 $local = $this->_read_config($this->_local_file); 60 $protected = $this->_read_config($this->_protected_file); 61 62 $keys = array_merge(array_keys($this->_metadata),array_keys($default), array_keys($local), array_keys($protected)); 63 $keys = array_unique($keys); 64 65 foreach ($keys as $key) { 66 if (isset($this->_metadata[$key])) { 67 $class = $this->_metadata[$key][0]; 68 $class = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting'; 69 if ($class=='setting') { 70 $this->setting[] = new setting_no_class($key,$param); 71 } 72 73 $param = $this->_metadata[$key]; 74 array_shift($param); 75 } else { 76 $class = 'setting_undefined'; 77 $param = NULL; 78 } 79 80 if (!in_array($class, $no_default_check) && !isset($default[$key])) { 81 $this->setting[] = new setting_no_default($key,$param); 82 } 83 84 $this->setting[$key] = new $class($key,$param); 85 $this->setting[$key]->initialize($default[$key],$local[$key],$protected[$key]); 86 } 87 88 $this->_loaded = true; 89 } 90 } 91 92 function save_settings($id, $header='', $backup=true) { 93 94 if ($this->locked) return false; 95 96 $file = eval('return '.$this->_local_file.';'); 97 98 // backup current file (remove any existing backup) 99 if (@file_exists($file) && $backup) { 100 if (@file_exists($file.'.bak')) @unlink($file.'.bak'); 101 if (!io_rename($file, $file.'.bak')) return false; 102 } 103 104 if (!$fh = @fopen($file, 'wb')) { 105 io_rename($file.'.bak', $file); // problem opening, restore the backup 106 return false; 107 } 108 109 if (empty($header)) $header = $this->_heading; 110 111 $out = $this->_out_header($id,$header); 112 113 foreach ($this->setting as $setting) { 114 $out .= $setting->out($this->_name, $this->_format); 115 } 116 117 $out .= $this->_out_footer(); 118 119 @fwrite($fh, $out); 120 fclose($fh); 121 return true; 122 } 123 124 /** 125 * return an array of config settings 126 */ 127 function _read_config($file) { 128 129 if (!$file) return array(); 130 131 $config = array(); 132 $file = eval('return '.$file.';'); 133 134 if ($this->_format == 'php') { 135 136 if(@file_exists($file)){ 137 $contents = @php_strip_whitespace($file); 138 }else{ 139 $contents = ''; 140 } 141 $pattern = '/\$'.$this->_name.'\[[\'"]([^=]+)[\'"]\] ?= ?(.*?);(?=[^;]*(?:\$'.$this->_name.'|@include|$))/s'; 142 $matches=array(); 143 preg_match_all($pattern,$contents,$matches,PREG_SET_ORDER); 144 145 for ($i=0; $i<count($matches); $i++) { 146 147 // correct issues with the incoming data 148 // FIXME ... for now merge multi-dimensional array indices using ____ 149 $key = preg_replace('/.\]\[./',CM_KEYMARKER,$matches[$i][1]); 150 151 // remove quotes from quoted strings & unescape escaped data 152 $value = preg_replace('/^(\'|")(.*)(?<!\\\\)\1$/s','$2',$matches[$i][2]); 153 $value = strtr($value, array('\\\\'=>'\\','\\\''=>'\'','\\"'=>'"')); 154 155 $config[$key] = $value; 156 } 157 } 158 159 return $config; 160 } 161 162 function _out_header($id, $header) { 163 $out = ''; 164 if ($this->_format == 'php') { 165 $out .= '<'.'?php'."\n". 166 "/*\n". 167 " * ".$header." \n". 168 " * Auto-generated by ".$id." plugin \n". 169 " * Run for user: ".$_SERVER['REMOTE_USER']."\n". 170 " * Date: ".date('r')."\n". 171 " */\n\n"; 172 } 173 174 return $out; 175 } 176 177 function _out_footer() { 178 $out = ''; 179 if ($this->_format == 'php') { 180 if ($this->_protected_file) { 181 $out .= "\n@include(".$this->_protected_file.");\n"; 182 } 183 $out .= "\n// end auto-generated content\n"; 184 } 185 186 return $out; 187 } 188 189 // configuration is considered locked if there is no local settings filename 190 // or the directory its in is not writable or the file exists and is not writable 191 function _is_locked() { 192 if (!$this->_local_file) return true; 193 194 $local = eval('return '.$this->_local_file.';'); 195 196 if (!is_writable(dirname($local))) return true; 197 if (@file_exists($local) && !is_writable($local)) return true; 198 199 return false; 200 } 201 202 /** 203 * not used ... conf's contents are an array! 204 * reduce any multidimensional settings to one dimension using CM_KEYMARKER 205 */ 206 function _flatten($conf,$prefix='') { 207 208 $out = array(); 209 210 foreach($conf as $key => $value) { 211 if (!is_array($value)) { 212 $out[$prefix.$key] = $value; 213 continue; 214 } 215 216 $tmp = $this->_flatten($value,$prefix.$key.CM_KEYMARKER); 217 $out = array_merge($out,$tmp); 218 } 219 220 return $out; 221 } 222 223 /** 224 * load metadata for plugin and template settings 225 */ 226 function get_plugintpl_metadata($tpl){ 227 $file = '/conf/metadata.php'; 228 $class = '/conf/settings.class.php'; 229 $metadata = array(); 230 231 if ($dh = opendir(DOKU_PLUGIN)) { 232 while (false !== ($plugin = readdir($dh))) { 233 if ($plugin == '.' || $plugin == '..' || $plugin == 'tmp' || $plugin == 'config') continue; 234 if (is_file(DOKU_PLUGIN.$plugin)) continue; 235 236 if (@file_exists(DOKU_PLUGIN.$plugin.$file)){ 237 $meta = array(); 238 @include(DOKU_PLUGIN.$plugin.$file); 239 @include(DOKU_PLUGIN.$plugin.$class); 240 if (!empty($meta)) { 241 $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'] = array('fieldset'); 242 } 243 foreach ($meta as $key => $value){ 244 if ($value[0]=='fieldset') { continue; } //plugins only get one fieldset 245 $metadata['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value; 246 } 247 } 248 } 249 closedir($dh); 250 } 251 252 // the same for the active template 253 if (@file_exists(DOKU_TPLINC.$file)){ 254 $meta = array(); 255 @include(DOKU_TPLINC.$file); 256 @include(DOKU_TPLINC.$class); 257 if (!empty($meta)) { 258 $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] = array('fieldset'); 259 } 260 foreach ($meta as $key => $value){ 261 if ($value[0]=='fieldset') { continue; } //template only gets one fieldset 262 $metadata['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value; 263 } 264 } 265 266 return $metadata; 267 } 268 269 /** 270 * load default settings for plugins and templates 271 */ 272 function get_plugintpl_default($tpl){ 273 $file = '/conf/default.php'; 274 $default = array(); 275 276 if ($dh = opendir(DOKU_PLUGIN)) { 277 while (false !== ($plugin = readdir($dh))) { 278 if (@file_exists(DOKU_PLUGIN.$plugin.$file)){ 279 $conf = array(); 280 @include(DOKU_PLUGIN.$plugin.$file); 281 foreach ($conf as $key => $value){ 282 $default['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value; 283 } 284 } 285 } 286 closedir($dh); 287 } 288 289 // the same for the active template 290 if (@file_exists(DOKU_TPLINC.$file)){ 291 $conf = array(); 292 @include(DOKU_TPLINC.$file); 293 foreach ($conf as $key => $value){ 294 $default['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value; 295 } 296 } 297 298 return $default; 299 } 300 301 } 302 } 303 304 if (!class_exists('setting')) { 305 class setting { 306 307 var $_key = ''; 308 var $_default = NULL; 309 var $_local = NULL; 310 var $_protected = NULL; 311 312 var $_pattern = ''; 313 var $_error = false; // only used by those classes which error check 314 var $_input = NULL; // only used by those classes which error check 315 316 function setting($key, $params=NULL) { 317 $this->_key = $key; 318 319 if (is_array($params)) { 320 foreach($params as $property => $value) { 321 $this->$property = $value; 322 } 323 } 324 } 325 326 /** 327 * receives current values for the setting $key 328 */ 329 function initialize($default, $local, $protected) { 330 if (isset($default)) $this->_default = $default; 331 if (isset($local)) $this->_local = $local; 332 if (isset($protected)) $this->_protected = $protected; 333 } 334 335 /** 336 * update setting with user provided value $input 337 * if value fails error check, save it 338 * 339 * @return true if changed, false otherwise (incl. on error) 340 */ 341 function update($input) { 342 if (is_null($input)) return false; 343 if ($this->is_protected()) return false; 344 345 $value = is_null($this->_local) ? $this->_default : $this->_local; 346 if ($value == $input) return false; 347 348 if ($this->_pattern && !preg_match($this->_pattern,$input)) { 349 $this->_error = true; 350 $this->_input = $input; 351 return false; 352 } 353 354 $this->_local = $input; 355 return true; 356 } 357 358 /** 359 * @return array(string $label_html, string $input_html) 360 */ 361 function html(&$plugin, $echo=false) { 362 $value = ''; 363 $disable = ''; 364 365 if ($this->is_protected()) { 366 $value = $this->_protected; 367 $disable = 'disabled="disabled"'; 368 } else { 369 if ($echo && $this->_error) { 370 $value = $this->_input; 371 } else { 372 $value = is_null($this->_local) ? $this->_default : $this->_local; 373 } 374 } 375 376 $key = htmlspecialchars($this->_key); 377 $value = htmlspecialchars($value); 378 379 $label = '<label for="config___'.$key.'">'.$this->prompt($plugin).'</label>'; 380 $input = '<textarea rows="3" cols="40" id="config___'.$key.'" name="config['.$key.']" class="edit" '.$disable.'>'.$value.'</textarea>'; 381 return array($label,$input); 382 } 383 384 /** 385 * generate string to save setting value to file according to $fmt 386 */ 387 function out($var, $fmt='php') { 388 389 if ($this->is_protected()) return ''; 390 if (is_null($this->_local) || ($this->_default == $this->_local)) return ''; 391 392 $out = ''; 393 394 if ($fmt=='php') { 395 // translation string needs to be improved FIXME 396 $tr = array("\n"=>'\n', "\r"=>'\r', "\t"=>'\t', "\\" => '\\\\', "'" => '\\\''); 397 $tr = array("\\" => '\\\\', "'" => '\\\''); 398 399 $out = '$'.$var."['".$this->_out_key()."'] = '".strtr($this->_local, $tr)."';\n"; 400 } 401 402 return $out; 403 } 404 405 function prompt(&$plugin) { 406 $prompt = $plugin->getLang($this->_key); 407 if (!$prompt) $prompt = htmlspecialchars(str_replace(array('____','_'),' ',$this->_key)); 408 return $prompt; 409 } 410 411 function is_protected() { return !is_null($this->_protected); } 412 function is_default() { return !$this->is_protected() && is_null($this->_local); } 413 function error() { return $this->_error; } 414 415 function _out_key($pretty=false) { 416 if($pretty){ 417 return str_replace(