10a5b05ebSAndreas Gohr<?php 20a5b05ebSAndreas Gohr 30a5b05ebSAndreas Gohrnamespace dokuwiki\plugin\config\core\Setting; 498a151baSAndreas Gohr 50a5b05ebSAndreas Gohruse dokuwiki\plugin\config\core\Configuration; 60a5b05ebSAndreas Gohr 70a5b05ebSAndreas Gohr/** 80a5b05ebSAndreas Gohr * Class Setting 90a5b05ebSAndreas Gohr */ 108c7c53b0SAndreas Gohrclass Setting 118c7c53b0SAndreas Gohr{ 120a5b05ebSAndreas Gohr /** @var string unique identifier of this setting */ 130a5b05ebSAndreas Gohr protected $key = ''; 140a5b05ebSAndreas Gohr 150a5b05ebSAndreas Gohr /** @var mixed the default value of this setting */ 16467c1427SAndreas Gohr protected $default; 170a5b05ebSAndreas Gohr /** @var mixed the local value of this setting */ 18467c1427SAndreas Gohr protected $local; 190a5b05ebSAndreas Gohr /** @var mixed the protected value of this setting */ 20467c1427SAndreas Gohr protected $protected; 210a5b05ebSAndreas Gohr 220a5b05ebSAndreas Gohr /** @var array valid alerts, images matching the alerts are in the plugin's images directory */ 23fe15e2c0SAndreas Gohr protected static $validCautions = ['warning', 'danger', 'security']; 240a5b05ebSAndreas Gohr 250a5b05ebSAndreas Gohr protected $pattern = ''; 260a5b05ebSAndreas Gohr protected $error = false; // only used by those classes which error check 27467c1427SAndreas Gohr protected $input; // only used by those classes which error check 28467c1427SAndreas Gohr protected $caution; // used by any setting to provide an alert along with the setting 290a5b05ebSAndreas Gohr 300a5b05ebSAndreas Gohr /** 310a5b05ebSAndreas Gohr * Constructor. 320a5b05ebSAndreas Gohr * 330a5b05ebSAndreas Gohr * The given parameters will be set up as class properties 340a5b05ebSAndreas Gohr * 35af40dea1SAndreas Gohr * @see initialize() to set the actual value of the setting 36af40dea1SAndreas Gohr * 370a5b05ebSAndreas Gohr * @param string $key 380a5b05ebSAndreas Gohr * @param array|null $params array with metadata of setting 390a5b05ebSAndreas Gohr */ 40d868eb89SAndreas Gohr public function __construct($key, $params = null) 41d868eb89SAndreas Gohr { 420a5b05ebSAndreas Gohr $this->key = $key; 430a5b05ebSAndreas Gohr 440a5b05ebSAndreas Gohr if (is_array($params)) { 450a5b05ebSAndreas Gohr foreach ($params as $property => $value) { 460a5b05ebSAndreas Gohr $property = trim($property, '_'); // we don't use underscores anymore 470a5b05ebSAndreas Gohr $this->$property = $value; 480a5b05ebSAndreas Gohr } 490a5b05ebSAndreas Gohr } 500a5b05ebSAndreas Gohr } 510a5b05ebSAndreas Gohr 520a5b05ebSAndreas Gohr /** 53af40dea1SAndreas Gohr * Set the current values for the setting $key 540a5b05ebSAndreas Gohr * 55af40dea1SAndreas Gohr * This is used to initialize the setting with the data read form the config files. 56af40dea1SAndreas Gohr * 57af40dea1SAndreas Gohr * @see update() to set a new value 580a5b05ebSAndreas Gohr * @param mixed $default default setting value 590a5b05ebSAndreas Gohr * @param mixed $local local setting value 600a5b05ebSAndreas Gohr * @param mixed $protected protected setting value 610a5b05ebSAndreas Gohr */ 62d868eb89SAndreas Gohr public function initialize($default = null, $local = null, $protected = null) 63d868eb89SAndreas Gohr { 6428cc4f40SAndreas Gohr $this->default = $this->cleanValue($default); 6528cc4f40SAndreas Gohr $this->local = $this->cleanValue($local); 6628cc4f40SAndreas Gohr $this->protected = $this->cleanValue($protected); 67af40dea1SAndreas Gohr } 68af40dea1SAndreas Gohr 69af40dea1SAndreas Gohr /** 708356fe60SChristopher Smith * update changed setting with validated user provided value $input 718356fe60SChristopher Smith * - if changed value fails validation check, save it to $this->input (to allow echoing later) 728356fe60SChristopher Smith * - if changed value passes validation check, set $this->local to the new value 73af40dea1SAndreas Gohr * 74af40dea1SAndreas Gohr * @param mixed $input the new value 758356fe60SChristopher Smith * @return boolean true if changed, false otherwise 76af40dea1SAndreas Gohr */ 77d868eb89SAndreas Gohr public function update($input) 78d868eb89SAndreas Gohr { 79af40dea1SAndreas Gohr if (is_null($input)) return false; 80af40dea1SAndreas Gohr if ($this->isProtected()) return false; 8128cc4f40SAndreas Gohr $input = $this->cleanValue($input); 82af40dea1SAndreas Gohr 83af40dea1SAndreas Gohr $value = is_null($this->local) ? $this->default : $this->local; 84af40dea1SAndreas Gohr if ($value == $input) return false; 85af40dea1SAndreas Gohr 868356fe60SChristopher Smith // validate new value 87af40dea1SAndreas Gohr if ($this->pattern && !preg_match($this->pattern, $input)) { 88af40dea1SAndreas Gohr $this->error = true; 89af40dea1SAndreas Gohr $this->input = $input; 90af40dea1SAndreas Gohr return false; 91af40dea1SAndreas Gohr } 92af40dea1SAndreas Gohr 938356fe60SChristopher Smith // update local copy of this setting with new value 94af40dea1SAndreas Gohr $this->local = $input; 958356fe60SChristopher Smith 968356fe60SChristopher Smith // setting ready for update 97af40dea1SAndreas Gohr return true; 980a5b05ebSAndreas Gohr } 990a5b05ebSAndreas Gohr 1000a5b05ebSAndreas Gohr /** 10128cc4f40SAndreas Gohr * Clean a value read from a config before using it internally 10228cc4f40SAndreas Gohr * 10328cc4f40SAndreas Gohr * Default implementation returns $value as is. Subclasses can override. 10428cc4f40SAndreas Gohr * Note: null should always be returned as null! 10528cc4f40SAndreas Gohr * 10628cc4f40SAndreas Gohr * This is applied in initialize() and update() 10728cc4f40SAndreas Gohr * 10828cc4f40SAndreas Gohr * @param mixed $value 10928cc4f40SAndreas Gohr * @return mixed 11028cc4f40SAndreas Gohr */ 111d868eb89SAndreas Gohr protected function cleanValue($value) 112d868eb89SAndreas Gohr { 11328cc4f40SAndreas Gohr return $value; 11428cc4f40SAndreas Gohr } 11528cc4f40SAndreas Gohr 11628cc4f40SAndreas Gohr /** 1170a5b05ebSAndreas Gohr * Should this type of config have a default? 1180a5b05ebSAndreas Gohr * 1190a5b05ebSAndreas Gohr * @return bool 1200a5b05ebSAndreas Gohr */ 121d868eb89SAndreas Gohr public function shouldHaveDefault() 122d868eb89SAndreas Gohr { 1230a5b05ebSAndreas Gohr return true; 1240a5b05ebSAndreas Gohr } 1250a5b05ebSAndreas Gohr 1260a5b05ebSAndreas Gohr /** 1270a5b05ebSAndreas Gohr * Get this setting's unique key 1280a5b05ebSAndreas Gohr * 1290a5b05ebSAndreas Gohr * @return string 1300a5b05ebSAndreas Gohr */ 131d868eb89SAndreas Gohr public function getKey() 132d868eb89SAndreas Gohr { 1330a5b05ebSAndreas Gohr return $this->key; 1340a5b05ebSAndreas Gohr } 1350a5b05ebSAndreas Gohr 1360a5b05ebSAndreas Gohr /** 1370a5b05ebSAndreas Gohr * Get the key of this setting marked up human readable 1380a5b05ebSAndreas Gohr * 1390a5b05ebSAndreas Gohr * @param bool $url link to dokuwiki.org manual? 1400a5b05ebSAndreas Gohr * @return string 1410a5b05ebSAndreas Gohr */ 142d868eb89SAndreas Gohr public function getPrettyKey($url = true) 143d868eb89SAndreas Gohr { 1440a5b05ebSAndreas Gohr $out = str_replace(Configuration::KEYMARKER, "»", $this->key); 1450a5b05ebSAndreas Gohr if ($url && !strstr($out, '»')) {//provide no urls for plugins, etc. 1460a5b05ebSAndreas Gohr if ($out == 'start') { 1470a5b05ebSAndreas Gohr // exception, because this config name is clashing with our actual start page 148ffd2c6d6SAndreas Gohr return '<a href="https://www.dokuwiki.org/config:startpage">' . $out . '</a>'; 1490a5b05ebSAndreas Gohr } else { 150ffd2c6d6SAndreas Gohr return '<a href="https://www.dokuwiki.org/config:' . $out . '">' . $out . '</a>'; 1510a5b05ebSAndreas Gohr } 1520a5b05ebSAndreas Gohr } 1530a5b05ebSAndreas Gohr return $out; 1540a5b05ebSAndreas Gohr } 1550a5b05ebSAndreas Gohr 1560a5b05ebSAndreas Gohr /** 1570a5b05ebSAndreas Gohr * Returns setting key as an array key separator 1580a5b05ebSAndreas Gohr * 1590a5b05ebSAndreas Gohr * This is used to create form output 1600a5b05ebSAndreas Gohr * 1610a5b05ebSAndreas Gohr * @return string key 1620a5b05ebSAndreas Gohr */ 163d868eb89SAndreas Gohr public function getArrayKey() 164d868eb89SAndreas Gohr { 1650a5b05ebSAndreas Gohr return str_replace(Configuration::KEYMARKER, "']['", $this->key); 1660a5b05ebSAndreas Gohr } 1670a5b05ebSAndreas Gohr 1680a5b05ebSAndreas Gohr /** 1690a5b05ebSAndreas Gohr * What type of configuration is this 1700a5b05ebSAndreas Gohr * 1710a5b05ebSAndreas Gohr * Returns one of 1720a5b05ebSAndreas Gohr * 1730a5b05ebSAndreas Gohr * 'plugin' for plugin configuration 1740a5b05ebSAndreas Gohr * 'template' for template configuration 175d6fc72e1SAndreas Gohr * 'dokuwiki' for core configuration 1760a5b05ebSAndreas Gohr * 1770a5b05ebSAndreas Gohr * @return string 1780a5b05ebSAndreas Gohr */ 179d868eb89SAndreas Gohr public function getType() 180d868eb89SAndreas Gohr { 181*1b2deed9Sfiwswe if (str_starts_with($this->getKey(), 'plugin' . Configuration::KEYMARKER)) { 1820a5b05ebSAndreas Gohr return 'plugin'; 183*1b2deed9Sfiwswe } elseif (str_starts_with($this->getKey(), 'tpl' . Configuration::KEYMARKER)) { 1840a5b05ebSAndreas Gohr return 'template'; 1850a5b05ebSAndreas Gohr } else { 186d6fc72e1SAndreas Gohr return 'dokuwiki'; 1870a5b05ebSAndreas Gohr } 1880a5b05ebSAndreas Gohr } 1890a5b05ebSAndreas Gohr 1900a5b05ebSAndreas Gohr /** 1910a5b05ebSAndreas Gohr * Build html for label and input of setting 1920a5b05ebSAndreas Gohr * 1930a5b05ebSAndreas Gohr * @param \admin_plugin_config $plugin object of config plugin 1940a5b05ebSAndreas Gohr * @param bool $echo true: show inputted value, when error occurred, otherwise the stored setting 1950a5b05ebSAndreas Gohr * @return string[] with content array(string $label_html, string $input_html) 1960a5b05ebSAndreas Gohr */ 197d868eb89SAndreas Gohr public function html(\admin_plugin_config $plugin, $echo = false) 198d868eb89SAndreas Gohr { 1990a5b05ebSAndreas Gohr $disable = ''; 2000a5b05ebSAndreas Gohr 2010a5b05ebSAndreas Gohr if ($this->isProtected()) { 2020a5b05ebSAndreas Gohr $value = $this->protected; 2030a5b05ebSAndreas Gohr $disable = 'disabled="disabled"'; 204467c1427SAndreas Gohr } elseif ($echo && $this->error) { 2050a5b05ebSAndreas Gohr $value = $this->input; 2060a5b05ebSAndreas Gohr } else { 2070a5b05ebSAndreas Gohr $value = is_null($this->local) ? $this->default : $this->local; 2080a5b05ebSAndreas Gohr } 2090a5b05ebSAndreas Gohr 2100a5b05ebSAndreas Gohr $key = htmlspecialchars($this->key); 2110a5b05ebSAndreas Gohr $value = formText($value); 2120a5b05ebSAndreas Gohr 2130a5b05ebSAndreas Gohr $label = '<label for="config___' . $key . '">' . $this->prompt($plugin) . '</label>'; 2140a5b05ebSAndreas Gohr $input = '<textarea rows="3" cols="40" id="config___' . $key . 2150a5b05ebSAndreas Gohr '" name="config[' . $key . ']" class="edit" ' . $disable . '>' . $value . '</textarea>'; 216467c1427SAndreas Gohr return [$label, $input]; 2170a5b05ebSAndreas Gohr } 2180a5b05ebSAndreas Gohr 2190a5b05ebSAndreas Gohr /** 220f00299d8SAndreas Gohr * Should the current local value be saved? 2210a5b05ebSAndreas Gohr * 222f00299d8SAndreas Gohr * @see out() to run when this returns true 223f00299d8SAndreas Gohr * @return bool 224f00299d8SAndreas Gohr */ 225d868eb89SAndreas Gohr public function shouldBeSaved() 226d868eb89SAndreas Gohr { 227f00299d8SAndreas Gohr if ($this->isProtected()) return false; 228f00299d8SAndreas Gohr if ($this->local === null) return false; 229f00299d8SAndreas Gohr if ($this->default == $this->local) return false; 230f00299d8SAndreas Gohr return true; 231f00299d8SAndreas Gohr } 232f00299d8SAndreas Gohr 233f00299d8SAndreas Gohr /** 2340772dde2SAndreas Gohr * Escaping 2350772dde2SAndreas Gohr * 2360772dde2SAndreas Gohr * @param string $string 2370772dde2SAndreas Gohr * @return string 2380772dde2SAndreas Gohr */ 239d868eb89SAndreas Gohr protected function escape($string) 240d868eb89SAndreas Gohr { 241467c1427SAndreas Gohr $tr = ["\\" => '\\\\', "'" => '\\\'']; 2420772dde2SAndreas Gohr return "'" . strtr(cleanText($string), $tr) . "'"; 2430772dde2SAndreas Gohr } 2440772dde2SAndreas Gohr 2450772dde2SAndreas Gohr /** 246f00299d8SAndreas Gohr * Generate string to save local setting value to file according to $fmt 247f00299d8SAndreas Gohr * 248f00299d8SAndreas Gohr * @see shouldBeSaved() to check if this should be called 2490a5b05ebSAndreas Gohr * @param string $var name of variable 2500a5b05ebSAndreas Gohr * @param string $fmt save format 2510a5b05ebSAndreas Gohr * @return string 2520a5b05ebSAndreas Gohr */ 253d868eb89SAndreas Gohr public function out($var, $fmt = 'php') 254d868eb89SAndreas Gohr { 255f00299d8SAndreas Gohr if ($fmt != 'php') return ''; 2560a5b05ebSAndreas Gohr 2570772dde2SAndreas Gohr if (is_array($this->local)) { 258467c1427SAndreas Gohr $value = 'array(' . implode(', ', array_map([$this, 'escape'], $this->local)) . ')'; 2590772dde2SAndreas Gohr } else { 2600772dde2SAndreas Gohr $value = $this->escape($this->local); 2610772dde2SAndreas Gohr } 2620772dde2SAndreas Gohr 2630772dde2SAndreas Gohr $out = '$' . $var . "['" . $this->getArrayKey() . "'] = $value;\n"; 2640a5b05ebSAndreas Gohr 2650a5b05ebSAndreas Gohr return $out; 2660a5b05ebSAndreas Gohr } 2670a5b05ebSAndreas Gohr 2680a5b05ebSAndreas Gohr /** 2690a5b05ebSAndreas Gohr * Returns the localized prompt 2700a5b05ebSAndreas Gohr * 2710a5b05ebSAndreas Gohr * @param \admin_plugin_config $plugin object of config plugin 2720a5b05ebSAndreas Gohr * @return string text 2730a5b05ebSAndreas Gohr */ 274d868eb89SAndreas Gohr public function prompt(\admin_plugin_config $plugin) 275d868eb89SAndreas Gohr { 2760a5b05ebSAndreas Gohr $prompt = $plugin->getLang($this->key); 277467c1427SAndreas Gohr if (!$prompt) $prompt = htmlspecialchars(str_replace(['____', '_'], ' ', $this->key)); 2780a5b05ebSAndreas Gohr return $prompt; 2790a5b05ebSAndreas Gohr } 2800a5b05ebSAndreas Gohr 2810a5b05ebSAndreas Gohr /** 2820a5b05ebSAndreas Gohr * Is setting protected 2830a5b05ebSAndreas Gohr * 2840a5b05ebSAndreas Gohr * @return bool 2850a5b05ebSAndreas Gohr */ 286d868eb89SAndreas Gohr public function isProtected() 287d868eb89SAndreas Gohr { 2880a5b05ebSAndreas Gohr return !is_null($this->protected); 2890a5b05ebSAndreas Gohr } 2900a5b05ebSAndreas Gohr 2910a5b05ebSAndreas Gohr /** 2920a5b05ebSAndreas Gohr * Is setting the default? 2930a5b05ebSAndreas Gohr * 2940a5b05ebSAndreas Gohr * @return bool 2950a5b05ebSAndreas Gohr */ 296d868eb89SAndreas Gohr public function isDefault() 297d868eb89SAndreas Gohr { 2980a5b05ebSAndreas Gohr return !$this->isProtected() && is_null($this->local); 2990a5b05ebSAndreas Gohr } 3000a5b05ebSAndreas Gohr 3010a5b05ebSAndreas Gohr /** 3020a5b05ebSAndreas Gohr * Has an error? 3030a5b05ebSAndreas Gohr * 3040a5b05ebSAndreas Gohr * @return bool 3050a5b05ebSAndreas Gohr */ 306d868eb89SAndreas Gohr public function hasError() 307d868eb89SAndreas Gohr { 3080a5b05ebSAndreas Gohr return $this->error; 3090a5b05ebSAndreas Gohr } 3100a5b05ebSAndreas Gohr 3110a5b05ebSAndreas Gohr /** 3120a5b05ebSAndreas Gohr * Returns caution 3130a5b05ebSAndreas Gohr * 3140a5b05ebSAndreas Gohr * @return false|string caution string, otherwise false for invalid caution 3150a5b05ebSAndreas Gohr */ 316d868eb89SAndreas Gohr public function caution() 317d868eb89SAndreas Gohr { 318f74addc3SAndreas Gohr if (empty($this->caution)) return false; 3190a5b05ebSAndreas Gohr if (!in_array($this->caution, Setting::$validCautions)) { 32098a151baSAndreas Gohr throw new \RuntimeException( 32198a151baSAndreas Gohr 'Invalid caution string (' . $this->caution . ') in metadata for setting "' . $this->key . '"' 3220a5b05ebSAndreas Gohr ); 3230a5b05ebSAndreas Gohr } 3240a5b05ebSAndreas Gohr return $this->caution; 3250a5b05ebSAndreas Gohr } 3260a5b05ebSAndreas Gohr} 327