1<?php
2/*
3 *  TxtConfiguration Class and generic setting classes
4 *
5 *  @author     Stephane Chamberland <stephane.chamberland@ec.gc.ca>
6 *
7 *  Extends Configuration Class and generic setting classes by Chris Smith <chris@jalakai.co.uk>
8 *  Deals with TXT (None PHP) config files
9 */
10
11if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
13
14if(!defined('PLUGIN_TXTCONF')) define('PLUGIN_TXTCONF',dirname(__FILE__).'/');
15
16require_once(PLUGIN_TXTCONF.'config0.class.php');
17
18
19if (!class_exists('txtconfiguration') && class_exists('configuration0')) {
20
21  class txtconfiguration extends configuration0 {
22
23    var $_intro = '';
24    var $_format = 'txt';
25    var $_defkeymeta = array('text');      // holds metametadata describing the settings key/name
26    var $_defvalmeta = array('text');      // holds metametadata describing the settings valueblog
27
28   /**
29     *  constructor
30     */
31    function txtconfiguration($datafile) {
32        global $conf;
33
34        if (!@file_exists($datafile)) {
35          //TODO: localize msg
36          msg('No configuration metadata found at - '.htmlspecialchars($datafile),-1);
37          return;
38        }
39        include($datafile);
40
41        //?TODO:? fill $meta with appropriate values
42
43        if (isset($config['intro'])) $this->_intro = $config['intro'];
44
45        if (isset($config['varname'])) $this->_name = $config['varname'];
46        if (isset($config['format'])) $this->_format = $config['format'];
47        if (isset($config['heading'])) $this->_heading = $config['heading'];
48
49        if (isset($file['default'])) $this->_default_file = $file['default'];
50        if (isset($file['local'])) $this->_local_file = $file['local'];
51        if (isset($file['protected'])) $this->_protected_file = $file['protected'];
52
53        if (isset($metameta['key'])) $this->_defkeymeta = $metameta['key'];
54        if (isset($metameta['value'])) $this->_defvalmeta = $metameta['value'];
55
56        $this->locked = $this->_is_locked();
57
58        //$this->_metadata = array_merge($meta, $this->get_plugintpl_metadata($conf['template']));
59
60        $this->retrieve_settings();
61    }
62
63    function retrieve_settings() {
64        global $conf;
65
66        if (!$this->_loaded) {
67          //$default = array_merge($this->_read_config($this->_default_file), $this->get_plugintpl_default($conf['template']));
68          $default = $this->_read_config($this->_default_file);
69          $local = $this->_read_config($this->_local_file);
70          $protected = $this->_read_config($this->_protected_file);
71
72          $keys = array_merge(array_keys($this->_metadata),array_keys($default), array_keys($local), array_keys($protected));
73          $keys = array_unique($keys);
74
75          $class = NULL;
76          $defparam = NULL;
77          if (isset($this->_defvalmeta[0])) {
78            $class = $this->_defvalmeta[0];
79            $defparam = $this->_defvalmeta;
80            array_shift($defparam);
81          }
82          $defclass = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting0';
83
84          foreach ($keys as $key) {
85            if (isset($this->_metadata[$key])) {
86              $class = $this->_metadata[$key][0];
87              //$class = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting';
88              //$param = $this->_metadata[$key];
89              //array_shift($param);
90              //$class = $defclass;
91              $param = $defparam;
92              if ($class && class_exists('setting_'.$class)) {
93                $class = 'setting_'.$class;
94                $param = $this->_metadata[$key];
95                array_shift($param);
96              }
97            } else {
98              //$class = 'setting';
99              //$param = NULL;
100              $class = $defclass;
101              $param = $defparam;
102            }
103
104            $this->setting[$key] = new $class($key,$param);
105            $this->setting[$key]->initialize($default[$key],$local[$key],$protected[$key]);
106          }
107
108          $this->_loaded = true;
109        }
110
111    }
112
113    function add_local_setting($key,$value) {
114        global $conf;
115
116        $defclass = NULL;
117        $defparam = NULL;
118        $class = NULL;
119        if (isset($this->_defvalmeta[0])) {
120            $class = $this->_defvalmeta[0];
121            $defparam = $this->_defvalmeta;
122            array_shift($defparam);
123        }
124        $defclass = ($class && class_exists('setting_'.$class)) ? 'setting_'.$class : 'setting0';
125
126        $class = $defclass;
127        $param = $defparam;
128
129        if (isset($this->_metadata[$key])) {
130            $class = $this->_metadata[$key][0];
131            $param = $defparam;
132            if ($class && class_exists('setting_'.$class)) {
133                $class = 'setting_'.$class;
134                $param = $this->_metadata[$key];
135                array_shift($param);
136            }
137        }
138
139        $this->setting[$key] = new $class($key,$param);
140        $this->setting[$key]->initialize(null,$value,null);
141
142        //TODO: if key already exist, return False
143        return True;
144    }
145
146    /**
147     * return an array of config settings
148     */
149    function _read_config($file) {
150
151      if (!$file) return array();
152
153      $config = array();
154      $file = eval('return '.$file.';');
155
156      if ($this->_format == 'php') {
157
158        $contents = @php_strip_whitespace($file);
159        $pattern = '/\$'.$this->_name.'\[[\'"]([^=]+)[\'"]\] ?= ?(.*?);/';
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$/','$2',$matches[$i][2]);
171          $value = strtr($value, array('\\\\'=>'\\','\\\''=>'\'','\\"'=>'"'));
172
173          $config[$key] = $value;
174        }
175      } else if ($this->_format == 'txt') {
176        //TODO: if confToHash not found, include(confutils.php)
177        //if(!defined('DOKU_CONF')) define('DOKU_CONF',DOKU_INC.'conf/');
178        //require_once(DOKU_INC.'inc/confutils.php');
179        //include();
180        $config = confToHash($file);
181      }
182
183      return $config;
184    }
185
186    function _out_header($id, $header) {
187      $out = '';
188      if ($this->_format == 'php') {
189          $out .= '<'.'?php'."\n".
190                "/*\n".
191                " * ".$header." \n".
192                " * Auto-generated by ".$id." plugin \n".
193                " * Run for user: ".$_SERVER['REMOTE_USER']."\n".
194                " * Date: ".date('r')."\n".
195                " */\n\n";
196      } else if ($this->_format == 'txt') {
197          $out .=
198                "#\n".
199                "# ".$header." \n".
200                "# Auto-generated by ".$id." plugin \n".
201                "# Run for user: ".$_SERVER['REMOTE_USER']."\n".
202                "# Date: ".date('r')."\n".
203                "#\n\n";
204      }
205
206      return $out;
207    }
208
209    function _out_footer() {
210      $out = '';
211      if ($this->_format == 'php') {
212          if ($this->_protected_file) {
213            $out .= "\n@include(".$this->_protected_file.");\n";
214          }
215          $out .= "\n// end auto-generated content\n";
216      } else if ($this->_format == 'txt') {
217          $out .= "\n# end auto-generated content\n";
218      }
219
220      return $out;
221    }
222
223    function html() {
224    }
225
226  }
227}
228
229if (!class_exists('setting_text')  && class_exists('setting0')) {
230  class setting_text extends setting0 {
231
232    /**
233     *  update setting with user provided value $input
234     *  if value fails error check, save it
235     *
236     *  @return true if changed, false otherwise (incl. on error)
237     */
238    function update($input) {
239        if (is_null($input)) return false;
240        if ($this->is_protected()) return false;
241
242        $value = is_null($this->_local) ? $this->_default : $this->_local;
243        if ($value == $input) return false;
244
245        if ($this->_pattern && !preg_match($this->_pattern,$input)) {
246          $this->_error = true;
247          $this->_input = $input;
248          return false;
249        }
250
251        $this->_local = $input;
252        return true;
253    }
254
255    /**
256     *  @return   array(string $label_html, string $input_html)
257     */
258    function html(&$plugin, $echo=false) {
259        $value = '';
260        $defaultstyle = '';
261        $disable = '';
262
263        if ($this->is_protected()) {
264          $value = $this->_protected;
265          $disable = 'disabled="disabled"';
266        } else {
267          if ($echo && $this->_error) {
268            $value = $this->_input;
269          } else {
270            $value = is_null($this->_local) ? $this->_default : $this->_local;
271          }
272        }
273
274        if (is_null($this->_local)) $defaultstyle = 'style="font-weight:bold;"';
275
276        $key = htmlspecialchars($this->_key);
277        $value = htmlspecialchars($value);
278
279        //$label = '<input type="checkbox" name="configbox__'.$key.'" value="1"'.$noerase.'/> <label for="config__'.$key.'">'.$key.'</label>&nbsp;';
280       $label = '<label '.$defaultstyle.' for="config__'.$key.'">'.$key.'</label>&nbsp;';
281        //$input = '<textarea rows="3" cols="80" id="config__'.$key.'" name="config['.$key.']" class="edit" '.$disable.'>'.$value.'</textarea>';
282        $input = '<input id="config__'.$key.'" name="config['.$key.']" type="text" class="edit" size="80" value="'.$value.'" '.$disable.'/>';
283        return array($label,$input);
284    }
285
286    /**
287     *  generate string to save setting value to file according to $fmt
288     */
289    function out($var='config', $fmt='txt') {
290
291      if ($this->is_protected()) return '';
292      if (is_null($this->_local) || ($this->_default == $this->_local)) return '';
293
294      if ($fmt=='php') {
295        // translation string needs to be improved FIXME
296        $tr = array("\n"=>'\n', "\r"=>'\r', "\t"=>'\t', "\\" => '\\\\', "'" => '\\\'');
297        $tr = array("\\" => '\\\\', "'" => '\\\'');
298
299        $out =  '$'.$var."['".$this->_out_key()."'] = '".strtr($this->_local, $tr)."';\n";
300      } else if ($fmt=='txt') {
301        //$value = is_null($this->_local) ? $this->_default : $this->_local;
302        //$out =  $this->_key."\t".$value."\n";
303         $out =  $this->_key."\t".$this->_local."\n";
304     }
305
306      return $out;
307    }
308
309  }
310}
311