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