1<?php 2 3namespace dokuwiki\plugin\config\core\Setting; 4 5use dokuwiki\plugin\config\core\Configuration; 6 7/** 8 * Class Setting 9 */ 10class Setting 11{ 12 /** @var string unique identifier of this setting */ 13 protected $key = ''; 14 15 /** @var mixed the default value of this setting */ 16 protected $default; 17 /** @var mixed the local value of this setting */ 18 protected $local; 19 /** @var mixed the protected value of this setting */ 20 protected $protected; 21 22 /** @var array valid alerts, images matching the alerts are in the plugin's images directory */ 23 protected static $validCautions = ['warning', 'danger', 'security']; 24 25 protected $pattern = ''; 26 protected $error = false; // only used by those classes which error check 27 protected $input; // only used by those classes which error check 28 protected $caution; // used by any setting to provide an alert along with the setting 29 30 /** 31 * Constructor. 32 * 33 * The given parameters will be set up as class properties 34 * 35 * @see initialize() to set the actual value of the setting 36 * 37 * @param string $key 38 * @param array|null $params array with metadata of setting 39 */ 40 public function __construct($key, $params = null) 41 { 42 $this->key = $key; 43 44 if (is_array($params)) { 45 foreach ($params as $property => $value) { 46 $property = trim($property, '_'); // we don't use underscores anymore 47 $this->$property = $value; 48 } 49 } 50 } 51 52 /** 53 * Set the current values for the setting $key 54 * 55 * This is used to initialize the setting with the data read form the config files. 56 * 57 * @see update() to set a new value 58 * @param mixed $default default setting value 59 * @param mixed $local local setting value 60 * @param mixed $protected protected setting value 61 */ 62 public function initialize($default = null, $local = null, $protected = null) 63 { 64 $this->default = $this->cleanValue($default); 65 $this->local = $this->cleanValue($local); 66 $this->protected = $this->cleanValue($protected); 67 } 68 69 /** 70 * update changed setting with validated user provided value $input 71 * - if changed value fails validation check, save it to $this->input (to allow echoing later) 72 * - if changed value passes validation check, set $this->local to the new value 73 * 74 * @param mixed $input the new value 75 * @return boolean true if changed, false otherwise 76 */ 77 public function update($input) 78 { 79 if (is_null($input)) return false; 80 if ($this->isProtected()) return false; 81 $input = $this->cleanValue($input); 82 83 $value = is_null($this->local) ? $this->default : $this->local; 84 if ($value == $input) return false; 85 86 // validate new value 87 if ($this->pattern && !preg_match($this->pattern, $input)) { 88 $this->error = true; 89 $this->input = $input; 90 return false; 91 } 92 93 // update local copy of this setting with new value 94 $this->local = $input; 95 96 // setting ready for update 97 return true; 98 } 99 100 /** 101 * Clean a value read from a config before using it internally 102 * 103 * Default implementation returns $value as is. Subclasses can override. 104 * Note: null should always be returned as null! 105 * 106 * This is applied in initialize() and update() 107 * 108 * @param mixed $value 109 * @return mixed 110 */ 111 protected function cleanValue($value) 112 { 113 return $value; 114 } 115 116 /** 117 * Should this type of config have a default? 118 * 119 * @return bool 120 */ 121 public function shouldHaveDefault() 122 { 123 return true; 124 } 125 126 /** 127 * Get this setting's unique key 128 * 129 * @return string 130 */ 131 public function getKey() 132 { 133 return $this->key; 134 } 135 136 /** 137 * Get the key of this setting marked up human readable 138 * 139 * @param bool $url link to dokuwiki.org manual? 140 * @return string 141 */ 142 public function getPrettyKey($url = true) 143 { 144 $out = str_replace(Configuration::KEYMARKER, "»", $this->key); 145 if ($url && !strstr($out, '»')) {//provide no urls for plugins, etc. 146 if ($out == 'start') { 147 // exception, because this config name is clashing with our actual start page 148 return '<a href="https://www.dokuwiki.org/config:startpage">' . $out . '</a>'; 149 } else { 150 return '<a href="https://www.dokuwiki.org/config:' . $out . '">' . $out . '</a>'; 151 } 152 } 153 return $out; 154 } 155 156 /** 157 * Returns setting key as an array key separator 158 * 159 * This is used to create form output 160 * 161 * @return string key 162 */ 163 public function getArrayKey() 164 { 165 return str_replace(Configuration::KEYMARKER, "']['", $this->key); 166 } 167 168 /** 169 * What type of configuration is this 170 * 171 * Returns one of 172 * 173 * 'plugin' for plugin configuration 174 * 'template' for template configuration 175 * 'dokuwiki' for core configuration 176 * 177 * @return string 178 */ 179 public function getType() 180 { 181 if (str_starts_with($this->getKey(), 'plugin' . Configuration::KEYMARKER)) { 182 return 'plugin'; 183 } elseif (str_starts_with($this->getKey(), 'tpl' . Configuration::KEYMARKER)) { 184 return 'template'; 185 } else { 186 return 'dokuwiki'; 187 } 188 } 189 190 /** 191 * Build html for label and input of setting 192 * 193 * @param \admin_plugin_config $plugin object of config plugin 194 * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 195 * @return string[] with content array(string $label_html, string $input_html) 196 */ 197 public function html(\admin_plugin_config $plugin, $echo = false) 198 { 199 $disable = ''; 200 201 if ($this->isProtected()) { 202 $value = $this->protected; 203 $disable = 'disabled="disabled"'; 204 } elseif ($echo && $this->error) { 205 $value = $this->input; 206 } else { 207 $value = is_null($this->local) ? $this->default : $this->local; 208 } 209 210 $key = htmlspecialchars($this->key); 211 $value = formText($value); 212 213 $label = '<label for="config___' . $key . '">' . $this->prompt($plugin) . '</label>'; 214 $input = '<textarea rows="3" cols="40" id="config___' . $key . 215 '" name="config[' . $key . ']" class="edit" ' . $disable . '>' . $value . '</textarea>'; 216 return [$label, $input]; 217 } 218 219 /** 220 * Should the current local value be saved? 221 * 222 * @see out() to run when this returns true 223 * @return bool 224 */ 225 public function shouldBeSaved() 226 { 227 if ($this->isProtected()) return false; 228 if ($this->local === null) return false; 229 if ($this->default == $this->local) return false; 230 return true; 231 } 232 233 /** 234 * Escaping 235 * 236 * @param string $string 237 * @return string 238 */ 239 protected function escape($string) 240 { 241 $tr = ["\\" => '\\\\', "'" => '\\\'']; 242 return "'" . strtr(cleanText($string), $tr) . "'"; 243 } 244 245 /** 246 * Generate string to save local setting value to file according to $fmt 247 * 248 * @see shouldBeSaved() to check if this should be called 249 * @param string $var name of variable 250 * @param string $fmt save format 251 * @return string 252 */ 253 public function out($var, $fmt = 'php') 254 { 255 if ($fmt != 'php') return ''; 256 257 if (is_array($this->local)) { 258 $value = 'array(' . implode(', ', array_map([$this, 'escape'], $this->local)) . ')'; 259 } else { 260 $value = $this->escape($this->local); 261 } 262 263 $out = '$' . $var . "['" . $this->getArrayKey() . "'] = $value;\n"; 264 265 return $out; 266 } 267 268 /** 269 * Returns the localized prompt 270 * 271 * @param \admin_plugin_config $plugin object of config plugin 272 * @return string text 273 */ 274 public function prompt(\admin_plugin_config $plugin) 275 { 276 $prompt = $plugin->getLang($this->key); 277 if (!$prompt) $prompt = htmlspecialchars(str_replace(['____', '_'], ' ', $this->key)); 278 return $prompt; 279 } 280 281 /** 282 * Is setting protected 283 * 284 * @return bool 285 */ 286 public function isProtected() 287 { 288 return !is_null($this->protected); 289 } 290 291 /** 292 * Is setting the default? 293 * 294 * @return bool 295 */ 296 public function isDefault() 297 { 298 return !$this->isProtected() && is_null($this->local); 299 } 300 301 /** 302 * Has an error? 303 * 304 * @return bool 305 */ 306 public function hasError() 307 { 308 return $this->error; 309 } 310 311 /** 312 * Returns caution 313 * 314 * @return false|string caution string, otherwise false for invalid caution 315 */ 316 public function caution() 317 { 318 if (empty($this->caution)) return false; 319 if (!in_array($this->caution, Setting::$validCautions)) { 320 throw new \RuntimeException( 321 'Invalid caution string (' . $this->caution . ') in metadata for setting "' . $this->key . '"' 322 ); 323 } 324 return $this->caution; 325 } 326} 327