xref: /plugin/struct/types/Page.php (revision 1e11f3d953de62eea93e7ca4b557516706add6a2)
1<?php
2
3namespace dokuwiki\plugin\struct\types;
4
5use dokuwiki\plugin\struct\meta\QueryBuilder;
6use dokuwiki\plugin\struct\meta\QueryBuilderWhere;
7
8/**
9 * Class Page
10 *
11 * Represents a single page in the wiki. Will be linked in output.
12 *
13 * @package dokuwiki\plugin\struct\types
14 */
15class Page extends AbstractMultiBaseType
16{
17
18    protected $config = array(
19        'usetitles' => false,
20        'autocomplete' => array(
21            'mininput' => 2,
22            'maxresult' => 5,
23            'namespace' => '',
24            'postfix' => '',
25        ),
26    );
27
28    /**
29     * Output the stored data
30     *
31     * @param string $value the value stored in the database - JSON when titles are used
32     * @param \Doku_Renderer $R the renderer currently used to render the data
33     * @param string $mode The mode the output is rendered in (eg. XHTML)
34     * @return bool true if $mode could be satisfied
35     */
36    public function renderValue($value, \Doku_Renderer $R, $mode)
37    {
38        if ($this->config['usetitles']) {
39            list($id, $title) = \helper_plugin_struct::decodeJson($value);
40        } else {
41            $id = $value;
42            $title = null;
43        }
44
45        if (!$id) return true;
46
47        $R->internallink(":$id", $title);
48        return true;
49    }
50
51    /**
52     * Cleans the link
53     *
54     * @param string $rawvalue
55     * @return string
56     */
57    public function validate($rawvalue)
58    {
59        list($page, $fragment) = explode('#', $rawvalue, 2);
60        return cleanID($page) . (strlen(cleanID($fragment)) > 0 ? '#' . cleanID($fragment) : '');
61    }
62
63    /**
64     * Autocompletion support for pages
65     *
66     * @return array
67     */
68    public function handleAjax()
69    {
70        global $INPUT;
71
72        // check minimum length
73        $lookup = trim($INPUT->str('search'));
74        if (utf8_strlen($lookup) < $this->config['autocomplete']['mininput']) return array();
75
76        // results wanted?
77        $max = $this->config['autocomplete']['maxresult'];
78        if ($max <= 0) return array();
79
80        // lookup with namespace and postfix applied
81        $namespace = $this->config['autocomplete']['namespace'];
82        if ($namespace) {
83            // namespace may be relative, resolve in current context
84            $namespace .= ':foo'; // resolve expects pageID
85            resolve_pageid($INPUT->str('ns'), $namespace, $exists);
86            $namespace = getNS($namespace);
87        }
88        $postfix = $this->config['autocomplete']['postfix'];
89        if ($namespace) $lookup .= ' @' . $namespace;
90
91        $data = ft_pageLookup($lookup, true, $this->config['usetitles']);
92        if (!count($data)) return array();
93
94        // this basically duplicates what we do in ajax_qsearch()
95        $result = array();
96        $counter = 0;
97        foreach ($data as $id => $title) {
98            if ($this->config['usetitles']) {
99                $name = $title . ' (' . $id . ')';
100            } else {
101                $ns = getNS($id);
102                if ($ns) {
103                    $name = noNS($id) . ' (' . $ns . ')';
104                } else {
105                    $name = $id;
106                }
107            }
108
109            // check suffix
110            if ($postfix && substr($id, -1 * strlen($postfix)) != $postfix) {
111                continue; // page does not end in postfix, don't suggest it
112            }
113
114            $result[] = array(
115                'label' => $name,
116                'value' => $id
117            );
118
119            $counter++;
120            if ($counter > $max) break;
121        }
122
123        return $result;
124    }
125
126    /**
127     * When using titles, we need ot join the titles table
128     *
129     * @param QueryBuilder $QB
130     * @param string $tablealias
131     * @param string $colname
132     * @param string $alias
133     */
134    public function select(QueryBuilder $QB, $tablealias, $colname, $alias)
135    {
136        if (!$this->config['usetitles']) {
137            parent::select($QB, $tablealias, $colname, $alias);
138            return;
139        }
140        $rightalias = $QB->generateTableAlias();
141        $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid");
142        $QB->addSelectStatement("STRUCT_JSON($tablealias.$colname, $rightalias.title)", $alias);
143    }
144
145    /**
146     * When using titles, sort by them first
147     *
148     * @param QueryBuilder $QB
149     * @param string $tablealias
150     * @param string $colname
151     * @param string $order
152     */
153    public function sort(QueryBuilder $QB, $tablealias, $colname, $order)
154    {
155        if (!$this->config['usetitles']) {
156            parent::sort($QB, $tablealias, $colname, $order);
157            return;
158        }
159
160        $rightalias = $QB->generateTableAlias();
161        $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid");
162        $QB->addOrderBy("$rightalias.title $order");
163        $QB->addOrderBy("$tablealias.$colname $order");
164    }
165
166    /**
167     * Return the pageid only
168     *
169     * @param string $value
170     * @return string
171     */
172    public function rawValue($value)
173    {
174        if ($this->config['usetitles']) {
175            list($value) = \helper_plugin_struct::decodeJson($value);
176        }
177        return $value;
178    }
179
180    /**
181     * Return the title only
182     *
183     * @param string $value
184     * @return string
185     */
186    public function displayValue($value)
187    {
188        if ($this->config['usetitles']) {
189            list($pageid, $value) = \helper_plugin_struct::decodeJson($value);
190            if (blank($value)) {
191                $value = $pageid;
192            }
193        }
194        return $value;
195    }
196
197    /**
198     * When using titles, we need to compare against the title table, too
199     *
200     * @param QueryBuilderWhere $add
201     * @param string $tablealias
202     * @param string $colname
203     * @param string $comp
204     * @param string $value
205     * @param string $op
206     */
207    public function filter(QueryBuilderWhere $add, $tablealias, $colname, $comp, $value, $op)
208    {
209        if (!$this->config['usetitles']) {
210            parent::filter($add, $tablealias, $colname, $comp, $value, $op);
211            return;
212        }
213
214        $QB = $add->getQB();
215        $rightalias = $QB->generateTableAlias();
216        $QB->addLeftJoin($tablealias, 'titles', $rightalias, "$tablealias.$colname = $rightalias.pid");
217
218        // compare against page and title
219        $sub = $add->where($op);
220        $pl = $QB->addValue($value);
221        $sub->whereOr("$tablealias.$colname $comp $pl");
222        $pl = $QB->addValue($value);
223        $sub->whereOr("$rightalias.title $comp $pl");
224    }
225}
226