xref: /dokuwiki/lib/plugins/config/core/Setting/SettingMulticheckbox.php (revision 90fb952c4c30c09c8446076ba05991c89a3f0b01)
1<?php
2
3namespace dokuwiki\plugin\config\core\Setting;
4
5/**
6 * Class setting_multicheckbox
7 */
8class SettingMulticheckbox extends SettingString
9{
10
11    protected $choices = [];
12    protected $combine = [];
13    protected $other = 'always';
14
15    /** @inheritdoc */
16    public function update($input)
17    {
18        if ($this->isProtected()) return false;
19
20        // split any combined values + convert from array to comma separated string
21        $input = $input ?: [];
22        $input = $this->array2str($input);
23
24        $value = is_null($this->local) ? $this->default : $this->local;
25        if ($value == $input) return false;
26
27        if ($this->pattern && !preg_match($this->pattern, $input)) {
28            $this->error = true;
29            $this->input = $input;
30            return false;
31        }
32
33        $this->local = $input;
34        return true;
35    }
36
37    /** @inheritdoc */
38    public function html(\admin_plugin_config $plugin, $echo = false)
39    {
40
41        $disable = '';
42
43        if ($this->isProtected()) {
44            $value = $this->protected;
45            $disable = 'disabled="disabled"';
46        } elseif ($echo && $this->error) {
47            $value = $this->input;
48        } else {
49            $value = is_null($this->local) ? $this->default : $this->local;
50        }
51
52        $key = htmlspecialchars($this->key);
53
54        // convert from comma separated list into array + combine complimentary actions
55        $value = $this->str2array($value);
56        $default = $this->str2array($this->default);
57
58        $input = '';
59        foreach ($this->choices as $choice) {
60            $idx = array_search($choice, $value);
61            $idx_default = array_search($choice, $default);
62
63            $checked = ($idx !== false) ? 'checked="checked"' : '';
64
65            // @todo ideally this would be handled using a second class of "default"
66            $class = (($idx !== false) === (false !== $idx_default)) ? " selectiondefault" : "";
67
68            $prompt = ($plugin->getLang($this->key . '_' . $choice) ?: htmlspecialchars($choice));
69
70            $input .= '<div class="selection' . $class . '">' . "\n";
71            $input .= '<label for="config___' . $key . '_' . $choice . '">' . $prompt . "</label>\n";
72            $input .= '<input id="config___' . $key . '_' . $choice . '" name="config[' . $key .
73                '][]" type="checkbox" class="checkbox" value="' . $choice . '" ' . $disable . ' ' . $checked . "/>\n";
74            $input .= "</div>\n";
75
76            // remove this action from the disabledactions array
77            if ($idx !== false) unset($value[$idx]);
78            if ($idx_default !== false) unset($default[$idx_default]);
79        }
80
81        // handle any remaining values
82        if ($this->other != 'never') {
83            $other = implode(',', $value);
84            // test equivalent to ($this->_other == 'always' || ($other && $this->_other == 'exists')
85            // use != 'exists' rather than == 'always' to ensure invalid values default to 'always'
86            if ($this->other != 'exists' || $other) {
87                $class = (
88                    (count($default) === count($value)) &&
89                    (count($value) === count(array_intersect($value, $default)))
90                ) ?
91                    " selectiondefault" : "";
92
93                $input .= '<div class="other' . $class . '">' . "\n";
94                $input .= '<label for="config___' . $key . '_other">' .
95                    $plugin->getLang($key . '_other') .
96                    "</label>\n";
97                $input .= '<input id="config___' . $key . '_other" name="config[' . $key .
98                    '][other]" type="text" class="edit" value="' . htmlspecialchars($other) .
99                    '" ' . $disable . " />\n";
100                $input .= "</div>\n";
101            }
102        }
103        $label = '<label>' . $this->prompt($plugin) . '</label>';
104        return [$label, $input];
105    }
106
107    /**
108     * convert comma separated list to an array and combine any complimentary values
109     *
110     * @param string $str
111     * @return array
112     */
113    protected function str2array($str)
114    {
115        $array = explode(',', $str);
116
117        if (!empty($this->combine)) {
118            foreach ($this->combine as $key => $combinators) {
119                $idx = [];
120                foreach ($combinators as $val) {
121                    if (($idx[] = array_search($val, $array)) === false) break;
122                }
123
124                if (count($idx) && $idx[count($idx) - 1] !== false) {
125                    foreach ($idx as $i) unset($array[$i]);
126                    $array[] = $key;
127                }
128            }
129        }
130
131        return $array;
132    }
133
134    /**
135     * convert array of values + other back to a comma separated list, incl. splitting any combined values
136     *
137     * @param array $input
138     * @return string
139     */
140    protected function array2str($input)
141    {
142
143        // handle other
144        $other = trim($input['other']);
145        $other = empty($other) ? [] : explode(',', str_replace(' ', '', $input['other']));
146        unset($input['other']);
147
148        $array = array_unique(array_merge($input, $other));
149
150        // deconstruct any combinations
151        if (!empty($this->combine)) {
152            foreach ($this->combine as $key => $combinators) {
153                $idx = array_search($key, $array);
154                if ($idx !== false) {
155                    unset($array[$idx]);
156                    $array = array_merge($array, $combinators);
157                }
158            }
159        }
160
161        return implode(',', array_unique($array));
162    }
163}
164