xref: /plugin/struct/types/AbstractBaseType.php (revision fd81b928afb513ef792d1537a28003c99cab3622)
1<?php
2namespace plugin\struct\types;
3
4use dokuwiki\Form\Form;
5use plugin\struct\meta\ValidationException;
6
7/**
8 * Class AbstractBaseType
9 *
10 * This class represents a basic type that can be configured to be used in a Schema. It is the main
11 * part of a column definition as defined in meta\Column
12 *
13 * This defines also how the content of the coulmn will be entered and formatted.
14 *
15 * @package plugin\struct\types
16 * @see Column
17 */
18abstract class AbstractBaseType {
19
20    /**
21     * @var array current config
22     */
23    protected $config = array();
24
25    /**
26     * @var array config keys that should not be cleaned despite not being in $config
27     */
28    protected $keepconfig = array('translation');
29
30    /**
31     * @var string label for the field
32     */
33    protected $label = '';
34
35    /**
36     * @var bool is this a multivalue field?
37     */
38    protected $ismulti = false;
39
40    /**
41     * @var int the type ID
42     */
43    protected $tid = 0;
44
45    /**
46     * AbstractBaseType constructor.
47     * @param array|null $config The configuration, might be null if nothing saved, yet
48     * @param string $label The label for this field (empty for new definitions=
49     * @param bool $ismulti Should this field accept multiple values?
50     * @param int $tid The id of this type if it has been saved, yet
51     */
52    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0) {
53        // initialize the configuration, ignoring all keys that are not supposed to be here
54        if(!is_null($config)) {
55            foreach($config as $key => $value) {
56                if(isset($this->config[$key]) || in_array($key, $this->keepconfig)) {
57                    $this->config[$key] = $value;
58                }
59            }
60        }
61
62        $this->initTransConfig();
63        $this->label = $label;
64        $this->ismulti = (bool) $ismulti;
65        $this->tid = $tid;
66    }
67
68    /**
69     * Add the translation keys to the configuration
70     *
71     * This checks if a configuration for the translation plugin exists and if so
72     * adds all configured languages to the config array. This ensures all types
73     * can have translatable labels.
74     */
75    protected function initTransConfig() {
76        global $conf;
77        $lang = $conf['lang'];
78        if(isset($conf['plugin']['translation']['translations'])) {
79            $lang .= ' ' . $conf['plugin']['translation']['translations'];
80        }
81        $langs = explode(' ', $lang);
82        $langs = array_map('trim', $langs);
83        $langs = array_filter($langs);
84        $langs = array_unique($langs);
85
86        if(!isset($this->config['translation'])) $this->config['translation'] = array();
87        foreach($langs as $lang) {
88            if(!isset($this->config['translation'][$lang])) $this->config['translation'][$lang] = '';
89        }
90    }
91
92    /**
93     * Returns data as associative array
94     *
95     * @return array
96     */
97    public function getAsEntry() {
98        return array(
99            'config' => json_encode($this->config),
100            'label' => $this->label,
101            'ismulti' => $this->ismulti,
102            'class' => $this->getClass()
103        );
104    }
105
106    /**
107     * The class name of this type (no namespace)
108     * @return string
109     */
110    public function getClass() {
111        return substr(get_class($this), 20);
112    }
113
114    /**
115     * Return the current configuration for this type
116     *
117     * @return array
118     */
119    public function getConfig() {
120        return $this->config;
121    }
122
123    /**
124     * @return boolean
125     */
126    public function isMulti() {
127        return $this->ismulti;
128    }
129
130    /**
131     * @return string
132     */
133    public function getLabel() {
134        return $this->label;
135    }
136
137    /**
138     * Returns the translated label for this type
139     *
140     * Uses the current language as determined by $conf['lang']. Falls back to english
141     * and then to the Schema label
142     *
143     * @return string
144     */
145    public function getTranslatedLabel() {
146        global $conf;
147        $lang = $conf['lang'];
148        if(!blank($this->config['translation'][$lang])) {
149            return $this->config['translation'][$lang];
150        }
151        if(!blank($this->config['translation']['en'])) {
152            return $this->config['translation']['en'];
153        }
154        return $this->label;
155    }
156
157    /**
158     * @return int
159     */
160    public function getTid() {
161        return $this->tid;
162    }
163
164    /**
165     * Split a single value into multiple values
166     *
167     * This function is called on saving data when only a single value instead of an array
168     * was submitted.
169     *
170     * Types implementing their own @see multiValueEditor() will probably want to override this
171     *
172     * @param string $value
173     * @return array
174     */
175    public function splitValues($value) {
176        return array_map('trim', explode(',', $value));
177    }
178
179    /**
180     * Return the editor to edit multiple values
181     *
182     * Types can override this to provide a better alternative than multiple entry fields
183     *
184     * @param string $name the form base name where this has to be stored
185     * @param string[] $values the current values
186     * @return string html
187     */
188    public function multiValueEditor($name, $values) {
189        $html = '';
190        foreach($values as $value) {
191            $html .= '<div class="multiwrap">';
192            $html .= $this->valueEditor($name . '[]', $value);
193            $html .= '</div>';
194        }
195        // empty field to add
196        $html .= '<div class="newtemplate">';
197        $html .= '<div class="multiwrap">';
198        $html .= $this->valueEditor($name . '[]', '');
199        $html .= '</div>';
200        $html .= '</div>';
201
202        return $html;
203    }
204
205    /**
206     * Return the editor to edit a single value
207     *
208     * @param string $name the form name where this has to be stored
209     * @param string $value the current value
210     * @return string html
211     */
212    public function valueEditor($name, $value) {
213        $name = hsc($name);
214        $value = hsc($value);
215        $html = "<input name=\"$name\" value=\"$value\" />";
216        return "$html";
217    }
218
219    /**
220     * Output the stored data
221     *
222     * @param string|int $value the value stored in the database
223     * @param \Doku_Renderer $R the renderer currently used to render the data
224     * @param string $mode The mode the output is rendered in (eg. XHTML)
225     * @return bool true if $mode could be satisfied
226     */
227    public function renderValue($value, \Doku_Renderer $R, $mode) {
228        $R->cdata($value);
229        return true;
230    }
231
232    /**
233     * format and return the data
234     *
235     * @param int[]|string[] $values the values stored in the database
236     * @param \Doku_Renderer $R the renderer currently used to render the data
237     * @param string $mode The mode the output is rendered in (eg. XHTML)
238     * @return bool true if $mode could be satisfied
239     */
240    public function renderMultiValue($values, \Doku_Renderer $R, $mode) {
241        $len = count($values);
242        for($i = 0; $i < $len; $i++) {
243            $this->renderValue($values[$i], $R, $mode);
244            if($i < $len - 1) {
245                $R->cdata(', ');
246            }
247        }
248        return true;
249    }
250
251    /**
252     * This function builds a where clause for this column, comparing
253     * the current value stored in $column with $value. Types can use it to do
254     * clever things with the comparison.
255     *
256     * This default implementation is probably good enough for most basic types
257     *
258     * @param string $column The column name to us in the SQL
259     * @param string $comp The comparator @see Search::$COMPARATORS
260     * @param string $value
261     * @return array Tuple with the SQL and parameter array
262     */
263    public function compare($column, $comp, $value) {
264        switch ($comp) {
265            case '~':
266                $sql = "$column LIKE ?";
267                $opt = array($value);
268                break;
269            case '!~':
270                $sql = "$column NOT LIKE ?";
271                $opt = array($value);
272                break;
273            default:
274                $sql = "$column $comp ?";
275                $opt = array($value);
276        }
277
278        return array($sql, $opt);
279    }
280
281    /**
282     * Validate a single value
283     *
284     * This function needs to throw a validation exception when validation fails.
285     * The exception message will be prefixed by the appropriate field on output
286     *
287     * @param string|int $value
288     * @throws ValidationException
289     */
290    public function validate($value) {
291        // nothing by default - we allow everything
292    }
293}
294