1<?php 2 3/** 4 * Configuration Manager admin plugin 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Christopher Smith <chris@jalakai.co.uk> 8 * @author Ben Coburn <btcoburn@silicodon.net> 9 */ 10 11use dokuwiki\Extension\AdminPlugin; 12use dokuwiki\plugin\config\core\Configuration; 13use dokuwiki\plugin\config\core\Setting\Setting; 14use dokuwiki\plugin\config\core\Setting\SettingFieldset; 15use dokuwiki\plugin\config\core\Setting\SettingHidden; 16 17/** 18 * All DokuWiki plugins to extend the admin function 19 * need to inherit from this class 20 */ 21class admin_plugin_config extends AdminPlugin 22{ 23 protected const IMGDIR = DOKU_BASE . 'lib/plugins/config/images/'; 24 25 /** @var Configuration */ 26 protected $configuration; 27 28 /** @var bool were there any errors in the submitted data? */ 29 protected $hasErrors = false; 30 31 /** @var bool have the settings translations been loaded? */ 32 protected $promptsLocalized = false; 33 34 35 /** 36 * handle user request 37 */ 38 public function handle() 39 { 40 global $ID, $INPUT; 41 42 // always initialize the configuration 43 $this->configuration = new Configuration(); 44 45 if (!$INPUT->bool('save') || !checkSecurityToken()) { 46 return; 47 } 48 49 // don't go any further if the configuration is locked 50 if ($this->configuration->isLocked()) return; 51 52 // update settings and redirect of successful 53 $ok = $this->configuration->updateSettings($INPUT->arr('config')); 54 if ($ok) { // no errors 55 try { 56 if ($this->configuration->hasChanged()) { 57 $this->configuration->save(); 58 } else { 59 $this->configuration->touch(); 60 } 61 msg($this->getLang('updated'), 1); 62 } catch (Exception $e) { 63 msg($this->getLang('error'), -1); 64 } 65 send_redirect(wl($ID, ['do' => 'admin', 'page' => 'config'], true, '&')); 66 } else { 67 $this->hasErrors = true; 68 msg($this->getLang('error'), -1); 69 } 70 } 71 72 /** 73 * output appropriate html 74 */ 75 public function html() 76 { 77 $allow_debug = $GLOBALS['conf']['allowdebug']; // avoid global $conf; here. 78 global $lang; 79 global $ID; 80 81 $this->setupLocale(true); 82 83 echo $this->locale_xhtml('intro'); 84 85 echo '<div id="config__manager">'; 86 87 if ($this->configuration->isLocked()) { 88 echo '<div class="info">' . $this->getLang('locked') . '</div>'; 89 } 90 91 // POST to script() instead of wl($ID) so config manager still works if 92 // rewrite config is broken. Add $ID as hidden field to remember 93 // current ID in most cases. 94 echo '<form id="dw__configform" action="' . script() . '" method="post" autocomplete="off">'; 95 echo '<div class="no"><input type="hidden" name="id" value="' . $ID . '" /></div>'; 96 formSecurityToken(); 97 $this->printH1('dokuwiki_settings', $this->getLang('_header_dokuwiki')); 98 99 $in_fieldset = false; 100 $first_plugin_fieldset = true; 101 $first_template_fieldset = true; 102 foreach ($this->configuration->getSettings() as $setting) { 103 if ($setting instanceof SettingHidden) { 104 continue; 105 } elseif ($setting instanceof SettingFieldset) { 106 // config setting group 107 if ($in_fieldset) { 108 echo '</table>'; 109 echo '</div>'; 110 echo '</fieldset>'; 111 } else { 112 $in_fieldset = true; 113 } 114 if ($first_plugin_fieldset && $setting->getType() == 'plugin') { 115 $this->printH1('plugin_settings', $this->getLang('_header_plugin')); 116 $first_plugin_fieldset = false; 117 } elseif ($first_template_fieldset && $setting->getType() == 'template') { 118 $this->printH1('template_settings', $this->getLang('_header_template')); 119 $first_template_fieldset = false; 120 } 121 echo '<fieldset id="' . $setting->getKey() . '">'; 122 echo '<legend>' . $setting->prompt($this) . '</legend>'; 123 echo '<div class="table">'; 124 echo '<table class="inline">'; 125 } else { 126 // config settings 127 [$label, $input] = $setting->html($this, $this->hasErrors); 128 129 $class = $setting->isDefault() 130 ? ' class="default"' 131 : ($setting->isProtected() ? ' class="protected"' : ''); 132 $error = $setting->hasError() 133 ? ' class="value error"' 134 : ' class="value"'; 135 $icon = $setting->caution() 136 ? '<img src="' . self::IMGDIR . $setting->caution() . '.png" ' . 137 'alt="' . $setting->caution() . '" title="' . $this->getLang($setting->caution()) . '" />' 138 : ''; 139 140 echo '<tr' . $class . '>'; 141 echo '<td class="label">'; 142 echo '<span class="outkey">' . $setting->getPrettyKey() . '</span>'; 143 echo $icon . $label; 144 echo '</td>'; 145 echo '<td' . $error . '>' . $input . '</td>'; 146 echo '</tr>'; 147 } 148 } 149 150 echo '</table>'; 151 echo '</div>'; 152 if ($in_fieldset) { 153 echo '</fieldset>'; 154 } 155 156 // show undefined settings list 157 $undefined_settings = $this->configuration->getUndefined(); 158 if ($allow_debug && !empty($undefined_settings)) { 159 /** 160 * Callback for sorting settings 161 * 162 * @param Setting $a 163 * @param Setting $b 164 * @return int if $a is lower/equal/higher than $b 165 */ 166 function settingNaturalComparison($a, $b) 167 { 168 return strnatcmp($a->getKey(), $b->getKey()); 169 } 170 171 usort($undefined_settings, 'settingNaturalComparison'); 172 $this->printH1('undefined_settings', $this->getLang('_header_undefined')); 173 echo '<fieldset>'; 174 echo '<div class="table">'; 175 echo '<table class="inline">'; 176 foreach ($undefined_settings as $setting) { 177 [$label, $input] = $setting->html($this); 178 echo '<tr>'; 179 echo '<td class="label">' . $label . '</td>'; 180 echo '<td>' . $input . '</td>'; 181 echo '</tr>'; 182 } 183 echo '</table>'; 184 echo '</div>'; 185 echo '</fieldset>'; 186 } 187 188 // finish up form 189 echo '<p>'; 190 echo '<input type="hidden" name="do" value="admin" />'; 191 echo '<input type="hidden" name="page" value="config" />'; 192 193 if (!$this->configuration->isLocked()) { 194 echo '<input type="hidden" name="save" value="1" />'; 195 echo '<button type="submit" name="submit" accesskey="s">' . $lang['btn_save'] . '</button>'; 196 echo '<button type="reset">' . $lang['btn_reset'] . '</button>'; 197 } 198 199 echo '</p>'; 200 201 echo '</form>'; 202 echo '</div>'; 203 } 204 205 /** 206 * @param bool $prompts 207 */ 208 public function setupLocale($prompts = false) 209 { 210 parent::setupLocale(); 211 if (!$prompts || $this->promptsLocalized) return; 212 $this->lang = array_merge($this->lang, $this->configuration->getLangs()); 213 $this->promptsLocalized = true; 214 } 215 216 /** 217 * Generates a two-level table of contents for the config plugin. 218 * 219 * @author Ben Coburn <btcoburn@silicodon.net> 220 * 221 * @return array 222 */ 223 public function getTOC() 224 { 225 $this->setupLocale(true); 226 227 $allow_debug = $GLOBALS['conf']['allowdebug']; // avoid global $conf; here. 228 $toc = []; 229 $check = false; 230 231 // gather settings data into three sub arrays 232 $labels = ['dokuwiki' => [], 'plugin' => [], 'template' => []]; 233 foreach ($this->configuration->getSettings() as $setting) { 234 if ($setting instanceof SettingFieldset) { 235 $labels[$setting->getType()][] = $setting; 236 } 237 } 238 239 // top header 240 $title = $this->getLang('_configuration_manager'); 241 $toc[] = html_mktocitem(sectionID($title, $check), $title, 1); 242 243 // main entries 244 foreach (['dokuwiki', 'plugin', 'template'] as $section) { 245 if (empty($labels[$section])) continue; // no entries, skip 246 247 // create main header 248 $toc[] = html_mktocitem( 249 $section . '_settings', 250 $this->getLang('_header_' . $section), 251 1 252 ); 253 254 // create sub headers 255 foreach ($labels[$section] as $setting) { 256 /** @var SettingFieldset $setting */ 257 $name = $setting->prompt($this); 258 $toc[] = html_mktocitem($setting->getKey(), $name, 2); 259 } 260 } 261 262 // undefined settings if allowed 263 if (count($this->configuration->getUndefined()) && $allow_debug) { 264 $toc[] = html_mktocitem('undefined_settings', $this->getLang('_header_undefined'), 1); 265 } 266 267 return $toc; 268 } 269 270 /** 271 * @param string $id 272 * @param string $text 273 */ 274 protected function printH1($id, $text) 275 { 276 echo '<h1 id="' . $id . '">' . $text . '</h1>'; 277 } 278 279 /** 280 * Adds a translation to this plugin's language array 281 * 282 * Used by some settings to set up dynamic translations 283 * 284 * @param string $key 285 * @param string $value 286 */ 287 public function addLang($key, $value) 288 { 289 if (!$this->localised) $this->setupLocale(); 290 $this->lang[$key] = $value; 291 } 292} 293