1<?php
2/**
3 * DokuWiki Plugin linksuggest (Action Component)
4 *
5 * ajax autosuggest for links
6 *
7 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author lisps
9 */
10
11class action_plugin_linksuggest extends DokuWiki_Action_Plugin {
12
13    /**
14     * Register the eventhandlers
15     *
16     * @param Doku_Event_Handler $controller
17     */
18    function register(Doku_Event_Handler $controller) {
19        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'page_link');
20        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'media_link');
21    }
22
23    /**
24     * ajax Request Handler
25     * page_link
26     *
27     * @param $event
28     * @param $param
29     */
30    function page_link(&$event, $param) {
31        if ($event->data !== 'plugin_linksuggest') {
32            return;
33        }
34        //no other ajax call handlers needed
35        $event->stopPropagation();
36        $event->preventDefault();
37
38        global $INPUT;
39
40        $page_ns = trim($INPUT->post->str('ns')); //current namespace
41        $page_id = trim($INPUT->post->str('id')); //current id
42        $q = trim($INPUT->post->str('q')); //entered string
43
44        //keep hashlink if exists
45        $hash = null;
46        if (strpos($q, '#') !== false) {
47            list($q, $hash) = explode('#', $q, 2);
48        }
49        $has_hash = $hash === null ? false : true;
50        $ns_user = $ns = getNS($q); //namespace of entered string
51        $id = cleanID(noNS($q)); //page of entered string
52
53        if ($q && trim($q, '.') === '') { //only "." return
54            $data = [];
55        } else if ($ns === '') { // [[:xxx -> absolute link
56            $data = $this->search_pages($ns, $id, $has_hash);
57        } else if ($ns === false && $page_ns) { // [[xxx and not in root-namespace
58            $data = array_merge(
59                $this->search_pages($page_ns, $id, true),//search in current
60                $this->search_pages('', $id, $has_hash)            //and in root
61            );
62        } else if (strpos($ns, '.') !== false) { //relative link
63            resolve_pageid($page_ns, $ns, $exists); //resolve the ns based on current id
64            $data = $this->search_pages($ns, $id, $has_hash);
65        } else {
66            $data = $this->search_pages($ns, $id, $has_hash);
67        }
68
69
70        $data_r = [];
71        $link = '';
72
73        if ($hash !== null && $data[0]['type'] === 'f') {
74            //if hash is given and a page was found
75            $page = $data[0]['id'];
76            $meta = p_get_metadata($page, false, METADATA_RENDER_USING_CACHE);
77
78            if (isset($meta['internal']['toc'])) {
79                $toc = $meta['description']['tableofcontents'];
80                trigger_event('TPL_TOC_RENDER', $toc, null, false);
81                if (is_array($toc) && count($toc) !== 0) {
82                    foreach ($toc as &$t) { //loop through toc and compare
83                        if ($hash === '' || strpos($t['hid'], $hash) === 0) {
84                            $data_r[] = $t;
85                        }
86                    }
87                    $link = $q;
88                }
89            }
90        } else {
91
92            foreach ($data as $entry) {
93                $data_r[] = [
94                    'id' => noNS($entry['id']),
95                    'ns' => ($ns_user !== "") ? $ns_user : ':', //return what user has typed in
96                    'type' => $entry['type'], // d/f
97                    'title' => $entry['title'],
98                    'rootns' => $entry['ns'] ? 0 : 1,
99                ];
100            }
101        }
102
103        echo json_encode([
104            'data' => $data_r,
105            'link' => $link
106        ]);
107    }
108
109    /**
110     * ajax Request Handler
111     * media_link
112     *
113     * @param $event
114     * @param $param
115     */
116    function media_link(&$event, $param) {
117        if ($event->data !== 'plugin_imglinksuggest') {
118            return;
119        }
120        //no other ajax call handlers needed
121        $event->stopPropagation();
122        $event->preventDefault();
123
124        global $INPUT;
125
126        $page_ns = trim($INPUT->post->str('ns')); //current namespace
127        $q = trim($INPUT->post->str('q')); //entered string
128
129        $ns_user = $ns = getNS($q); //namespace of entered string
130        $id = cleanID(noNS($q)); //media of entered string
131
132        if ($q && trim($q, '.') === '') { //only "." return
133            $data = [];
134        } else if ($ns === '') { // [[:xxx -> absolute link
135            $data = $this->search_medias($ns, $id);
136        } else if ($ns === false && $page_ns) { // [[xxx and not in root-namespace
137            $data = array_merge(
138                $this->search_medias($page_ns, $id),//search in current
139                $this->search_medias('', $id)            //and in root
140            );
141        } else if (strpos($ns, '.') !== false) { //relative link
142            resolve_pageid($page_ns, $ns, $exists); //resolve the ns based on current id
143            $data = $this->search_medias($ns, $id);
144        } else {
145            $data = $this->search_medias($ns, $id);
146        }
147
148        $data_r = [];
149        $link = '';
150
151        foreach ($data as $entry) {
152            $data_r[] = [
153                'id' => noNS($entry['id']),
154                'ns' => ($ns_user !== "") ? $ns_user : ':', //return what user has typed in
155                'type' => $entry['type'], // d/f
156                'rootns' => $entry['ns'] ? 0 : 1,
157            ];
158        }
159
160        echo json_encode([
161            'data' => $data_r,
162            'link' => $link
163        ]);
164    }
165
166
167    /**
168     * List available pages, and eventually namespaces
169     *
170     * @param string $ns
171     * @param string $id
172     * @param bool $pagesonly
173     * @return array
174     */
175    protected function search_pages($ns, $id, $pagesonly = false) {
176        global $conf;
177
178        $data = [];
179        $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); //dir
180
181        $opts = [
182            'depth' => 1,
183            'listfiles' => true,
184            'listdirs' => !$pagesonly,
185            'pagesonly' => true,
186            'firsthead' => true,
187            'sneakyacl' => $conf['sneaky_index'],
188        ];
189        if ($id) $opts['filematch'] = '^.*\/' . $id;
190        if ($id && !$pagesonly) $opts['dirmatch'] = '^.*\/' . $id;
191        search($data, $conf['datadir'], 'search_universal', $opts, $nsd);
192
193        return $data;
194    }
195
196    /**
197     * List available media
198     *
199     * @param string $ns
200     * @param string $id
201     * @return array
202     */
203    protected function search_medias($ns, $id) {
204        global $conf;
205
206        $data = [];
207        $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); //dir
208
209        $opts = [
210            'depth' => 1,
211            'listfiles' => true,
212            'listdirs' => true,
213            'firsthead' => true,
214            'sneakyacl' => $conf['sneaky_index'],
215        ];
216        if ($id) $opts['filematch'] = '^.*\/' . $id;
217        if ($id) $opts['dirmatch'] = '^.*\/' . $id;
218        search($data, $conf['mediadir'], 'search_universal', $opts, $nsd);
219
220        return $data;
221    }
222
223}
224