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 $conf; 123 global $lang; 124 global $ID; 125 global $INFO; 126 global $INPUT; 127 128 $ID = cleanID($INPUT->post->str('id')); 129 if(empty($ID)) return; 130 131 $INFO = pageinfo(); 132 133 if(!$INFO['writable']) { 134 echo 'Permission denied'; 135 return; 136 } 137 138 if(!checklock($ID)) { 139 lock($ID); 140 echo 1; 141 } 142 143 if($conf['usedraft'] && $INPUT->post->str('wikitext')) { 144 $client = $_SERVER['REMOTE_USER']; 145 if(!$client) $client = clientIP(true); 146 147 $draft = array( 148 'id' => $ID, 149 'prefix' => substr($INPUT->post->str('prefix'), 0, -1), 150 'text' => $INPUT->post->str('wikitext'), 151 'suffix' => $INPUT->post->str('suffix'), 152 'date' => $INPUT->post->int('date'), 153 'client' => $client, 154 ); 155 $cname = getCacheName($draft['client'] . $ID, '.draft'); 156 if(io_saveFile($cname, serialize($draft))) { 157 echo $lang['draftdate'] . ' ' . dformat(); 158 } 159 } 160 161 } 162 163 /** 164 * Delete a draft 165 * 166 * @author Andreas Gohr <andi@splitbrain.org> 167 */ 168 protected function call_draftdel() { 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 call_medians() { 186 global $conf; 187 global $INPUT; 188 189 // wanted namespace 190 $ns = cleanID($INPUT->post->str('ns')); 191 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 192 193 $lvl = count(explode(':', $ns)); 194 195 $data = array(); 196 search($data, $conf['mediadir'], 'search_index', array('nofiles' => true), $dir); 197 foreach(array_keys($data) as $item) { 198 $data[$item]['level'] = $lvl + 1; 199 } 200 echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li'); 201 } 202 203 /** 204 * Return list of files for the Mediamanager 205 * 206 * @author Andreas Gohr <andi@splitbrain.org> 207 */ 208 protected function call_medialist() { 209 global $NS; 210 global $INPUT; 211 212 $NS = cleanID($INPUT->post->str('ns')); 213 $sort = $INPUT->post->bool('recent') ? 'date' : 'natural'; 214 if($INPUT->post->str('do') == 'media') { 215 tpl_mediaFileList(); 216 } else { 217 tpl_mediaContent(true, $sort); 218 } 219 } 220 221 /** 222 * Return the content of the right column 223 * (image details) for the Mediamanager 224 * 225 * @author Kate Arzamastseva <pshns@ukr.net> 226 */ 227 protected function call_mediadetails() { 228 global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT; 229 $fullscreen = true; 230 require_once(DOKU_INC . 'lib/exe/mediamanager.php'); 231 232 $image = ''; 233 if($INPUT->has('image')) $image = cleanID($INPUT->str('image')); 234 if(isset($IMG)) $image = $IMG; 235 if(isset($JUMPTO)) $image = $JUMPTO; 236 $rev = false; 237 if(isset($REV) && !$JUMPTO) $rev = $REV; 238 239 html_msgarea(); 240 tpl_mediaFileDetails($image, $rev); 241 } 242 243 /** 244 * Returns image diff representation for mediamanager 245 * 246 * @author Kate Arzamastseva <pshns@ukr.net> 247 */ 248 protected function call_mediadiff() { 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 call_mediaupload() { 265 global $NS, $MSG, $INPUT; 266 267 $id = ''; 268 if($_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($_FILES['qqfile']['error']) unset($_FILES['qqfile']); 285 286 $res = false; 287 if($_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 $json = new \JSON; 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 call_index() { 320 global $conf; 321 global $INPUT; 322 323 // wanted namespace 324 $ns = cleanID($INPUT->post->str('idx')); 325 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 326 327 $lvl = count(explode(':', $ns)); 328 329 $data = array(); 330 search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir); 331 foreach(array_keys($data) as $item) { 332 $data[$item]['level'] = $lvl + 1; 333 } 334 echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index'); 335 } 336 337 /** 338 * List matching namespaces and pages for the link wizard 339 * 340 * @author Andreas Gohr <gohr@cosmocode.de> 341 */ 342 protected function call_linkwiz() { 343 global $conf; 344 global $lang; 345 global $INPUT; 346 347 $q = ltrim(trim($INPUT->post->str('q')), ':'); 348 $id = noNS($q); 349 $ns = getNS($q); 350 351 $ns = cleanID($ns); 352 $id = cleanID($id); 353 354 $nsd = utf8_encodeFN(str_replace(':', '/', $ns)); 355 356 $data = array(); 357 if($q && !$ns) { 358 359 // use index to lookup matching pages 360 $pages = ft_pageLookup($id, true); 361 362 // result contains matches in pages and namespaces 363 // we now extract the matching namespaces to show 364 // them seperately 365 $dirs = array(); 366 367 foreach($pages as $pid => $title) { 368 if(strpos(noNS($pid), $id) === false) { 369 // match was in the namespace 370 $dirs[getNS($pid)] = 1; // assoc array avoids dupes 371 } else { 372 // it is a matching page, add it to the result 373 $data[] = array( 374 'id' => $pid, 375 'title' => $title, 376 'type' => 'f', 377 ); 378 } 379 unset($pages[$pid]); 380 } 381 foreach($dirs as $dir => $junk) { 382 $data[] = array( 383 'id' => $dir, 384 'type' => 'd', 385 ); 386 } 387 388 } else { 389 390 $opts = array( 391 'depth' => 1, 392 'listfiles' => true, 393 'listdirs' => true, 394 'pagesonly' => true, 395 'firsthead' => true, 396 'sneakyacl' => $conf['sneaky_index'], 397 ); 398 if($id) $opts['filematch'] = '^.*\/' . $id; 399 if($id) $opts['dirmatch'] = '^.*\/' . $id; 400 search($data, $conf['datadir'], 'search_universal', $opts, $nsd); 401 402 // add back to upper 403 if($ns) { 404 array_unshift( 405 $data, array( 406 'id' => getNS($ns), 407 'type' => 'u', 408 ) 409 ); 410 } 411 } 412 413 // fixme sort results in a useful way ? 414 415 if(!count($data)) { 416 echo $lang['nothingfound']; 417 exit; 418 } 419 420 // output the found data 421 $even = 1; 422 foreach($data as $item) { 423 $even *= -1; //zebra 424 425 if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id']) $item['id'] .= ':'; 426 $link = wl($item['id']); 427 428 echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">'; 429 430 if($item['type'] == 'u') { 431 $name = $lang['upperns']; 432 } else { 433 $name = hsc($item['id']); 434 } 435 436 echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>'; 437 438 if(!blank($item['title'])) { 439 echo '<span>' . hsc($item['title']) . '</span>'; 440 } 441 echo '</div>'; 442 } 443 444 } 445 446} 447