1<?php
2
3namespace dokuwiki\plugin\struct\types;
4
5use dokuwiki\plugin\struct\meta\Column;
6use dokuwiki\plugin\struct\meta\QueryBuilder;
7use dokuwiki\plugin\struct\meta\QueryBuilderWhere;
8use dokuwiki\plugin\struct\meta\SearchConfigParameters;
9
10class Tag extends AbstractMultiBaseType
11{
12    protected $config = array(
13        'page' => '',
14        'autocomplete' => array(
15            'mininput' => 2,
16            'maxresult' => 5,
17        ),
18    );
19
20    /**
21     * @param int|string $value
22     * @param \Doku_Renderer $R
23     * @param string $mode
24     * @return bool
25     */
26    public function renderValue($value, \Doku_Renderer $R, $mode)
27    {
28        $context = $this->getContext();
29        $filter = SearchConfigParameters::$PARAM_FILTER .
30            '[' . $context->getTable() . '.' . $context->getLabel() . '*~]=' . $value;
31
32        $page = trim($this->config['page']);
33        if (!$page) $page = cleanID($context->getLabel());
34
35        $R->internallink($page . '?' . $filter, $value);
36        return true;
37    }
38
39    /**
40     * Autocomplete from existing tags
41     *
42     * @return array
43     */
44    public function handleAjax()
45    {
46        global $INPUT;
47
48        // check minimum length
49        $lookup = trim($INPUT->str('search'));
50        if (utf8_strlen($lookup) < $this->config['autocomplete']['mininput']) return array();
51
52        // results wanted?
53        $max = $this->config['autocomplete']['maxresult'];
54        if ($max <= 0) return array();
55
56        $context = $this->getContext();
57        $sql = $this->buildSQLFromContext($context);
58        $opt = array("%$lookup%");
59
60        /** @var \helper_plugin_struct_db $hlp */
61        $hlp = plugin_load('helper', 'struct_db');
62        $sqlite = $hlp->getDB();
63        $res = $sqlite->query($sql, $opt);
64        $rows = $sqlite->res2arr($res);
65        $sqlite->res_close($res);
66
67        $result = array();
68        foreach ($rows as $row) {
69            $result[] = array(
70                'label' => $row['value'],
71                'value' => $row['value'],
72            );
73        }
74
75        return $result;
76    }
77
78    /**
79     * Create the sql to query the database for tags to do autocompletion
80     *
81     * This method both handles multi columns and page schemas that need access checking
82     *
83     * @param Column $context
84     *
85     * @return string The sql with a single "?" placeholde for the search value
86     */
87    protected function buildSQLFromContext(Column $context)
88    {
89        $sql = '';
90        if ($context->isMulti()) {
91            /** @noinspection SqlResolve */
92            $sql .= "SELECT DISTINCT value
93                      FROM multi_{$context->getTable()} AS M, data_{$context->getTable()} AS D
94                     WHERE M.pid = D.pid
95                       AND M.rev = D.rev
96                       AND M.colref = {$context->getColref()}\n";
97        } else {
98            /** @noinspection SqlResolve */
99            $sql .= "SELECT DISTINCT col{$context->getColref()} AS value
100                      FROM data_{$context->getTable()} AS D
101                     WHERE 1 = 1\n";
102        }
103
104        $sql .= "AND ( D.pid = '' OR (";
105        $sql .= "PAGEEXISTS(D.pid) = 1\n";
106        $sql .= "AND GETACCESSLEVEL(D.pid) > 0\n";
107        $sql .= ")) ";
108
109        $sql .= "AND D.latest = 1\n";
110        $sql .= "AND value LIKE ?\n";
111        $sql .= 'ORDER BY value';
112
113        return $sql;
114    }
115
116    /**
117     * Normalize tags before comparing
118     *
119     * @param QueryBuilder $QB
120     * @param string $tablealias
121     * @param string $colname
122     * @param string $comp
123     * @param string|string[] $value
124     * @param string $op
125     */
126    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
127    {
128        /** @var QueryBuilderWhere $add Where additionional queries are added to */
129        if (is_array($value)) {
130            $add = $add->where($op); // sub where group
131            $op = 'OR';
132        }
133        foreach ((array)$value as $item) {
134            $pl = $add->getQB()->addValue($item);
135            $add->where($op, "LOWER(REPLACE($tablealias.$colname, ' ', '')) $comp LOWER(REPLACE($pl, ' ', ''))");
136        }
137    }
138}
139