xref: /dokuwiki/lib/plugins/config/core/Setting/Setting.php (revision 1b2deed9152e2f2c24d7fb535f0b57093e2ede56)
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