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