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 55 $MetadataSearch = MetadataSearch::getInstance(); 56 $data = $MetadataSearch->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 */ 91 protected function callSuggestions() 92 { 93 global $INPUT; 94 95 $query = cleanID($INPUT->post->str('q')); 96 if (empty($query)) $query = cleanID($INPUT->get->str('q')); 97 if (empty($query)) return; 98 99 $MetadataSearch = MetadataSearch::getInstance(); 100 $data = $MetadataSearch->pageLookup($query); 101 if (!count($data)) return; 102 $data = array_keys($data); 103 104 // limit results to 15 hits 105 $data = array_slice($data, 0, 15); 106 $data = array_map('trim', $data); 107 $data = array_map('noNS', $data); 108 $data = array_unique($data); 109 Sort::sort($data); 110 111 /* now construct a json */ 112 $suggestions = array( 113 $query, // the original query 114 $data, // some suggestions 115 array(), // no description 116 array() // no urls 117 ); 118 119 header('Content-Type: application/x-suggestions+json'); 120 print json_encode($suggestions); 121 } 122 123 /** 124 * Refresh a page lock and save draft 125 * 126 * Andreas Gohr <andi@splitbrain.org> 127 */ 128 protected function callLock() { 129 global $ID; 130 global $INFO; 131 global $INPUT; 132 133 $ID = cleanID($INPUT->post->str('id')); 134 if (empty($ID)) return; 135 136 $INFO = pageinfo(); 137 138 $response = [ 139 'errors' => [], 140 'lock' => '0', 141 'draft' => '', 142 ]; 143 if (!$INFO['writable']) { 144 $response['errors'][] = 'Permission to write this page has been denied.'; 145 echo json_encode($response); 146 return; 147 } 148 149 if (!checklock($ID)) { 150 lock($ID); 151 $response['lock'] = '1'; 152 } 153 154 $draft = new Draft($ID, $INFO['client']); 155 if ($draft->saveDraft()) { 156 $response['draft'] = $draft->getDraftMessage(); 157 } else { 158 $response['errors'] = array_merge($response['errors'], $draft->getErrors()); 159 } 160 echo json_encode($response); 161 } 162 163 /** 164 * Delete a draft 165 * 166 * @author Andreas Gohr <andi@splitbrain.org> 167 */ 168 protected function callDraftdel() 169 { 170 global $INPUT; 171 $id = cleanID($INPUT->str('id')); 172 if (empty($id)) return; 173 174 $client = $_SERVER['REMOTE_USER']; 175 if (!$client) $client = clientIP(true); 176 177 $cname = getCacheName($client . $id, '.draft'); 178 @unlink($cname); 179 } 180 181 /** 182 * Return subnamespaces for the Mediamanager 183 * 184 * @author Andreas Gohr <andi@splitbrain.org> 185 */ 186 protected function callMedians() 187 { 188 global $conf; 189 global $INPUT; 190 191 // wanted namespace 192 $ns = cleanID($INPUT->post->str('ns')); 193 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 194 195 $lvl = count(explode(':', $ns)); 196 197 $data = array(); 198 search($data, $conf['mediadir'], 'search_index', array('nofiles' => true), $dir); 199 foreach (array_keys($data) as $item) { 200 $data[$item]['level'] = $lvl + 1; 201 } 202 echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li'); 203 } 204 205 /** 206 * Return list of files for the Mediamanager 207 * 208 * @author Andreas Gohr <andi@splitbrain.org> 209 */ 210 protected function callMedialist() { 211 global $NS; 212 global $INPUT; 213 214 $NS = cleanID($INPUT->post->str('ns')); 215 $sort = $INPUT->post->bool('recent') ? 'date' : 'natural'; 216 if ($INPUT->post->str('do') == 'media') { 217 tpl_mediaFileList(); 218 } else { 219 tpl_mediaContent(true, $sort); 220 } 221 } 222 223 /** 224 * Return the content of the right column 225 * (image details) for the Mediamanager 226 * 227 * @author Kate Arzamastseva <pshns@ukr.net> 228 */ 229 protected function callMediadetails() { 230 global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT; 231 $fullscreen = true; 232 require_once(DOKU_INC . 'lib/exe/mediamanager.php'); 233 234 $image = ''; 235 if ($INPUT->has('image')) $image = cleanID($INPUT->str('image')); 236 if (isset($IMG)) $image = $IMG; 237 if (isset($JUMPTO)) $image = $JUMPTO; 238 $rev = false; 239 if (isset($REV) && !$JUMPTO) $rev = $REV; 240 241 html_msgarea(); 242 tpl_mediaFileDetails($image, $rev); 243 } 244 245 /** 246 * Returns image diff representation for mediamanager 247 * 248 * @author Kate Arzamastseva <pshns@ukr.net> 249 */ 250 protected function callMediadiff() 251 { 252 global $NS; 253 global $INPUT; 254 255 $image = ''; 256 if ($INPUT->has('image')) $image = cleanID($INPUT->str('image')); 257 $NS = getNS($image); 258 $auth = auth_quickaclcheck("$NS:*"); 259 media_diff($image, $NS, $auth, true); 260 } 261 262 /** 263 * Manages file uploads 264 * 265 * @author Kate Arzamastseva <pshns@ukr.net> 266 */ 267 protected function callMediaupload() { 268 global $NS, $MSG, $INPUT; 269 270 $id = ''; 271 if (isset($_FILES['qqfile']['tmp_name'])) { 272 $id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']); 273 } elseif ($INPUT->get->has('qqfile')) { 274 $id = $INPUT->get->str('qqfile'); 275 } 276 277 $id = cleanID($id); 278 279 $NS = $INPUT->str('ns'); 280 $ns = $NS . ':' . getNS($id); 281 282 $AUTH = auth_quickaclcheck("$ns:*"); 283 if ($AUTH >= AUTH_UPLOAD) { 284 io_createNamespace("$ns:xxx", 'media'); 285 } 286 287 if (isset($_FILES['qqfile']['error']) && $_FILES['qqfile']['error']) unset($_FILES['qqfile']); 288 289 $res = false; 290 if (isset($_FILES['qqfile']['tmp_name'])) $res = media_upload($NS, $AUTH, $_FILES['qqfile']); 291 if ($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH); 292 293 if ($res) { 294 $result = array( 295 'success' => true, 296 'link' => media_managerURL(array('ns' => $ns, 'image' => $NS . ':' . $id), '&'), 297 'id' => $NS . ':' . $id, 298 'ns' => $NS 299 ); 300 } else { 301 $error = ''; 302 if (isset($MSG)) { 303 foreach ($MSG as $msg) { 304 $error .= $msg['msg']; 305 } 306 } 307 $result = array( 308 'error' => $error, 309 'ns' => $NS 310 ); 311 } 312 313 header('Content-Type: application/json'); 314 echo json_encode($result); 315 } 316 317 /** 318 * Return sub index for index view 319 * 320 * @author Andreas Gohr <andi@splitbrain.org> 321 */ 322 protected function callIndex() 323 { 324 global $conf; 325 global $INPUT; 326 327 // wanted namespace 328 $ns = cleanID($INPUT->post->str('idx')); 329 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 330 331 $lvl = count(explode(':', $ns)); 332 333 $data = array(); 334 search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir); 335 foreach (array_keys($data) as $item) { 336 $data[$item]['level'] = $lvl + 1; 337 } 338 echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index'); 339 } 340 341 /** 342 * List matching namespaces and pages for the link wizard 343 * 344 * @author Andreas Gohr <gohr@cosmocode.de> 345 */ 346 protected function callLinkwiz() 347 { 348 global $conf; 349 global $lang; 350 global $INPUT; 351 352 $q = ltrim(trim($INPUT->post->str('q')), ':'); 353 $id = noNS($q); 354 $ns = getNS($q); 355 356 $ns = cleanID($ns); 357 $id = cleanID($id); 358 359 $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); 360 361 $data = array(); 362 if($q !== '' && $ns === '') { 363 364 // use index to lookup matching pages 365 $MetadataSearch = MetadataSearch::getInstance(); 366 $pages = $MetadataSearch->pageLookup($id, true); 367 368 // result contains matches in pages and namespaces 369 // we now extract the matching namespaces to show 370 // them seperately 371 $dirs = array(); 372 373 foreach ($pages as $pid => $title) { 374 if (strpos(noNS($pid), $id) === false) { 375 // match was in the namespace 376 $dirs[getNS($pid)] = 1; // assoc array avoids dupes 377 } else { 378 // it is a matching page, add it to the result 379 $data[] = array( 380 'id' => $pid, 381 'title' => $title, 382 'type' => 'f', 383 ); 384 } 385 unset($pages[$pid]); 386 } 387 foreach ($dirs as $dir => $junk) { 388 $data[] = array( 389 'id' => $dir, 390 'type' => 'd', 391 ); 392 } 393 394 } else { 395 396 $opts = array( 397 'depth' => 1, 398 'listfiles' => true, 399 'listdirs' => true, 400 'pagesonly' => true, 401 'firsthead' => true, 402 'sneakyacl' => $conf['sneaky_index'], 403 ); 404 if ($id) $opts['filematch'] = '^.*\/' . $id; 405 if ($id) $opts['dirmatch'] = '^.*\/' . $id; 406 search($data, $conf['datadir'], 'search_universal', $opts, $nsd); 407 408 // add back to upper 409 if ($ns) { 410 array_unshift( 411 $data, array( 412 'id' => getNS($ns), 413 'type' => 'u', 414 ) 415 ); 416 } 417 } 418 419 // fixme sort results in a useful way ? 420 421 if (!count($data)) { 422 echo $lang['nothingfound']; 423 exit; 424 } 425 426 // output the found data 427 $even = 1; 428 foreach ($data as $item) { 429 $even *= -1; //zebra 430 431 if (($item['type'] == 'd' || $item['type'] == 'u') && $item['id'] !== '') $item['id'] .= ':'; 432 $link = wl($item['id']); 433 434 echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">'; 435 436 if ($item['type'] == 'u') { 437 $name = $lang['upperns']; 438 } else { 439 $name = hsc($item['id']); 440 } 441 442 echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>'; 443 444 if (!blank($item['title'])) { 445 echo '<span>' . hsc($item['title']) . '</span>'; 446 } 447 echo '</div>'; 448 } 449 450 } 451 452} 453