Completed
Push — sidebaracl ( 7a112d...7c3e4a )
by Andreas
04:38
created

lib/plugins/config/admin.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Configuration Manager admin plugin
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Christopher Smith <[email protected]>
7
 * @author     Ben Coburn <[email protected]>
8
 */
9
// must be run within Dokuwiki
10
if(!defined('DOKU_INC')) die();
11
12
define('CM_KEYMARKER','____');            // used for settings with multiple dimensions of array indices
13
14
define('PLUGIN_SELF',dirname(__FILE__).'/');
15
define('PLUGIN_METADATA',PLUGIN_SELF.'settings/config.metadata.php');
16
if(!defined('DOKU_PLUGIN_IMAGES')) define('DOKU_PLUGIN_IMAGES',DOKU_BASE.'lib/plugins/config/images/');
17
18
require_once(PLUGIN_SELF.'settings/config.class.php');  // main configuration class and generic settings classes
19
require_once(PLUGIN_SELF.'settings/extra.class.php');   // settings classes specific to these settings
20
21
/**
22
 * All DokuWiki plugins to extend the admin function
23
 * need to inherit from this class
24
 */
25
class admin_plugin_config extends DokuWiki_Admin_Plugin {
26
27
    var $_file = PLUGIN_METADATA;
28
    var $_config = null;
29
    var $_input = null;
30
    var $_changed = false;          // set to true if configuration has altered
31
    var $_error = false;
32
    var $_session_started = false;
33
    var $_localised_prompts = false;
34
35
    /**
36
     * @return int
37
     */
38
    function getMenuSort() { return 100; }
39
40
    /**
41
     * handle user request
42
     */
43
    function handle() {
44
        global $ID, $INPUT;
45
46
        if(!$this->_restore_session() || $INPUT->int('save') != 1 || !checkSecurityToken()) {
47
            $this->_close_session();
48
            return;
49
        }
50
51
        if(is_null($this->_config)) {
52
            $this->_config = new configuration($this->_file);
53
        }
54
55
        // don't go any further if the configuration is locked
56
        if($this->_config->locked) {
57
            $this->_close_session();
58
            return;
59
        }
60
61
        $this->_input = $INPUT->arr('config');
62
63
        while (list($key) = each($this->_config->setting)) {
64
            $input = isset($this->_input[$key]) ? $this->_input[$key] : null;
65
            if ($this->_config->setting[$key]->update($input)) {
66
                $this->_changed = true;
67
            }
68
            if ($this->_config->setting[$key]->error()) $this->_error = true;
69
        }
70
71
        if ($this->_changed  && !$this->_error) {
72
            $this->_config->save_settings($this->getPluginName());
73
74
            // save state & force a page reload to get the new settings to take effect
75
            $_SESSION['PLUGIN_CONFIG'] = array('state' => 'updated', 'time' => time());
76
            $this->_close_session();
77
            send_redirect(wl($ID,array('do'=>'admin','page'=>'config'),true,'&'));
78
            exit();
79
        } elseif(!$this->_error) {
80
            $this->_config->touch_settings(); // just touch to refresh cache
81
        }
82
83
        $this->_close_session();
84
    }
85
86
    /**
87
     * output appropriate html
88
     */
89
    function html() {
90
        $allow_debug = $GLOBALS['conf']['allowdebug']; // avoid global $conf; here.
91
        global $lang;
92
        global $ID;
93
94
        if (is_null($this->_config)) { $this->_config = new configuration($this->_file); }
95
        $this->setupLocale(true);
96
97
        print $this->locale_xhtml('intro');
98
99
        ptln('<div id="config__manager">');
100
101
        if ($this->_config->locked)
0 ignored issues
show
The property locked cannot be accessed from this context as it is declared private in class configuration.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
102
            ptln('<div class="info">'.$this->getLang('locked').'</div>');
103
        elseif ($this->_error)
104
            ptln('<div class="error">'.$this->getLang('error').'</div>');
105
        elseif ($this->_changed)
106
            ptln('<div class="success">'.$this->getLang('updated').'</div>');
107
108
        // POST to script() instead of wl($ID) so config manager still works if
109
        // rewrite config is broken. Add $ID as hidden field to remember
110
        // current ID in most cases.
111
        ptln('<form action="'.script().'" method="post">');
112
        ptln('<div class="no"><input type="hidden" name="id" value="'.$ID.'" /></div>');
113
        formSecurityToken();
114
        $this->_print_h1('dokuwiki_settings', $this->getLang('_header_dokuwiki'));
115
116
        /** @var setting[] $undefined_settings */
117
        $undefined_settings = array();
118
        $in_fieldset = false;
119
        $first_plugin_fieldset = true;
120
        $first_template_fieldset = true;
121
        foreach($this->_config->setting as $setting) {
0 ignored issues
show
The property setting cannot be accessed from this context as it is declared private in class configuration.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
122
            if (is_a($setting, 'setting_hidden')) {
123
                // skip hidden (and undefined) settings
124
                if ($allow_debug && is_a($setting, 'setting_undefined')) {
125
                    $undefined_settings[] = $setting;
126
                } else {
127
                    continue;
128
                }
129
            } else if (is_a($setting, 'setting_fieldset')) {
130
                // config setting group
131
                if ($in_fieldset) {
132
                    ptln('  </table>');
133
                    ptln('  </div>');
134
                    ptln('  </fieldset>');
135
                } else {
136
                    $in_fieldset = true;
137
                }
138
                if ($first_plugin_fieldset && substr($setting->_key, 0, 10)=='plugin'.CM_KEYMARKER) {
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
139
                    $this->_print_h1('plugin_settings', $this->getLang('_header_plugin'));
140
                    $first_plugin_fieldset = false;
141
                } else if ($first_template_fieldset && substr($setting->_key, 0, 7)=='tpl'.CM_KEYMARKER) {
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
142
                    $this->_print_h1('template_settings', $this->getLang('_header_template'));
143
                    $first_template_fieldset = false;
144
                }
145
                ptln('  <fieldset id="'.$setting->_key.'">');
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
146
                ptln('  <legend>'.$setting->prompt($this).'</legend>');
147
                ptln('  <div class="table">');
148
                ptln('  <table class="inline">');
149
            } else {
150
                // config settings
151
                list($label,$input) = $setting->html($this, $this->_error);
152
153
                $class = $setting->is_default() ? ' class="default"' : ($setting->is_protected() ? ' class="protected"' : '');
154
                $error = $setting->error() ? ' class="value error"' : ' class="value"';
155
                $icon = $setting->caution() ? '<img src="'.DOKU_PLUGIN_IMAGES.$setting->caution().'.png" alt="'.$setting->caution().'" title="'.$this->getLang($setting->caution()).'" />' : '';
156
157
                ptln('    <tr'.$class.'>');
158
                ptln('      <td class="label">');
159
                ptln('        <span class="outkey">'.$setting->_out_key(true, true).'</span>');
160
                ptln('        '.$icon.$label);
161
                ptln('      </td>');
162
                ptln('      <td'.$error.'>'.$input.'</td>');
163
                ptln('    </tr>');
164
            }
165
        }
166
167
        ptln('  </table>');
168
        ptln('  </div>');
169
        if ($in_fieldset) {
170
            ptln('  </fieldset>');
171
        }
172
173
        // show undefined settings list
174
        if ($allow_debug && !empty($undefined_settings)) {
175
            /**
176
             * Callback for sorting settings
177
             *
178
             * @param setting $a
179
             * @param setting $b
180
             * @return int if $a is lower/equal/higher than $b
181
             */
182
            function _setting_natural_comparison($a, $b) {
183
                return strnatcmp($a->_key, $b->_key);
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
184
            }
185
186
            usort($undefined_settings, '_setting_natural_comparison');
187
            $this->_print_h1('undefined_settings', $this->getLang('_header_undefined'));
188
            ptln('<fieldset>');
189
            ptln('<div class="table">');
190
            ptln('<table class="inline">');
191
            $undefined_setting_match = array();
192
            foreach($undefined_settings as $setting) {
193
                if (preg_match('/^(?:plugin|tpl)'.CM_KEYMARKER.'.*?'.CM_KEYMARKER.'(.*)$/', $setting->_key, $undefined_setting_match)) {
194
                    $undefined_setting_key = $undefined_setting_match[1];
195
                } else {
196
                    $undefined_setting_key = $setting->_key;
197
                }
198
                ptln('  <tr>');
199
                ptln('    <td class="label"><span title="$meta[\''.$undefined_setting_key.'\']">$'.$this->_config->_name.'[\''.$setting->_out_key().'\']</span></td>');
0 ignored issues
show
The property _name cannot be accessed from this context as it is declared private in class configuration.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
200
                ptln('    <td>'.$this->getLang('_msg_'.get_class($setting)).'</td>');
201
                ptln('  </tr>');
202
            }
203
            ptln('</table>');
204
            ptln('</div>');
205
            ptln('</fieldset>');
206
        }
207
208
        // finish up form
209
        ptln('<p>');
210
        ptln('  <input type="hidden" name="do"     value="admin" />');
211
        ptln('  <input type="hidden" name="page"   value="config" />');
212
213
        if (!$this->_config->locked) {
0 ignored issues
show
The property locked cannot be accessed from this context as it is declared private in class configuration.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
214
            ptln('  <input type="hidden" name="save"   value="1" />');
215
            ptln('  <button type="submit" name="submit" accesskey="s">'.$lang['btn_save'].'</button>');
216
            ptln('  <button type="reset">'.$lang['btn_reset'].'</button>');
217
        }
218
219
        ptln('</p>');
220
221
        ptln('</form>');
222
        ptln('</div>');
223
    }
224
225
    /**
226
     * @return boolean   true - proceed with handle, false - don't proceed
227
     */
228
    function _restore_session() {
229
230
        // dokuwiki closes the session before act_dispatch. $_SESSION variables are all set,
231
        // however they can't be changed without starting the session again
232
        if (!headers_sent()) {
233
            session_start();
234
            $this->_session_started = true;
235
        }
236
237
        if (!isset($_SESSION['PLUGIN_CONFIG'])) return true;
238
239
        $session = $_SESSION['PLUGIN_CONFIG'];
240
        unset($_SESSION['PLUGIN_CONFIG']);
241
242
        // still valid?
243
        if (time() - $session['time'] > 120) return true;
244
245
        switch ($session['state']) {
246
            case 'updated' :
247
                $this->_changed = true;
248
                return false;
249
        }
250
251
        return true;
252
    }
253
254
    function _close_session() {
255
      if ($this->_session_started) session_write_close();
256
    }
257
258
    /**
259
     * @param bool $prompts
260
     */
261
    function setupLocale($prompts=false) {
262
263
        parent::setupLocale();
264
        if (!$prompts || $this->_localised_prompts) return;
265
266
        $this->_setup_localised_plugin_prompts();
267
        $this->_localised_prompts = true;
268
269
    }
270
271
    /**
272
     * @return bool
273
     */
274
    function _setup_localised_plugin_prompts() {
275
        global $conf;
276
277
        $langfile   = '/lang/'.$conf['lang'].'/settings.php';
278
        $enlangfile = '/lang/en/settings.php';
279
280
        if ($dh = opendir(DOKU_PLUGIN)) {
281
            while (false !== ($plugin = readdir($dh))) {
282
                if ($plugin == '.' || $plugin == '..' || $plugin == 'tmp' || $plugin == 'config') continue;
283
                if (is_file(DOKU_PLUGIN.$plugin)) continue;
284
285
                if (file_exists(DOKU_PLUGIN.$plugin.$enlangfile)){
286
                    $lang = array();
287
                    @include(DOKU_PLUGIN.$plugin.$enlangfile);
288
                    if ($conf['lang'] != 'en') @include(DOKU_PLUGIN.$plugin.$langfile);
289
                    foreach ($lang as $key => $value){
290
                        $this->lang['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.$key] = $value;
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
291
                    }
292
                }
293
294
                // fill in the plugin name if missing (should exist for plugins with settings)
295
                if (!isset($this->lang['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'])) {
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
296
                    $this->lang['plugin'.CM_KEYMARKER.$plugin.CM_KEYMARKER.'plugin_settings_name'] =
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
297
                      ucwords(str_replace('_', ' ', $plugin));
298
                }
299
            }
300
            closedir($dh);
301
      }
302
303
        // the same for the active template
304
        $tpl = $conf['template'];
305
306
        if (file_exists(tpl_incdir().$enlangfile)){
307
            $lang = array();
308
            @include(tpl_incdir().$enlangfile);
309
            if ($conf['lang'] != 'en') @include(tpl_incdir().$langfile);
310
            foreach ($lang as $key => $value){
311
                $this->lang['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.$key] = $value;
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
312
            }
313
        }
314
315
        // fill in the template name if missing (should exist for templates with settings)
316
        if (!isset($this->lang['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'])) {
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
317
            $this->lang['tpl'.CM_KEYMARKER.$tpl.CM_KEYMARKER.'template_settings_name'] =
0 ignored issues
show
The property lang cannot be accessed from this context as it is declared private in class DokuWiki_Plugin.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
318
              ucwords(str_replace('_', ' ', $tpl));
319
        }
320
321
        return true;
322
    }
323
324
    /**
325
     * Generates a two-level table of contents for the config plugin.
326
     *
327
     * @author Ben Coburn <[email protected]>
328
     *
329
     * @return array
330
     */
331
    function getTOC() {
332
        if (is_null($this->_config)) { $this->_config = new configuration($this->_file); }
333
        $this->setupLocale(true);
334
335
        $allow_debug = $GLOBALS['conf']['allowdebug']; // avoid global $conf; here.
336
337
        // gather toc data
338
        $has_undefined = false;
339
        $toc = array('conf'=>array(), 'plugin'=>array(), 'template'=>null);
340
        foreach($this->_config->setting as $setting) {
0 ignored issues
show
The property setting cannot be accessed from this context as it is declared private in class configuration.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
341
            if (is_a($setting, 'setting_fieldset')) {
342
                if (substr($setting->_key, 0, 10)=='plugin'.CM_KEYMARKER) {
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
343
                    $toc['plugin'][] = $setting;
344
                } else if (substr($setting->_key, 0, 7)=='tpl'.CM_KEYMARKER) {
0 ignored issues
show
The property _key cannot be accessed from this context as it is declared private in class setting.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
345
                    $toc['template'] = $setting;
346
                } else {
347
                    $toc['conf'][] = $setting;
348
                }
349
            } else if (!$has_undefined && is_a($setting, 'setting_undefined')) {
350
                $has_undefined = true;
351
            }
352
        }
353
354
        // build toc
355
        $t = array();
356
357
        $t[] = html_mktocitem('configuration_manager', $this->getLang('_configuration_manager'), 1);
358
        $t[] = html_mktocitem('dokuwiki_settings', $this->getLang('_header_dokuwiki'), 1);
359
        /** @var setting $setting */
360
        foreach($toc['conf'] as $setting) {
361
            $name = $setting->prompt($this);
362
            $t[] = html_mktocitem($setting->_key, $name, 2);
363
        }
364
        if (!empty($toc['plugin'])) {
365
            $t[] = html_mktocitem('plugin_settings', $this->getLang('_header_plugin'), 1);
366
        }
367
        foreach($toc['plugin'] as $setting) {
368
            $name = $setting->prompt($this);
369
            $t[] = html_mktocitem($setting->_key, $name, 2);
370
        }
371
        if (isset($toc['template'])) {
372
            $t[] = html_mktocitem('template_settings', $this->getLang('_header_template'), 1);
373
            $setting = $toc['template'];
374
            $name = $setting->prompt($this);
375
            $t[] = html_mktocitem($setting->_key, $name, 2);
376
        }
377
        if ($has_undefined && $allow_debug) {
378
            $t[] = html_mktocitem('undefined_settings', $this->getLang('_header_undefined'), 1);
379
        }
380
381
        return $t;
382
    }
383
384
    /**
385
     * @param string $id
386
     * @param string $text
387
     */
388
    function _print_h1($id, $text) {
389
        ptln('<h1 id="'.$id.'">'.$text.'</h1>');
390
    }
391
392
393
}
394