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