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