xref: /plugin/struct/types/AbstractBaseType.php (revision 5e29103a15bd9873f422f66a6a5239b6aec4651e)
1083afc55SAndreas Gohr<?php
2d6d97f60SAnna Dabrowska
3ba766201SAndreas Gohrnamespace dokuwiki\plugin\struct\types;
42350b48eSAndreas Gohr
57234bfb1Ssplitbrainuse dokuwiki\Extension\Plugin;
6ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Column;
7384e59cbSAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilder;
853528ecfSAndreas Gohruse dokuwiki\plugin\struct\meta\QueryBuilderWhere;
9ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\StructException;
10f800af69SMichael Großeuse dokuwiki\plugin\struct\meta\TranslationUtilities;
11ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\ValidationException;
12d560ea13SMichael Grosseuse dokuwiki\plugin\struct\meta\Value;
13083afc55SAndreas Gohr
141c502704SAndreas Gohr/**
151c502704SAndreas Gohr * Class AbstractBaseType
161c502704SAndreas Gohr *
171c502704SAndreas Gohr * This class represents a basic type that can be configured to be used in a Schema. It is the main
181c502704SAndreas Gohr * part of a column definition as defined in meta\Column
191c502704SAndreas Gohr *
207182938bSAndreas Gohr * This defines also how the content of the coulmn will be entered and formatted.
217182938bSAndreas Gohr *
22ba766201SAndreas Gohr * @package dokuwiki\plugin\struct\types
237182938bSAndreas Gohr * @see Column
241c502704SAndreas Gohr */
25d6d97f60SAnna Dabrowskaabstract class AbstractBaseType
26d6d97f60SAnna Dabrowska{
27f800af69SMichael Große    use TranslationUtilities;
28f800af69SMichael Große
29083afc55SAndreas Gohr    /**
30083afc55SAndreas Gohr     * @var array current config
31083afc55SAndreas Gohr     */
327234bfb1Ssplitbrain    protected $config = [];
33083afc55SAndreas Gohr
34083afc55SAndreas Gohr    /**
354d19af58SAndreas Gohr     * @var array config keys that should not be cleaned despite not being in $config
364d19af58SAndreas Gohr     */
377234bfb1Ssplitbrain    protected $keepconfig = ['label', 'hint', 'visibility'];
384d19af58SAndreas Gohr
394d19af58SAndreas Gohr    /**
40083afc55SAndreas Gohr     * @var string label for the field
41083afc55SAndreas Gohr     */
42083afc55SAndreas Gohr    protected $label = '';
43083afc55SAndreas Gohr
44083afc55SAndreas Gohr    /**
45083afc55SAndreas Gohr     * @var bool is this a multivalue field?
46083afc55SAndreas Gohr     */
47083afc55SAndreas Gohr    protected $ismulti = false;
48083afc55SAndreas Gohr
49083afc55SAndreas Gohr    /**
5004eb61a6SAndreas Gohr     * @var int the type ID
5104eb61a6SAndreas Gohr     */
5204eb61a6SAndreas Gohr    protected $tid = 0;
5304eb61a6SAndreas Gohr
5404eb61a6SAndreas Gohr    /**
55bbf3d6aaSAndreas Gohr     * @var null|Column the column context this type is part of
56bbf3d6aaSAndreas Gohr     */
577234bfb1Ssplitbrain    protected $context;
58bbf3d6aaSAndreas Gohr
59bbf3d6aaSAndreas Gohr    /**
607234bfb1Ssplitbrain     * @var Plugin
6153c92fe4SAndreas Gohr     */
627234bfb1Ssplitbrain    protected $hlp;
6353c92fe4SAndreas Gohr
6453c92fe4SAndreas Gohr    /**
65083afc55SAndreas Gohr     * AbstractBaseType constructor.
66083afc55SAndreas Gohr     * @param array|null $config The configuration, might be null if nothing saved, yet
67083afc55SAndreas Gohr     * @param string $label The label for this field (empty for new definitions=
68083afc55SAndreas Gohr     * @param bool $ismulti Should this field accept multiple values?
6904eb61a6SAndreas Gohr     * @param int $tid The id of this type if it has been saved, yet
70083afc55SAndreas Gohr     */
71d6d97f60SAnna Dabrowska    public function __construct($config = null, $label = '', $ismulti = false, $tid = 0)
72d6d97f60SAnna Dabrowska    {
732350b48eSAndreas Gohr        // general config options
747fe2cdf2SAndreas Gohr        $baseconfig = [
757fe2cdf2SAndreas Gohr            'visibility' => [
767fe2cdf2SAndreas Gohr                'inpage' => true,
777fe2cdf2SAndreas Gohr                'ineditor' => true
787fe2cdf2SAndreas Gohr            ]
797fe2cdf2SAndreas Gohr        ];
802350b48eSAndreas Gohr
812350b48eSAndreas Gohr        // use previously saved configuration, ignoring all keys that are not supposed to be here
824d19af58SAndreas Gohr        if (!is_null($config)) {
83ace65259SAndreas Gohr            $this->mergeConfig($config, $this->config);
844d19af58SAndreas Gohr        }
854d19af58SAndreas Gohr
869e9bee91SAndreas Gohr        $this->initTransConfig();
872350b48eSAndreas Gohr        $this->config = array_merge($baseconfig, $this->config);
88083afc55SAndreas Gohr        $this->label = $label;
89083afc55SAndreas Gohr        $this->ismulti = (bool)$ismulti;
9004eb61a6SAndreas Gohr        $this->tid = $tid;
911c502704SAndreas Gohr    }
921c502704SAndreas Gohr
931c502704SAndreas Gohr    /**
94ace65259SAndreas Gohr     * Merge the current config with the base config of the type
95ace65259SAndreas Gohr     *
96ace65259SAndreas Gohr     * Ignores all keys that are not supposed to be there. Recurses into sub keys
97ace65259SAndreas Gohr     *
98ace65259SAndreas Gohr     * @param array $current Current configuration
99ace65259SAndreas Gohr     * @param array $config Base Type configuration
100ace65259SAndreas Gohr     */
101d6d97f60SAnna Dabrowska    protected function mergeConfig($current, &$config)
102d6d97f60SAnna Dabrowska    {
103ace65259SAndreas Gohr        foreach ($current as $key => $value) {
104ace65259SAndreas Gohr            if (isset($config[$key]) || in_array($key, $this->keepconfig)) {
1059007da58SMichael Große                if (isset($config[$key]) && is_array($config[$key])) {
106ace65259SAndreas Gohr                    $this->mergeConfig($value, $config[$key]);
107ace65259SAndreas Gohr                } else {
108ace65259SAndreas Gohr                    $config[$key] = $value;
109ace65259SAndreas Gohr                }
110ace65259SAndreas Gohr            }
111ace65259SAndreas Gohr        }
112ace65259SAndreas Gohr    }
113ace65259SAndreas Gohr
114ace65259SAndreas Gohr    /**
1151c502704SAndreas Gohr     * Returns data as associative array
1161c502704SAndreas Gohr     *
1171c502704SAndreas Gohr     * @return array
1181c502704SAndreas Gohr     */
119d6d97f60SAnna Dabrowska    public function getAsEntry()
120d6d97f60SAnna Dabrowska    {
1217fe2cdf2SAndreas Gohr        return [
122*5e29103aSannda            'config' => json_encode($this->config, JSON_THROW_ON_ERROR),
1237fe2cdf2SAndreas Gohr            'label' => $this->label,
1247fe2cdf2SAndreas Gohr            'ismulti' => $this->ismulti,
1257fe2cdf2SAndreas Gohr            'class' => $this->getClass()
1267fe2cdf2SAndreas Gohr        ];
1271c502704SAndreas Gohr    }
1281c502704SAndreas Gohr
1291c502704SAndreas Gohr    /**
1301c502704SAndreas Gohr     * The class name of this type (no namespace)
1311c502704SAndreas Gohr     * @return string
1321c502704SAndreas Gohr     */
133d6d97f60SAnna Dabrowska    public function getClass()
134d6d97f60SAnna Dabrowska    {
135ba766201SAndreas Gohr        $class = get_class($this);
136ba766201SAndreas Gohr        return substr($class, strrpos($class, "\\") + 1);
137083afc55SAndreas Gohr    }
138083afc55SAndreas Gohr
139083afc55SAndreas Gohr    /**
140083afc55SAndreas Gohr     * Return the current configuration for this type
141083afc55SAndreas Gohr     *
142083afc55SAndreas Gohr     * @return array
143083afc55SAndreas Gohr     */
144d6d97f60SAnna Dabrowska    public function getConfig()
145d6d97f60SAnna Dabrowska    {
146083afc55SAndreas Gohr        return $this->config;
147083afc55SAndreas Gohr    }
148083afc55SAndreas Gohr
149083afc55SAndreas Gohr    /**
150083afc55SAndreas Gohr     * @return boolean
151083afc55SAndreas Gohr     */
152d6d97f60SAnna Dabrowska    public function isMulti()
153d6d97f60SAnna Dabrowska    {
154083afc55SAndreas Gohr        return $this->ismulti;
155083afc55SAndreas Gohr    }
156083afc55SAndreas Gohr
157083afc55SAndreas Gohr    /**
158083afc55SAndreas Gohr     * @return string
159083afc55SAndreas Gohr     */
160d6d97f60SAnna Dabrowska    public function getLabel()
161d6d97f60SAnna Dabrowska    {
162083afc55SAndreas Gohr        return $this->label;
163083afc55SAndreas Gohr    }
164083afc55SAndreas Gohr
165083afc55SAndreas Gohr    /**
1669e9bee91SAndreas Gohr     * Returns the translated label for this type
1679e9bee91SAndreas Gohr     *
1689e9bee91SAndreas Gohr     * Uses the current language as determined by $conf['lang']. Falls back to english
169f800af69SMichael Große     * and then to the type label
1709e9bee91SAndreas Gohr     *
1719e9bee91SAndreas Gohr     * @return string
1729e9bee91SAndreas Gohr     */
173d6d97f60SAnna Dabrowska    public function getTranslatedLabel()
174d6d97f60SAnna Dabrowska    {
175f800af69SMichael Große        return $this->getTranslatedKey('label', $this->label);
1769e9bee91SAndreas Gohr    }
1779e9bee91SAndreas Gohr
1789e9bee91SAndreas Gohr    /**
1793a717675SAndreas Gohr     * Returns the translated hint for this type
1803a717675SAndreas Gohr     *
1813a717675SAndreas Gohr     * Uses the current language as determined by $conf['lang']. Falls back to english.
1823a717675SAndreas Gohr     * Returns empty string if no hint is configured
1833a717675SAndreas Gohr     *
1843a717675SAndreas Gohr     * @return string
1853a717675SAndreas Gohr     */
186d6d97f60SAnna Dabrowska    public function getTranslatedHint()
187d6d97f60SAnna Dabrowska    {
188f800af69SMichael Große        return $this->getTranslatedKey('hint', '');
1893a717675SAndreas Gohr    }
1903a717675SAndreas Gohr
1913a717675SAndreas Gohr    /**
19204eb61a6SAndreas Gohr     * @return int
19304eb61a6SAndreas Gohr     */
194d6d97f60SAnna Dabrowska    public function getTid()
195d6d97f60SAnna Dabrowska    {
19604eb61a6SAndreas Gohr        return $this->tid;
19704eb61a6SAndreas Gohr    }
19804eb61a6SAndreas Gohr
19904eb61a6SAndreas Gohr    /**
200bbf3d6aaSAndreas Gohr     * @return Column
2010549dcc5SAndreas Gohr     * @throws StructException
202bbf3d6aaSAndreas Gohr     */
203d6d97f60SAnna Dabrowska    public function getContext()
204d6d97f60SAnna Dabrowska    {
20517a3a578SAndreas Gohr        if (is_null($this->context)) {
20617a3a578SAndreas Gohr            throw new StructException(
20717a3a578SAndreas Gohr                'Empty column context requested. Type was probably initialized outside of Schema.'
20817a3a578SAndreas Gohr            );
20917a3a578SAndreas Gohr        }
210bbf3d6aaSAndreas Gohr        return $this->context;
211bbf3d6aaSAndreas Gohr    }
212bbf3d6aaSAndreas Gohr
213bbf3d6aaSAndreas Gohr    /**
214bbf3d6aaSAndreas Gohr     * @param Column $context
215bbf3d6aaSAndreas Gohr     */
216d6d97f60SAnna Dabrowska    public function setContext($context)
217d6d97f60SAnna Dabrowska    {
218bbf3d6aaSAndreas Gohr        $this->context = $context;
219bbf3d6aaSAndreas Gohr    }
220bbf3d6aaSAndreas Gohr
221bbf3d6aaSAndreas Gohr    /**
2222350b48eSAndreas Gohr     * @return bool
2232350b48eSAndreas Gohr     */
224d6d97f60SAnna Dabrowska    public function isVisibleInEditor()
225d6d97f60SAnna Dabrowska    {
2262350b48eSAndreas Gohr        return $this->config['visibility']['ineditor'];
2272350b48eSAndreas Gohr    }
2282350b48eSAndreas Gohr
2292350b48eSAndreas Gohr    /**
2302350b48eSAndreas Gohr     * @return bool
2312350b48eSAndreas Gohr     */
232d6d97f60SAnna Dabrowska    public function isVisibleInPage()
233d6d97f60SAnna Dabrowska    {
2342350b48eSAndreas Gohr        return $this->config['visibility']['inpage'];
2352350b48eSAndreas Gohr    }
2362350b48eSAndreas Gohr
2372350b48eSAndreas Gohr    /**
238899b86acSAndreas Gohr     * Split a single value into multiple values
239899b86acSAndreas Gohr     *
240899b86acSAndreas Gohr     * This function is called on saving data when only a single value instead of an array
241899b86acSAndreas Gohr     * was submitted.
242899b86acSAndreas Gohr     *
2430549dcc5SAndreas Gohr     * Types implementing their own @param string $value
244899b86acSAndreas Gohr     * @return array
2450549dcc5SAndreas Gohr     * @see multiValueEditor() will probably want to override this
2460549dcc5SAndreas Gohr     *
247899b86acSAndreas Gohr     */
248d6d97f60SAnna Dabrowska    public function splitValues($value)
249d6d97f60SAnna Dabrowska    {
250899b86acSAndreas Gohr        return array_map('trim', explode(',', $value));
251899b86acSAndreas Gohr    }
252899b86acSAndreas Gohr
253899b86acSAndreas Gohr    /**
254899b86acSAndreas Gohr     * Return the editor to edit multiple values
255899b86acSAndreas Gohr     *
256899b86acSAndreas Gohr     * Types can override this to provide a better alternative than multiple entry fields
257899b86acSAndreas Gohr     *
258899b86acSAndreas Gohr     * @param string $name the form base name where this has to be stored
259c0230d2cSAndreas Gohr     * @param string[] $rawvalues the current values
260ee983135SMichael Große     * @param string $htmlID a unique id to be referenced by the label
261899b86acSAndreas Gohr     * @return string html
262899b86acSAndreas Gohr     */
263d6d97f60SAnna Dabrowska    public function multiValueEditor($name, $rawvalues, $htmlID)
264d6d97f60SAnna Dabrowska    {
265899b86acSAndreas Gohr        $html = '';
266c0230d2cSAndreas Gohr        foreach ($rawvalues as $value) {
26768da1619SAndreas Gohr            $html .= '<div class="multiwrap">';
268ee983135SMichael Große            $html .= $this->valueEditor($name . '[]', $value, '');
26968da1619SAndreas Gohr            $html .= '</div>';
270899b86acSAndreas Gohr        }
271899b86acSAndreas Gohr        // empty field to add
272a187dd5eSAndreas Gohr        $html .= '<div class="newtemplate">';
27368da1619SAndreas Gohr        $html .= '<div class="multiwrap">';
274ee983135SMichael Große        $html .= $this->valueEditor($name . '[]', '', $htmlID);
275a187dd5eSAndreas Gohr        $html .= '</div>';
27668da1619SAndreas Gohr        $html .= '</div>';
277899b86acSAndreas Gohr
278899b86acSAndreas Gohr        return $html;
279899b86acSAndreas Gohr    }
280899b86acSAndreas Gohr
281899b86acSAndreas Gohr    /**
28231263897SAndreas Gohr     * Return the editor to edit a single value
283083afc55SAndreas Gohr     *
28431263897SAndreas Gohr     * @param string $name the form name where this has to be stored
285c0230d2cSAndreas Gohr     * @param string $rawvalue the current value
286ee983135SMichael Große     * @param string $htmlID a unique id to be referenced by the label
287ee983135SMichael Große     *
28831263897SAndreas Gohr     * @return string html
289083afc55SAndreas Gohr     */
290d6d97f60SAnna Dabrowska    public function valueEditor($name, $rawvalue, $htmlID)
291d6d97f60SAnna Dabrowska    {
292e6a35dceSAndreas Gohr        $class = 'struct_' . strtolower($this->getClass());
293dacfa997SAndreas Gohr
294dacfa997SAndreas Gohr        // support the autocomplete configurations out of the box
295dacfa997SAndreas Gohr        if (isset($this->config['autocomplete']['maxresult']) && $this->config['autocomplete']['maxresult']) {
296dacfa997SAndreas Gohr            $class .= ' struct_autocomplete';
297dacfa997SAndreas Gohr        }
298dacfa997SAndreas Gohr
2997fe2cdf2SAndreas Gohr        $params = [
3007fe2cdf2SAndreas Gohr            'name' => $name,
3017fe2cdf2SAndreas Gohr            'value' => $rawvalue,
3027fe2cdf2SAndreas Gohr            'class' => $class,
3037fe2cdf2SAndreas Gohr            'id' => $htmlID
3047fe2cdf2SAndreas Gohr        ];
3053e7a5b3cSMichael Große        $attributes = buildAttributes($params, true);
3063e7a5b3cSMichael Große        return "<input $attributes>";
3072249c4d2SAndreas Gohr    }
308083afc55SAndreas Gohr
309083afc55SAndreas Gohr    /**
310083afc55SAndreas Gohr     * Output the stored data
311083afc55SAndreas Gohr     *
312083afc55SAndreas Gohr     * @param string|int $value the value stored in the database
313797f0dfeSAndreas Gohr     * @param \Doku_Renderer $R the renderer currently used to render the data
314797f0dfeSAndreas Gohr     * @param string $mode The mode the output is rendered in (eg. XHTML)
315797f0dfeSAndreas Gohr     * @return bool true if $mode could be satisfied
316083afc55SAndreas Gohr     */
317d6d97f60SAnna Dabrowska    public function renderValue($value, \Doku_Renderer $R, $mode)
318d6d97f60SAnna Dabrowska    {
31934ffb347SAndreas Gohr        $value = $this->displayValue($value);
320797f0dfeSAndreas Gohr        $R->cdata($value);
321797f0dfeSAndreas Gohr        return true;
3222249c4d2SAndreas Gohr    }
3239d7a36f9SAndreas Gohr
3249d7a36f9SAndreas Gohr    /**
325dbaaf815SMichael Große     * format and return the data
326dbaaf815SMichael Große     *
327dbaaf815SMichael Große     * @param int[]|string[] $values the values stored in the database
328797f0dfeSAndreas Gohr     * @param \Doku_Renderer $R the renderer currently used to render the data
329797f0dfeSAndreas Gohr     * @param string $mode The mode the output is rendered in (eg. XHTML)
330797f0dfeSAndreas Gohr     * @return bool true if $mode could be satisfied
331dbaaf815SMichael Große     */
332d6d97f60SAnna Dabrowska    public function renderMultiValue($values, \Doku_Renderer $R, $mode)
333d6d97f60SAnna Dabrowska    {
334797f0dfeSAndreas Gohr        $len = count($values);
335797f0dfeSAndreas Gohr        for ($i = 0; $i < $len; $i++) {
336797f0dfeSAndreas Gohr            $this->renderValue($values[$i], $R, $mode);
337797f0dfeSAndreas Gohr            if ($i < $len - 1) {
338797f0dfeSAndreas Gohr                $R->cdata(', ');
339dbaaf815SMichael Große            }
340797f0dfeSAndreas Gohr        }
341797f0dfeSAndreas Gohr        return true;
342dbaaf815SMichael Große    }
343dbaaf815SMichael Große
344dbaaf815SMichael Große    /**
345262c0fc6SMichael Grosse     * Render a link in a struct cloud. This should be good for most types, but can be overwritten if necessary.
346262c0fc6SMichael Grosse     *
347262c0fc6SMichael Grosse     * @param string|int $value the value stored in the database
348262c0fc6SMichael Grosse     * @param \Doku_Renderer $R the renderer currently used to render the data
349262c0fc6SMichael Grosse     * @param string $mode The mode the output is rendered in (eg. XHTML)
350262c0fc6SMichael Grosse     * @param string $page the target to which should be linked
351262c0fc6SMichael Grosse     * @param string $filter the filter to apply to the aggregations on $page
35217a3a578SAndreas Gohr     * @param int $weight the scaled weight of the item. implemented as css font-size on the outside container
3537a256bc0SAnna Dabrowska     * @param int|null $showCount count for the tag, only passed if summarize was set in config
354262c0fc6SMichael Grosse     */
3557a256bc0SAnna Dabrowska    public function renderTagCloudLink($value, \Doku_Renderer $R, $mode, $page, $filter, $weight, $showCount = null)
356d6d97f60SAnna Dabrowska    {
357bfb78ce3SAnna Dabrowska        $value = $this->displayValue($value);
358bfb78ce3SAnna Dabrowska        if ($showCount) {
359bfb78ce3SAnna Dabrowska             $value .= " ($showCount)";
360bfb78ce3SAnna Dabrowska        }
361bfb78ce3SAnna Dabrowska        $R->internallink("$page?$filter", $value);
362262c0fc6SMichael Grosse    }
363262c0fc6SMichael Grosse
364262c0fc6SMichael Grosse    /**
365384e59cbSAndreas Gohr     * This function is used to modify an aggregation query to add a filter
366384e59cbSAndreas Gohr     * for the given column matching the given value. A type should add at
367384e59cbSAndreas Gohr     * least a filter here but could do additional things like joining more
368384e59cbSAndreas Gohr     * tables needed to handle more complex filters
369384e59cbSAndreas Gohr     *
37053528ecfSAndreas Gohr     * Important: $value might be an array. If so, the filter should check against
37153528ecfSAndreas Gohr     * all provided values ORed together
37253528ecfSAndreas Gohr     *
373af993d55SMichael Grosse     * @param QueryBuilderWhere $add The where clause where statements can be added
3744e4e3ad2SAndreas Gohr     * @param string $tablealias The table the currently saved value(s) are stored in
3754e4e3ad2SAndreas Gohr     * @param string $colname The column name on above table to use in the SQL
3767da1a0fdSAndreas Gohr     * @param string $comp The SQL comparator (LIKE, NOT LIKE, =, !=, etc)
37753528ecfSAndreas Gohr     * @param string|string[] $value this is the user supplied value to compare against. might be multiple
3784e4e3ad2SAndreas Gohr     * @param string $op the logical operator this filter should use (AND|OR)
379384e59cbSAndreas Gohr     */
380d6d97f60SAnna Dabrowska    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
381d6d97f60SAnna Dabrowska    {
38253528ecfSAndreas Gohr        /** @var QueryBuilderWhere $add Where additionional queries are added to */
38353528ecfSAndreas Gohr        if (is_array($value)) {
384af993d55SMichael Grosse            $add = $add->where($op); // sub where group
38553528ecfSAndreas Gohr            $op = 'OR';
38653528ecfSAndreas Gohr        }
38753528ecfSAndreas Gohr        foreach ((array)$value as $item) {
388af993d55SMichael Grosse            $pl = $add->getQB()->addValue($item);
38953528ecfSAndreas Gohr            $add->where($op, "$tablealias.$colname $comp $pl");
39053528ecfSAndreas Gohr        }
391384e59cbSAndreas Gohr    }
392384e59cbSAndreas Gohr
393384e59cbSAndreas Gohr    /**
394578407b2SAndreas Gohr     * Add the proper selection for this type to the current Query
395578407b2SAndreas Gohr     *
396578407b2SAndreas Gohr     * The default implementation here should be good for nearly all types, it simply
397578407b2SAndreas Gohr     * passes the given parameters to the query builder. But type may do more fancy
398578407b2SAndreas Gohr     * stuff here, eg. join more tables or select multiple values and combine them to
399e7ee2b64SAndreas Gohr     * JSON. If you do, be sure implement a fitting rawValue() method.
400578407b2SAndreas Gohr     *
401578407b2SAndreas Gohr     * The passed $tablealias.$columnname might be a data_* table (referencing a single
402578407b2SAndreas Gohr     * row) or a multi_* table (referencing multiple rows). In the latter case the
403578407b2SAndreas Gohr     * multi table has already been joined with the proper conditions.
404578407b2SAndreas Gohr     *
405578407b2SAndreas Gohr     * You may assume a column alias named 'PID' to be available, should you need the
406578407b2SAndreas Gohr     * current page context for a join or sub select.
407578407b2SAndreas Gohr     *
408578407b2SAndreas Gohr     * @param QueryBuilder $QB
409578407b2SAndreas Gohr     * @param string $tablealias The table the currently saved value(s) are stored in
4104e4e3ad2SAndreas Gohr     * @param string $colname The column name on above table
411578407b2SAndreas Gohr     * @param string $alias The added selection *has* to use this column alias
412578407b2SAndreas Gohr     */
413d6d97f60SAnna Dabrowska    public function select(QueryBuilder $QB, $tablealias, $colname, $alias)
414d6d97f60SAnna Dabrowska    {
4154e4e3ad2SAndreas Gohr        $QB->addSelectColumn($tablealias, $colname, $alias);
416578407b2SAndreas Gohr    }
417578407b2SAndreas Gohr
418578407b2SAndreas Gohr    /**
4197e06fb39SAndreas Gohr     * Sort results by this type
4207e06fb39SAndreas Gohr     *
4217e06fb39SAndreas Gohr     * The default implementation should be good for nearly all types. However some
4227e06fb39SAndreas Gohr     * types may need to do proper SQLite type casting to have the right order.
4237e06fb39SAndreas Gohr     *
4240549dcc5SAndreas Gohr     * Generally if you implemented @param QueryBuilder $QB
4257e06fb39SAndreas Gohr     * @param string $tablealias The table the currently saved value is stored in
4267e06fb39SAndreas Gohr     * @param string $colname The column name on above table (always single column!)
4277e06fb39SAndreas Gohr     * @param string $order either ASC or DESC
4280549dcc5SAndreas Gohr     * @see select() you probably want to implement this,
4290549dcc5SAndreas Gohr     * too.
4300549dcc5SAndreas Gohr     *
4317e06fb39SAndreas Gohr     */
432d6d97f60SAnna Dabrowska    public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
433d6d97f60SAnna Dabrowska    {
434f0be5277SAnna Dabrowska        $QB->addOrderBy("$tablealias.$colname COLLATE NOCASE $order");
4357e06fb39SAndreas Gohr    }
4367e06fb39SAndreas Gohr
4377e06fb39SAndreas Gohr    /**
438d560ea13SMichael Grosse     * Get the string by which to sort values of this type
439d560ea13SMichael Grosse     *
440d560ea13SMichael Grosse     * This implementation is designed to work both as registered function in sqlite
441d560ea13SMichael Grosse     * and to provide a string to be used in sorting values of this type in PHP.
442d560ea13SMichael Grosse     *
443db9b8745SAndreas Gohr     * @param string|Value $value The string by which the types would usually be sorted
4449e7e1786SMichael Grosse     *
4459e7e1786SMichael Grosse     * @return string
446d560ea13SMichael Grosse     */
447d6d97f60SAnna Dabrowska    public function getSortString($value)
448d6d97f60SAnna Dabrowska    {
449d560ea13SMichael Grosse        if (is_string($value)) {
450d560ea13SMichael Grosse            return $value;
451d560ea13SMichael Grosse        }
452d560ea13SMichael Grosse        $display = $value->getDisplayValue();
453d560ea13SMichael Grosse        if (is_array($display)) {
454d560ea13SMichael Grosse            return blank($display[0]) ? "" : $display[0];
455d560ea13SMichael Grosse        }
456d560ea13SMichael Grosse        return $display;
457d560ea13SMichael Grosse    }
458d560ea13SMichael Grosse
459d560ea13SMichael Grosse    /**
460e7ee2b64SAndreas Gohr     * This allows types to apply a transformation to the value read by select()
461e7ee2b64SAndreas Gohr     *
462e7ee2b64SAndreas Gohr     * The returned value should always be a single, non-complex string. In general
463e7ee2b64SAndreas Gohr     * it is the identifier a type stores in the database.
464e7ee2b64SAndreas Gohr     *
465e7ee2b64SAndreas Gohr     * This value will be used wherever the raw saved data is needed for comparisons.
466e7ee2b64SAndreas Gohr     * The default implementations of renderValue() and valueEditor() will call this
467e7ee2b64SAndreas Gohr     * function as well.
468e7ee2b64SAndreas Gohr     *
469e7ee2b64SAndreas Gohr     * @param string $value The value as returned by select()
470e7ee2b64SAndreas Gohr     * @return string The value as saved in the database
471e7ee2b64SAndreas Gohr     */
472d6d97f60SAnna Dabrowska    public function rawValue($value)
473d6d97f60SAnna Dabrowska    {
474e7ee2b64SAndreas Gohr        return $value;
475e7ee2b64SAndreas Gohr    }
476e7ee2b64SAndreas Gohr
477e7ee2b64SAndreas Gohr    /**
4785241ca30SAndreas Gohr     * This is called when a single string is needed to represent this Type's current
4795241ca30SAndreas Gohr     * value as a single (non-HTML) string. Eg. in a dropdown or in autocompletion.
4805241ca30SAndreas Gohr     *
4815241ca30SAndreas Gohr     * @param string $value
4825241ca30SAndreas Gohr     * @return string
4835241ca30SAndreas Gohr     */
484d6d97f60SAnna Dabrowska    public function displayValue($value)
485d6d97f60SAnna Dabrowska    {
4865241ca30SAndreas Gohr        return $this->rawValue($value);
4875241ca30SAndreas Gohr    }
4885241ca30SAndreas Gohr
4895241ca30SAndreas Gohr    /**
4907717c082SMichael Große     * This is the value to be used as argument to a filter for another column.
4917717c082SMichael Große     *
4920549dcc5SAndreas Gohr     * In a sense this is the counterpart to the @param string $value
4937717c082SMichael Große     *
4947717c082SMichael Große     * @return string
4950549dcc5SAndreas Gohr     * @see filter() function
4960549dcc5SAndreas Gohr     *
4977717c082SMichael Große     */
498d6d97f60SAnna Dabrowska    public function compareValue($value)
499d6d97f60SAnna Dabrowska    {
5007717c082SMichael Große        return $this->rawValue($value);
5017717c082SMichael Große    }
5027717c082SMichael Große
5037717c082SMichael Große    /**
504806eec82SAndreas Gohr     * Validate and optionally clean a single value
50517560ecbSAndreas Gohr     *
50617560ecbSAndreas Gohr     * This function needs to throw a validation exception when validation fails.
50717560ecbSAndreas Gohr     * The exception message will be prefixed by the appropriate field on output
50817560ecbSAndreas Gohr     *
509806eec82SAndreas Gohr     * The function should return the value as it should be saved later on.
510806eec82SAndreas Gohr     *
51123169abeSAndreas Gohr     * @param string|int $rawvalue
512806eec82SAndreas Gohr     * @return int|string the cleaned value
51317560ecbSAndreas Gohr     * @throws ValidationException
51417560ecbSAndreas Gohr     */
515d6d97f60SAnna Dabrowska    public function validate($rawvalue)
516d6d97f60SAnna Dabrowska    {
51723169abeSAndreas Gohr        return trim($rawvalue);
51817560ecbSAndreas Gohr    }
519914921fbSAndreas Gohr
520914921fbSAndreas Gohr    /**
521914921fbSAndreas Gohr     * Overwrite to handle Ajax requests
522914921fbSAndreas Gohr     *
523914921fbSAndreas Gohr     * A call to DOKU_BASE/lib/exe/ajax.php?call=plugin_struct&column=schema.name will
524914921fbSAndreas Gohr     * be redirected to this function on a fully initialized type. The result is
525914921fbSAndreas Gohr     * JSON encoded and returned to the caller. Access additional parameter via $INPUT
526914921fbSAndreas Gohr     * as usual
527914921fbSAndreas Gohr     *
528914921fbSAndreas Gohr     * @return mixed
5290549dcc5SAndreas Gohr     * @throws StructException when something goes wrong
530914921fbSAndreas Gohr     */
531d6d97f60SAnna Dabrowska    public function handleAjax()
532d6d97f60SAnna Dabrowska    {
533914921fbSAndreas Gohr        throw new StructException('not implemented');
534914921fbSAndreas Gohr    }
53553c92fe4SAndreas Gohr
53653c92fe4SAndreas Gohr    /**
53753c92fe4SAndreas Gohr     * Convenience method to access plugin language strings
53853c92fe4SAndreas Gohr     *
53953c92fe4SAndreas Gohr     * @param string $string
54053c92fe4SAndreas Gohr     * @return string
54153c92fe4SAndreas Gohr     */
542d6d97f60SAnna Dabrowska    public function getLang($string)
543d6d97f60SAnna Dabrowska    {
544ba766201SAndreas Gohr        if (is_null($this->hlp)) $this->hlp = plugin_load('helper', 'struct');
54553c92fe4SAndreas Gohr        return $this->hlp->getLang($string);
54653c92fe4SAndreas Gohr    }
547db9b8745SAndreas Gohr
548db9b8745SAndreas Gohr    /**
549db9b8745SAndreas Gohr     * With what comparator should dynamic filters filter this type?
550db9b8745SAndreas Gohr     *
551db9b8745SAndreas Gohr     * This default does a LIKE operation
552db9b8745SAndreas Gohr     *
553db9b8745SAndreas Gohr     * @return string
5540549dcc5SAndreas Gohr     * @see Search::$COMPARATORS
555db9b8745SAndreas Gohr     */
556d6d97f60SAnna Dabrowska    public function getDefaultComparator()
557d6d97f60SAnna Dabrowska    {
558db9b8745SAndreas Gohr        return '*~';
559db9b8745SAndreas Gohr    }
560083afc55SAndreas Gohr}
561