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