xref: /dokuwiki/lib/exe/ajax.php (revision 900a9e9e56d360fa7d347304e9b0d4b691d4becc)
1<?php
2/**
3 * DokuWiki AJAX call handler
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 */
8
9if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../');
10require_once(DOKU_INC.'inc/init.php');
11//close session
12session_write_close();
13
14header('Content-Type: text/html; charset=utf-8');
15
16//call the requested function
17if($INPUT->post->has('call')){
18    $call = $INPUT->post->str('call');
19}else if($INPUT->get->has('call')){
20    $call = $INPUT->get->str('call');
21}else{
22    exit;
23}
24$callfn = 'ajax_'.$call;
25
26if(function_exists($callfn)){
27    $callfn();
28}else{
29    $evt = new Doku_Event('AJAX_CALL_UNKNOWN', $call);
30    if ($evt->advise_before()) {
31        print "AJAX call '".htmlspecialchars($call)."' unknown!\n";
32        exit;
33    }
34    $evt->advise_after();
35    unset($evt);
36}
37
38/**
39 * Searches for matching pagenames
40 *
41 * @author Andreas Gohr <andi@splitbrain.org>
42 */
43function ajax_qsearch(){
44    global $conf;
45    global $lang;
46    global $INPUT;
47
48    $maxnumbersuggestions = 50;
49
50    $query = $INPUT->post->str('q');
51    if(empty($query)) $query = $INPUT->get->str('q');
52    if(empty($query)) return;
53
54    $query = urldecode($query);
55
56    $data = ft_pageLookup($query, true, useHeading('navigation'));
57
58    if(!count($data)) return;
59
60    print '<strong>'.$lang['quickhits'].'</strong>';
61    print '<ul>';
62    $counter = 0;
63    foreach($data as $id => $title){
64        if (useHeading('navigation')) {
65            $name = $title;
66        } else {
67            $ns = getNS($id);
68            if($ns){
69                $name = noNS($id).' ('.$ns.')';
70            }else{
71                $name = $id;
72            }
73        }
74        echo '<li>' . html_wikilink(':'.$id,$name) . '</li>';
75
76        $counter ++;
77        if($counter > $maxnumbersuggestions) {
78            echo '<li>...</li>';
79            break;
80        }
81    }
82    print '</ul>';
83}
84
85/**
86 * Support OpenSearch suggestions
87 *
88 * @link   http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.0
89 * @author Mike Frysinger <vapier@gentoo.org>
90 */
91function ajax_suggestions() {
92    global $conf;
93    global $lang;
94    global $INPUT;
95
96    $query = cleanID($INPUT->post->str('q'));
97    if(empty($query)) $query = cleanID($INPUT->get->str('q'));
98    if(empty($query)) return;
99
100    $data = array();
101    $data = ft_pageLookup($query);
102    if(!count($data)) return;
103    $data = array_keys($data);
104
105    // limit results to 15 hits
106    $data = array_slice($data, 0, 15);
107    $data = array_map('trim',$data);
108    $data = array_map('noNS',$data);
109    $data = array_unique($data);
110    sort($data);
111
112    /* now construct a json */
113    $suggestions = array(
114                        $query,  // the original query
115                        $data,   // some suggestions
116                        array(), // no description
117                        array()  // no urls
118                   );
119    $json = new JSON();
120
121    header('Content-Type: application/x-suggestions+json');
122    print $json->encode($suggestions);
123}
124
125/**
126 * Refresh a page lock and save draft
127 *
128 * Andreas Gohr <andi@splitbrain.org>
129 */
130function ajax_lock(){
131    global $conf;
132    global $lang;
133    global $ID;
134    global $INFO;
135    global $INPUT;
136
137    $ID = cleanID($INPUT->post->str('id'));
138    if(empty($ID)) return;
139
140    $INFO = pageinfo();
141
142    if (!$INFO['writable']) {
143        echo 'Permission denied';
144        return;
145    }
146
147    if(!checklock($ID)){
148        lock($ID);
149        echo 1;
150    }
151
152    if($conf['usedraft'] && $INPUT->post->str('wikitext')){
153        $client = $_SERVER['REMOTE_USER'];
154        if(!$client) $client = clientIP(true);
155
156        $draft = array('id'     => $ID,
157                'prefix' => substr($INPUT->post->str('prefix'), 0, -1),
158                'text'   => $INPUT->post->str('wikitext'),
159                'suffix' => $INPUT->post->str('suffix'),
160                'date'   => $INPUT->post->int('date'),
161                'client' => $client,
162                );
163        $cname = getCacheName($draft['client'].$ID,'.draft');
164        if(io_saveFile($cname,serialize($draft))){
165            echo $lang['draftdate'].' '.dformat();
166        }
167    }
168
169}
170
171/**
172 * Delete a draft
173 *
174 * @author Andreas Gohr <andi@splitbrain.org>
175 */
176function ajax_draftdel(){
177    global $INPUT;
178    $id = cleanID($INPUT->str('id'));
179    if(empty($id)) return;
180
181    $client = $_SERVER['REMOTE_USER'];
182    if(!$client) $client = clientIP(true);
183
184    $cname = getCacheName($client.$id,'.draft');
185    @unlink($cname);
186}
187
188/**
189 * Return subnamespaces for the Mediamanager
190 *
191 * @author Andreas Gohr <andi@splitbrain.org>
192 */
193function ajax_medians(){
194    global $conf;
195    global $INPUT;
196
197    // wanted namespace
198    $ns  = cleanID($INPUT->post->str('ns'));
199    $dir  = utf8_encodeFN(str_replace(':','/',$ns));
200
201    $lvl = count(explode(':',$ns));
202
203    $data = array();
204    search($data,$conf['mediadir'],'search_index',array('nofiles' => true),$dir);
205    foreach(array_keys($data) as $item){
206        $data[$item]['level'] = $lvl+1;
207    }
208    echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
209}
210
211/**
212 * Return list of files for the Mediamanager
213 *
214 * @author Andreas Gohr <andi@splitbrain.org>
215 */
216function ajax_medialist(){
217    global $conf;
218    global $NS;
219    global $INPUT;
220
221    $NS = cleanID($INPUT->post->str('ns'));
222    $sort = $INPUT->post->bool('recent') ? 'date' : 'natural';
223    if ($INPUT->post->str('do') == 'media') {
224        tpl_mediaFileList();
225    } else {
226        tpl_mediaContent(true, $sort);
227    }
228}
229
230/**
231 * Return the content of the right column
232 * (image details) for the Mediamanager
233 *
234 * @author Kate Arzamastseva <pshns@ukr.net>
235 */
236function ajax_mediadetails(){
237    global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf, $INPUT;
238    $fullscreen = true;
239    require_once(DOKU_INC.'lib/exe/mediamanager.php');
240
241    if ($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
242    if (isset($IMG)) $image = $IMG;
243    if (isset($JUMPTO)) $image = $JUMPTO;
244    if (isset($REV) && !$JUMPTO) $rev = $REV;
245
246    html_msgarea();
247    tpl_mediaFileDetails($image, $rev);
248}
249
250/**
251 * Returns image diff representation for mediamanager
252 * @author Kate Arzamastseva <pshns@ukr.net>
253 */
254function ajax_mediadiff(){
255    global $NS;
256    global $INPUT;
257
258    if ($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
259    $NS = $INPUT->post->str('ns');
260    $auth = auth_quickaclcheck("$NS:*");
261    media_diff($image, $NS, $auth, true);
262}
263
264function ajax_mediaupload(){
265    global $NS, $MSG, $INPUT;
266
267    if ($_FILES['qqfile']['tmp_name']) {
268        $id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
269    } elseif ($INPUT->get->has('qqfile')) {
270        $id = $INPUT->get->str('qqfile');
271    }
272
273    $id = cleanID($id);
274
275    $NS = $INPUT->str('ns');
276    $ns = $NS.':'.getNS($id);
277
278    $AUTH = auth_quickaclcheck("$ns:*");
279    if($AUTH >= AUTH_UPLOAD) { io_createNamespace("$ns:xxx", 'media'); }
280
281    if ($_FILES['qqfile']['error']) unset($_FILES['qqfile']);
282
283    if ($_FILES['qqfile']['tmp_name']) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
284    if ($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
285
286    if ($res) $result = array('success' => true,
287        'link' => media_managerURL(array('ns' => $ns, 'image' => $NS.':'.$id), '&'),
288        'id' => $NS.':'.$id, 'ns' => $NS);
289
290    if (!$result) {
291        $error = '';
292        if (isset($MSG)) {
293            foreach($MSG as $msg) $error .= $msg['msg'];
294        }
295        $result = array('error' => $msg['msg'], 'ns' => $NS);
296    }
297    $json = new JSON;
298    echo htmlspecialchars($json->encode($result), ENT_NOQUOTES);
299}
300
301/**
302 * Return sub index for index view
303 *
304 * @author Andreas Gohr <andi@splitbrain.org>
305 */
306function ajax_index(){
307    global $conf;
308    global $INPUT;
309
310    // wanted namespace
311    $ns  = cleanID($INPUT->post->str('idx'));
312    $dir  = utf8_encodeFN(str_replace(':','/',$ns));
313
314    $lvl = count(explode(':',$ns));
315
316    $data = array();
317    search($data,$conf['datadir'],'search_index',array('ns' => $ns),$dir);
318    foreach(array_keys($data) as $item){
319        $data[$item]['level'] = $lvl+1;
320    }
321    echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index');
322}
323
324/**
325 * List matching namespaces and pages for the link wizard
326 *
327 * @author Andreas Gohr <gohr@cosmocode.de>
328 */
329function ajax_linkwiz(){
330    global $conf;
331    global $lang;
332    global $INPUT;
333
334    $q  = ltrim(trim($INPUT->post->str('q')),':');
335    $id = noNS($q);
336    $ns = getNS($q);
337
338    $ns = cleanID($ns);
339    $id = cleanID($id);
340
341    $nsd  = utf8_encodeFN(str_replace(':','/',$ns));
342    $idd  = utf8_encodeFN(str_replace(':','/',$id));
343
344    $data = array();
345    if($q && !$ns){
346
347        // use index to lookup matching pages
348        $pages = array();
349        $pages = ft_pageLookup($id,true);
350
351        // result contains matches in pages and namespaces
352        // we now extract the matching namespaces to show
353        // them seperately
354        $dirs  = array();
355
356        foreach($pages as $pid => $title){
357            if(strpos(noNS($pid),$id) === false){
358                // match was in the namespace
359                $dirs[getNS($pid)] = 1; // assoc array avoids dupes
360            }else{
361                // it is a matching page, add it to the result
362                $data[] = array(
363                        'id'    => $pid,
364                        'title' => $title,
365                        'type'  => 'f',
366                        );
367            }
368            unset($pages[$pid]);
369        }
370        foreach($dirs as $dir => $junk){
371            $data[] = array(
372                    'id'   => $dir,
373                    'type' => 'd',
374                    );
375        }
376
377    }else{
378
379        $opts = array(
380                'depth' => 1,
381                'listfiles' => true,
382                'listdirs'  => true,
383                'pagesonly' => true,
384                'firsthead' => true,
385                'sneakyacl' => $conf['sneaky_index'],
386                );
387        if($id) $opts['filematch'] = '^.*\/'.$id;
388        if($id) $opts['dirmatch']  = '^.*\/'.$id;
389        search($data,$conf['datadir'],'search_universal',$opts,$nsd);
390
391        // add back to upper
392        if($ns){
393            array_unshift($data,array(
394                        'id'   => getNS($ns),
395                        'type' => 'u',
396                        ));
397        }
398    }
399
400    // fixme sort results in a useful way ?
401
402    if(!count($data)){
403        echo $lang['nothingfound'];
404        exit;
405    }
406
407    // output the found data
408    $even = 1;
409    foreach($data as $item){
410        $even *= -1; //zebra
411
412        if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id']) $item['id'] .= ':';
413        $link = wl($item['id']);
414
415        echo '<div class="'.(($even > 0)?'even':'odd').' type_'.$item['type'].'">';
416
417        if($item['type'] == 'u'){
418            $name = $lang['upperns'];
419        }else{
420            $name = htmlspecialchars($item['id']);
421        }
422
423        echo '<a href="'.$link.'" title="'.htmlspecialchars($item['id']).'" class="wikilink1">'.$name.'</a>';
424
425        if($item['title']){
426            echo '<span>'.htmlspecialchars($item['title']).'</span>';
427        }
428        echo '</div>';
429    }
430
431}
432
433//Setup VIM: ex: et ts=2 :
434