xref: /plugin/struct/types/Dropdown.php (revision d6177a87fb2d88a366cb7caf11a3c39013eb137a)
1<?php
2namespace dokuwiki\plugin\struct\types;
3
4use dokuwiki\plugin\struct\meta\Column;
5use dokuwiki\plugin\struct\meta\QueryBuilder;
6use dokuwiki\plugin\struct\meta\Schema;
7use dokuwiki\plugin\struct\meta\Search;
8use dokuwiki\plugin\struct\meta\Value;
9
10class Dropdown extends AbstractBaseType {
11
12    protected $config = array(
13        'values' => 'one, two, three',
14        'schema' => '',
15        'field' => ''
16    );
17
18    /** @var Schema */
19    protected $schema = null;
20    /** @var Column */
21    protected $column = null;
22
23    /**
24     * Dropdown constructor.
25     *
26     * @param array|null $config
27     * @param string $label
28     * @param bool $ismulti
29     * @param int $tid
30     */
31    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) {
32        global $conf;
33
34        parent::__construct($config, $label, $ismulti, $tid);
35        $this->config['schema'] = Schema::cleanTableName($this->config['schema']);
36
37        if($this->usesLookup()) {
38            $this->schema = new Schema($this->config['schema']);
39            if(!$this->schema->getId()) {
40                // schema does not exist
41                msg(sprintf('Schema %s does not exist', $this->config['schema']), -1);
42                $this->schema = null;
43                $this->config['schema'] = '';
44                return;
45            }
46
47            // apply language replacement
48            $field = str_replace('$LANG', $conf['lang'], $this->config['field']);
49            $this->column = $this->schema->findColumn($field);
50            if(!$this->column) {
51                $field = str_replace('$LANG', 'en', $this->config['field']); // fallback to en
52                $this->column = $this->schema->findColumn($field);
53            }
54            if(!$this->column) {
55                // field does not exist
56                msg(sprintf('Field %s.%s does not exist', $this->config['schema'], $this->config['field']), -1);
57                $this->column = null;
58                $this->config['field'] = '';
59                return;
60            }
61
62            if($this->column->isMulti()) {
63                // field is multi
64                msg(sprintf('Field %s.%s is a multi field - not allowed for lookup', $this->config['schema'], $this->config['field']), -1);
65                $this->column = null;
66                $this->config['field'] = '';
67                return;
68            }
69        }
70    }
71
72    /**
73     * @return bool is this dropdown configured to use a lookup?
74     */
75    protected function usesLookup() {
76        return !blank($this->config['schema']) && !blank($this->config['field']);
77    }
78
79    /**
80     * Creates the options array
81     *
82     * @return array
83     */
84    protected function getOptions() {
85        if($this->usesLookup()) {
86            $options = $this->loadLookupData();
87        } else {
88            $options = explode(',', $this->config['values']);
89            $options = array_map('trim', $options);
90            $options = array_filter($options);
91            array_unshift($options, '');
92            $options = array_combine($options, $options);
93        }
94        return $options;
95    }
96
97    /**
98     * Loads all available lookup values
99     *
100     * @return array
101     */
102    protected function loadLookupData() {
103        $schema = $this->schema->getTable();
104        $field = $this->column->getLabel();
105
106        $search = new Search();
107        $search->addSchema($schema);
108        $search->addColumn($field);
109        $search->addSort($field);
110        $result = $search->execute();
111        $pids = $search->getPids();
112        $len = count($result);
113
114        /** @var Value[][] $result */
115        $options = array('' => '');
116        for($i = 0; $i < $len; $i++) {
117            $options[$pids[$i]] = $result[$i][0]->getDisplayValue();
118        }
119        return $options;
120    }
121
122    /**
123     * Render using linked field
124     *
125     * @param int|string $value
126     * @param \Doku_Renderer $R
127     * @param string $mode
128     * @return bool
129     */
130    public function renderValue($value, \Doku_Renderer $R, $mode) {
131        if(!$this->usesLookup()) {
132            return parent::renderValue($value, $R, $mode);
133        } else {
134            list(, $value) = json_decode($value);
135            return $this->column->getType()->renderValue($value, $R, $mode);
136        }
137    }
138
139    /**
140     * Render using linked field
141     *
142     * @param \int[]|\string[] $values
143     * @param \Doku_Renderer $R
144     * @param string $mode
145     * @return bool
146     */
147    public function renderMultiValue($values, \Doku_Renderer $R, $mode) {
148        if(!$this->usesLookup()) {
149            return parent::renderMultiValue($values, $R, $mode);
150        } else {
151            $values = array_map(
152                function ($val) {
153                    list(, $val) = json_decode($val);
154                    return $val;
155                }, $values
156            );
157            return $this->column->getType()->renderMultiValue($values, $R, $mode);
158        }
159    }
160
161    /**
162     * A Dropdown with a single value to pick
163     *
164     * @param string $name
165     * @param string $value
166     * @param bool $isRaw ignored
167     * @return string
168     */
169    public function valueEditor($name, $value, $isRaw = false) {
170        $class = 'struct_' . strtolower($this->getClass());
171
172        if(!$isRaw) $value = $this->rawValue($value);
173
174        $name = hsc($name);
175        $html = "<select name=\"$name\" class=\"$class\">";
176        foreach($this->getOptions() as $opt => $val) {
177            if($opt == $value) {
178                $selected = 'selected="selected"';
179            } else {
180                $selected = '';
181            }
182
183            $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($val) . '</option>';
184        }
185        $html .= '</select>';
186
187        return $html;
188    }
189
190    /**
191     * A dropdown that allows to pick multiple values
192     *
193     * @param string $name
194     * @param \string[] $values
195     * @return string
196     */
197    public function multiValueEditor($name, $values) {
198        $class = 'struct_' . strtolower($this->getClass());
199
200        $values = array_map(array($this, 'rawValue'), $values);
201
202        $name = hsc($name);
203        $html = "<select name=\"{$name}[]\" class=\"$class\" multiple=\"multiple\" size=\"5\">";
204        foreach($this->getOptions() as $opt) {
205            if(in_array($opt, $values)) {
206                $selected = 'selected="selected"';
207            } else {
208                $selected = '';
209            }
210
211            $html .= "<option $selected value=\"" . hsc($opt) . "\">" . hsc($opt) . '</option>';
212
213        }
214        $html .= '</select> ';
215        $html .= '<small>' . $this->getLang('multidropdown') . '</small>';
216        return $html;
217    }
218
219    /**
220     * @param string $value
221     * @return string
222     */
223    public function rawValue($value) {
224        if($this->usesLookup()) {
225            list($value) = json_decode($value);
226        }
227        return $value;
228    }
229
230    /**
231     * @param string $value
232     * @return string
233     */
234    public function displayValue($value) {
235        if($this->usesLookup()) {
236            list(, $value) = json_decode($value);
237            $value = $this->column->getType()->displayValue($value);
238        }
239        return $value;
240    }
241
242    /**
243     * Merge with lookup table
244     *
245     * @param QueryBuilder $QB
246     * @param string $tablealias
247     * @param string $colname
248     * @param string $alias
249     */
250    public function select(QueryBuilder $QB, $tablealias, $colname, $alias) {
251        if(!$this->usesLookup()) {
252            parent::select($QB, $tablealias, $colname, $alias);
253            return;
254        }
255
256        $schema = 'data_' . $this->schema->getTable();
257        $field = $this->column->getColName();
258
259        $rightalias = $QB->generateTableAlias();
260        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
261        $this->column->getType()->select($QB, $rightalias, $field, $alias);
262        $sql = $QB->getSelectStatement($alias);
263        $QB->addSelectStatement("JSON($tablealias.$colname, $sql)", $alias);
264    }
265
266    /**
267     * Compare against lookup table
268     *
269     * @param QueryBuilder $QB
270     * @param string $tablealias
271     * @param string $colname
272     * @param string $comp
273     * @param string|\string[] $value
274     * @param string $op
275     */
276    public function filter(QueryBuilder $QB, $tablealias, $colname, $comp, $value, $op) {
277        if(!$this->usesLookup()) {
278            parent::filter($QB, $tablealias, $colname, $comp, $value, $op);
279            return;
280        }
281
282        $schema = 'data_' . $this->schema->getTable();
283        $field = $this->column->getColName();
284
285        // compare against lookup field
286        $rightalias = $QB->generateTableAlias();
287        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
288        $this->column->getType()->filter($QB, $rightalias, $field, $comp, $value, $op);
289    }
290
291    /**
292     * Sort by lookup table
293     *
294     * @param QueryBuilder $QB
295     * @param string $tablealias
296     * @param string $colname
297     * @param string $order
298     */
299    public function sort(QueryBuilder $QB, $tablealias, $colname, $order) {
300        if(!$this->usesLookup()) {
301            parent::sort($QB, $tablealias, $colname, $order);
302            return;
303        }
304
305        $schema = 'data_' . $this->schema->getTable();
306        $field = $this->column->getColName();
307
308        $rightalias = $QB->generateTableAlias();
309        $QB->addLeftJoin($tablealias, $schema, $rightalias, "$tablealias.$colname = $rightalias.pid");
310        $this->column->getType()->sort($QB, $rightalias, $field, $order);
311    }
312
313}
314