xref: /dokuwiki/lib/exe/ajax.php (revision 94070874cc8d8702821161dbaa291e8b2d043ad3)
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
301function dir_delete($path) {
302    if (!is_string($path) || $path == "") return false;
303
304    if (is_dir($path) && !is_link($path)) {
305        if (!$dh = @opendir($path)) return false;
306
307        while ($f = readdir($dh)) {
308            if ($f == '..' || $f == '.') continue;
309            dir_delete("$path/$f");
310        }
311
312        closedir($dh);
313        return @rmdir($path);
314    } else {
315        return @unlink($path);
316    }
317
318    return false;
319}
320
321/**
322 * Return sub index for index view
323 *
324 * @author Andreas Gohr <andi@splitbrain.org>
325 */
326function ajax_index(){
327    global $conf;
328    global $INPUT;
329
330    // wanted namespace
331    $ns  = cleanID($INPUT->post->str('idx'));
332    $dir  = utf8_encodeFN(str_replace(':','/',$ns));
333
334    $lvl = count(explode(':',$ns));
335
336    $data = array();
337    search($data,$conf['datadir'],'search_index',array('ns' => $ns),$dir);
338    foreach(array_keys($data) as $item){
339        $data[$item]['level'] = $lvl+1;
340    }
341    echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index');
342}
343
344/**
345 * List matching namespaces and pages for the link wizard
346 *
347 * @author Andreas Gohr <gohr@cosmocode.de>
348 */
349function ajax_linkwiz(){
350    global $conf;
351    global $lang;
352    global $INPUT;
353
354    $q  = ltrim(trim($INPUT->post->str('q')),':');
355    $id = noNS($q);
356    $ns = getNS($q);
357
358    $ns = cleanID($ns);
359    $id = cleanID($id);
360
361    $nsd  = utf8_encodeFN(str_replace(':','/',$ns));
362    $idd  = utf8_encodeFN(str_replace(':','/',$id));
363
364    $data = array();
365    if($q && !$ns){
366
367        // use index to lookup matching pages
368        $pages = array();
369        $pages = ft_pageLookup($id,true);
370
371        // result contains matches in pages and namespaces
372        // we now extract the matching namespaces to show
373        // them seperately
374        $dirs  = array();
375
376        foreach($pages as $pid => $title){
377            if(strpos(noNS($pid),$id) === false){
378                // match was in the namespace
379                $dirs[getNS($pid)] = 1; // assoc array avoids dupes
380            }else{
381                // it is a matching page, add it to the result
382                $data[] = array(
383                        'id'    => $pid,
384                        'title' => $title,
385                        'type'  => 'f',
386                        );
387            }
388            unset($pages[$pid]);
389        }
390        foreach($dirs as $dir => $junk){
391            $data[] = array(
392                    'id'   => $dir,
393                    'type' => 'd',
394                    );
395        }
396
397    }else{
398
399        $opts = array(
400                'depth' => 1,
401                'listfiles' => true,
402                'listdirs'  => true,
403                'pagesonly' => true,
404                'firsthead' => true,
405                'sneakyacl' => $conf['sneaky_index'],
406                );
407        if($id) $opts['filematch'] = '^.*\/'.$id;
408        if($id) $opts['dirmatch']  = '^.*\/'.$id;
409        search($data,$conf['datadir'],'search_universal',$opts,$nsd);
410
411        // add back to upper
412        if($ns){
413            array_unshift($data,array(
414                        'id'   => getNS($ns),
415                        'type' => 'u',
416                        ));
417        }
418    }
419
420    // fixme sort results in a useful way ?
421
422    if(!count($data)){
423        echo $lang['nothingfound'];
424        exit;
425    }
426
427    // output the found data
428    $even = 1;
429    foreach($data as $item){
430        $even *= -1; //zebra
431
432        if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id']) $item['id'] .= ':';
433        $link = wl($item['id']);
434
435        echo '<div class="'.(($even > 0)?'even':'odd').' type_'.$item['type'].'">';
436
437        if($item['type'] == 'u'){
438            $name = $lang['upperns'];
439        }else{
440            $name = htmlspecialchars($item['id']);
441        }
442
443        echo '<a href="'.$link.'" title="'.htmlspecialchars($item['id']).'" class="wikilink1">'.$name.'</a>';
444
445        if($item['title']){
446            echo '<span>'.htmlspecialchars($item['title']).'</span>';
447        }
448        echo '</div>';
449    }
450
451}
452
453//Setup VIM: ex: et ts=2 :
454