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