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