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