Merge pull request #3969 from dokuwiki-translate/lang_update_635_1684167422
[dokuwiki.git] / inc / Ajax.php
blobfd6abb5e47ce8a93ea365c5edbaecd8a49bd0387
1 <?php
3 namespace dokuwiki;
5 use dokuwiki\Ui;
6 use dokuwiki\Utf8\Sort;
8 /**
9 * Manage all builtin AJAX calls
11 * @todo The calls should be refactored out to their own proper classes
12 * @package dokuwiki
14 class Ajax {
16 /**
17 * Execute the given call
19 * @param string $call name of the ajax call
21 public function __construct($call) {
22 $callfn = 'call' . ucfirst($call);
23 if(method_exists($this, $callfn)) {
24 $this->$callfn();
25 } else {
26 $evt = new Extension\Event('AJAX_CALL_UNKNOWN', $call);
27 if($evt->advise_before()) {
28 print "AJAX call '" . hsc($call) . "' unknown!\n";
29 } else {
30 $evt->advise_after();
31 unset($evt);
36 /**
37 * Searches for matching pagenames
39 * @author Andreas Gohr <andi@splitbrain.org>
41 protected function callQsearch() {
42 global $lang;
43 global $INPUT;
45 $maxnumbersuggestions = 50;
47 $query = $INPUT->post->str('q');
48 if(empty($query)) $query = $INPUT->get->str('q');
49 if(empty($query)) return;
51 $query = urldecode($query);
53 $data = ft_pageLookup($query, true, useHeading('navigation'));
55 if(!count($data)) return;
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;
71 echo '<li>' . html_wikilink(':' . $id, $name) . '</li>';
73 $counter++;
74 if($counter > $maxnumbersuggestions) {
75 echo '<li>...</li>';
76 break;
79 print '</ul>';
82 /**
83 * Support OpenSearch suggestions
85 * @link http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.0
86 * @author Mike Frysinger <vapier@gentoo.org>
88 protected function callSuggestions() {
89 global $INPUT;
91 $query = cleanID($INPUT->post->str('q'));
92 if(empty($query)) $query = cleanID($INPUT->get->str('q'));
93 if(empty($query)) return;
95 $data = ft_pageLookup($query);
96 if(!count($data)) return;
97 $data = array_keys($data);
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::sort($data);
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
114 header('Content-Type: application/x-suggestions+json');
115 print json_encode($suggestions);
119 * Refresh a page lock and save draft
121 * Andreas Gohr <andi@splitbrain.org>
123 protected function callLock() {
124 global $ID;
125 global $INFO;
126 global $INPUT;
128 $ID = cleanID($INPUT->post->str('id'));
129 if(empty($ID)) return;
131 $INFO = pageinfo();
133 $response = [
134 'errors' => [],
135 'lock' => '0',
136 'draft' => '',
138 if(!$INFO['writable']) {
139 $response['errors'][] = 'Permission to write this page has been denied.';
140 echo json_encode($response);
141 return;
144 if(!checklock($ID)) {
145 lock($ID);
146 $response['lock'] = '1';
149 $draft = new Draft($ID, $INFO['client']);
150 if ($draft->saveDraft()) {
151 $response['draft'] = $draft->getDraftMessage();
152 } else {
153 $response['errors'] = array_merge($response['errors'], $draft->getErrors());
155 echo json_encode($response);
159 * Delete a draft
161 * @author Andreas Gohr <andi@splitbrain.org>
163 protected function callDraftdel() {
164 global $INPUT;
165 $id = cleanID($INPUT->str('id'));
166 if(empty($id)) return;
168 $client = $INPUT->server->str('REMOTE_USER');
169 if(!$client) $client = clientIP(true);
171 $draft = new Draft($id, $client);
172 if ($draft->isDraftAvailable() && checkSecurityToken()) {
173 $draft->deleteDraft();
178 * Return subnamespaces for the Mediamanager
180 * @author Andreas Gohr <andi@splitbrain.org>
182 protected function callMedians() {
183 global $conf;
184 global $INPUT;
186 // wanted namespace
187 $ns = cleanID($INPUT->post->str('ns'));
188 $dir = utf8_encodeFN(str_replace(':', '/', $ns));
190 $lvl = count(explode(':', $ns));
192 $data = array();
193 search($data, $conf['mediadir'], 'search_index', array('nofiles' => true), $dir);
194 foreach(array_keys($data) as $item) {
195 $data[$item]['level'] = $lvl + 1;
197 echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
201 * Return list of files for the Mediamanager
203 * @author Andreas Gohr <andi@splitbrain.org>
205 protected function callMedialist() {
206 global $NS;
207 global $INPUT;
209 $NS = cleanID($INPUT->post->str('ns'));
210 $sort = $INPUT->post->bool('recent') ? 'date' : 'natural';
211 if($INPUT->post->str('do') == 'media') {
212 tpl_mediaFileList();
213 } else {
214 tpl_mediaContent(true, $sort);
219 * Return the content of the right column
220 * (image details) for the Mediamanager
222 * @author Kate Arzamastseva <pshns@ukr.net>
224 protected function callMediadetails() {
225 global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT;
226 $fullscreen = true;
227 require_once(DOKU_INC . 'lib/exe/mediamanager.php');
229 $image = '';
230 if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
231 if(isset($IMG)) $image = $IMG;
232 if(isset($JUMPTO)) $image = $JUMPTO;
233 $rev = false;
234 if(isset($REV) && !$JUMPTO) $rev = $REV;
236 html_msgarea();
237 tpl_mediaFileDetails($image, $rev);
241 * Returns image diff representation for mediamanager
243 * @author Kate Arzamastseva <pshns@ukr.net>
245 protected function callMediadiff() {
246 global $INPUT;
248 $image = '';
249 if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
250 (new Ui\MediaDiff($image))->preference('fromAjax', true)->show();
254 * Manages file uploads
256 * @author Kate Arzamastseva <pshns@ukr.net>
258 protected function callMediaupload() {
259 global $NS, $MSG, $INPUT;
261 $id = '';
262 if(isset($_FILES['qqfile']['tmp_name'])) {
263 $id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
264 } elseif($INPUT->get->has('qqfile')) {
265 $id = $INPUT->get->str('qqfile');
268 $id = cleanID($id);
270 $NS = $INPUT->str('ns');
271 $ns = $NS . ':' . getNS($id);
273 $AUTH = auth_quickaclcheck("$ns:*");
274 if($AUTH >= AUTH_UPLOAD) {
275 io_createNamespace("$ns:xxx", 'media');
278 if(isset($_FILES['qqfile']['error']) && $_FILES['qqfile']['error']) unset($_FILES['qqfile']);
280 $res = false;
281 if(isset($_FILES['qqfile']['tmp_name'])) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
282 if($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
284 if($res) {
285 $result = array(
286 'success' => true,
287 'link' => media_managerURL(array('ns' => $ns, 'image' => $NS . ':' . $id), '&'),
288 'id' => $NS . ':' . $id,
289 'ns' => $NS
291 } else {
292 $error = '';
293 if(isset($MSG)) {
294 foreach($MSG as $msg) {
295 $error .= $msg['msg'];
298 $result = array(
299 'error' => $error,
300 'ns' => $NS
304 header('Content-Type: application/json');
305 echo json_encode($result);
309 * Return sub index for index view
311 * @author Andreas Gohr <andi@splitbrain.org>
313 protected function callIndex() {
314 global $conf;
315 global $INPUT;
317 // wanted namespace
318 $ns = cleanID($INPUT->post->str('idx'));
319 $dir = utf8_encodeFN(str_replace(':', '/', $ns));
321 $lvl = count(explode(':', $ns));
323 $data = array();
324 search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir);
325 foreach (array_keys($data) as $item) {
326 $data[$item]['level'] = $lvl + 1;
328 $idx = new Ui\Index;
329 echo html_buildlist($data, 'idx', [$idx,'formatListItem'], [$idx,'tagListItem']);
333 * List matching namespaces and pages for the link wizard
335 * @author Andreas Gohr <gohr@cosmocode.de>
337 protected function callLinkwiz() {
338 global $conf;
339 global $lang;
340 global $INPUT;
342 $q = ltrim(trim($INPUT->post->str('q')), ':');
343 $id = noNS($q);
344 $ns = getNS($q);
346 $ns = cleanID($ns);
347 $id = cleanID($id);
349 $nsd = utf8_encodeFN(str_replace(':', '/', $ns));
351 $data = array();
352 if($q !== '' && $ns === '') {
354 // use index to lookup matching pages
355 $pages = ft_pageLookup($id, true);
357 // If 'useheading' option is 'always' or 'content',
358 // search page titles with original query as well.
359 if ($conf['useheading'] == '1' || $conf['useheading'] == 'content') {
360 $pages = array_merge($pages, ft_pageLookup($q, true, true));
361 asort($pages, SORT_STRING);
364 // result contains matches in pages and namespaces
365 // we now extract the matching namespaces to show
366 // them seperately
367 $dirs = array();
369 foreach($pages as $pid => $title) {
370 if(strpos(getNS($pid), $id) !== false) {
371 // match was in the namespace
372 $dirs[getNS($pid)] = 1; // assoc array avoids dupes
373 } else {
374 // it is a matching page, add it to the result
375 $data[] = array(
376 'id' => $pid,
377 'title' => $title,
378 'type' => 'f',
381 unset($pages[$pid]);
383 foreach($dirs as $dir => $junk) {
384 $data[] = array(
385 'id' => $dir,
386 'type' => 'd',
390 } else {
392 $opts = array(
393 'depth' => 1,
394 'listfiles' => true,
395 'listdirs' => true,
396 'pagesonly' => true,
397 'firsthead' => true,
398 'sneakyacl' => $conf['sneaky_index'],
400 if($id) $opts['filematch'] = '^.*\/' . $id;
401 if($id) $opts['dirmatch'] = '^.*\/' . $id;
402 search($data, $conf['datadir'], 'search_universal', $opts, $nsd);
404 // add back to upper
405 if($ns) {
406 array_unshift(
407 $data, array(
408 'id' => getNS($ns),
409 'type' => 'u',
415 // fixme sort results in a useful way ?
417 if(!count($data)) {
418 echo $lang['nothingfound'];
419 exit;
422 // output the found data
423 $even = 1;
424 foreach($data as $item) {
425 $even *= -1; //zebra
427 if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id'] !== '') $item['id'] .= ':';
428 $link = wl($item['id']);
430 echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">';
432 if($item['type'] == 'u') {
433 $name = $lang['upperns'];
434 } else {
435 $name = hsc($item['id']);
438 echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>';
440 if(!blank($item['title'])) {
441 echo '<span>' . hsc($item['title']) . '</span>';
443 echo '</div>';