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