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