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