[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 namespace dokuwiki\Remote; 4 5 use Doku_Renderer_xhtml; 6 use dokuwiki\ChangeLog\MediaChangeLog; 7 use dokuwiki\ChangeLog\PageChangeLog; 8 use dokuwiki\Extension\Event; 9 use dokuwiki\Utf8\Sort; 10 11 define('DOKU_API_VERSION', 10); 12 13 /** 14 * Provides the core methods for the remote API. 15 * The methods are ordered in 'wiki.<method>' and 'dokuwiki.<method>' namespaces 16 */ 17 class ApiCore 18 { 19 /** @var int Increased whenever the API is changed */ 20 const API_VERSION = 10; 21 22 23 /** @var Api */ 24 private $api; 25 26 /** 27 * @param Api $api 28 */ 29 public function __construct(Api $api) 30 { 31 $this->api = $api; 32 } 33 34 /** 35 * Returns details about the core methods 36 * 37 * @return array 38 */ 39 public function __getRemoteInfo() 40 { 41 return array( 42 'dokuwiki.getVersion' => array( 43 'args' => array(), 44 'return' => 'string', 45 'doc' => 'Returns the running DokuWiki version.' 46 ), 'dokuwiki.login' => array( 47 'args' => array('string', 'string'), 48 'return' => 'int', 49 'doc' => 'Tries to login with the given credentials and sets auth cookies.', 50 'public' => '1' 51 ), 'dokuwiki.logoff' => array( 52 'args' => array(), 53 'return' => 'int', 54 'doc' => 'Tries to logoff by expiring auth cookies and the associated PHP session.' 55 ), 'dokuwiki.getPagelist' => array( 56 'args' => array('string', 'array'), 57 'return' => 'array', 58 'doc' => 'List all pages within the given namespace.', 59 'name' => 'readNamespace' 60 ), 'dokuwiki.search' => array( 61 'args' => array('string'), 62 'return' => 'array', 63 'doc' => 'Perform a fulltext search and return a list of matching pages' 64 ), 'dokuwiki.getTime' => array( 65 'args' => array(), 66 'return' => 'int', 67 'doc' => 'Returns the current time at the remote wiki server as Unix timestamp.', 68 ), 'dokuwiki.setLocks' => array( 69 'args' => array('array'), 70 'return' => 'array', 71 'doc' => 'Lock or unlock pages.' 72 ), 'dokuwiki.getTitle' => array( 73 'args' => array(), 74 'return' => 'string', 75 'doc' => 'Returns the wiki title.', 76 'public' => '1' 77 ), 'dokuwiki.appendPage' => array( 78 'args' => array('string', 'string', 'array'), 79 'return' => 'bool', 80 'doc' => 'Append text to a wiki page.' 81 ), 'dokuwiki.deleteUsers' => array( 82 'args' => array('array'), 83 'return' => 'bool', 84 'doc' => 'Remove one or more users from the list of registered users.' 85 ), 'wiki.getPage' => array( 86 'args' => array('string'), 87 'return' => 'string', 88 'doc' => 'Get the raw Wiki text of page, latest version.', 89 'name' => 'rawPage', 90 ), 'wiki.getPageVersion' => array( 91 'args' => array('string', 'int'), 92 'name' => 'rawPage', 93 'return' => 'string', 94 'doc' => 'Return a raw wiki page' 95 ), 'wiki.getPageHTML' => array( 96 'args' => array('string'), 97 'return' => 'string', 98 'doc' => 'Return page in rendered HTML, latest version.', 99 'name' => 'htmlPage' 100 ), 'wiki.getPageHTMLVersion' => array( 101 'args' => array('string', 'int'), 102 'return' => 'string', 103 'doc' => 'Return page in rendered HTML.', 104 'name' => 'htmlPage' 105 ), 'wiki.getAllPages' => array( 106 'args' => array(), 107 'return' => 'array', 108 'doc' => 'Returns a list of all pages. The result is an array of utf8 pagenames.', 109 'name' => 'listPages' 110 ), 'wiki.getAttachments' => array( 111 'args' => array('string', 'array'), 112 'return' => 'array', 113 'doc' => 'Returns a list of all media files.', 114 'name' => 'listAttachments' 115 ), 'wiki.getBackLinks' => array( 116 'args' => array('string'), 117 'return' => 'array', 118 'doc' => 'Returns the pages that link to this page.', 119 'name' => 'listBackLinks' 120 ), 'wiki.getPageInfo' => array( 121 'args' => array('string'), 122 'return' => 'array', 123 'doc' => 'Returns a struct with info about the page, latest version.', 124 'name' => 'pageInfo' 125 ), 'wiki.getPageInfoVersion' => array( 126 'args' => array('string', 'int'), 127 'return' => 'array', 128 'doc' => 'Returns a struct with info about the page.', 129 'name' => 'pageInfo' 130 ), 'wiki.getPageVersions' => array( 131 'args' => array('string', 'int'), 132 'return' => 'array', 133 'doc' => 'Returns the available revisions of the page.', 134 'name' => 'pageVersions' 135 ), 'wiki.putPage' => array( 136 'args' => array('string', 'string', 'array'), 137 'return' => 'bool', 138 'doc' => 'Saves a wiki page.' 139 ), 'wiki.listLinks' => array( 140 'args' => array('string'), 141 'return' => 'array', 142 'doc' => 'Lists all links contained in a wiki page.' 143 ), 'wiki.getRecentChanges' => array( 144 'args' => array('int'), 145 'return' => 'array', 146 'doc' => 'Returns a struct about all recent changes since given timestamp.' 147 ), 'wiki.getRecentMediaChanges' => array( 148 'args' => array('int'), 149 'return' => 'array', 150 'doc' => 'Returns a struct about all recent media changes since given timestamp.' 151 ), 'wiki.aclCheck' => array( 152 'args' => array('string', 'string', 'array'), 153 'return' => 'int', 154 'doc' => 'Returns the permissions of a given wiki page. By default, for current user/groups' 155 ), 'wiki.putAttachment' => array( 156 'args' => array('string', 'file', 'array'), 157 'return' => 'array', 158 'doc' => 'Upload a file to the wiki.' 159 ), 'wiki.deleteAttachment' => array( 160 'args' => array('string'), 161 'return' => 'int', 162 'doc' => 'Delete a file from the wiki.' 163 ), 'wiki.getAttachment' => array( 164 'args' => array('string'), 165 'doc' => 'Return a media file', 166 'return' => 'file', 167 'name' => 'getAttachment', 168 ), 'wiki.getAttachmentInfo' => array( 169 'args' => array('string'), 170 'return' => 'array', 171 'doc' => 'Returns a struct with info about the attachment.' 172 ), 'dokuwiki.getXMLRPCAPIVersion' => array( 173 'args' => array(), 174 'name' => 'getAPIVersion', 175 'return' => 'int', 176 'doc' => 'Returns the XMLRPC API version.', 177 'public' => '1', 178 ), 'wiki.getRPCVersionSupported' => array( 179 'args' => array(), 180 'name' => 'wikiRpcVersion', 181 'return' => 'int', 182 'doc' => 'Returns 2 with the supported RPC API version.', 183 'public' => '1' 184 ), 185 186 ); 187 } 188 189 /** 190 * @return string 191 */ 192 public function getVersion() 193 { 194 return getVersion(); 195 } 196 197 /** 198 * @return int unix timestamp 199 */ 200 public function getTime() 201 { 202 return time(); 203 } 204 205 /** 206 * Return a raw wiki page 207 * 208 * @param string $id wiki page id 209 * @param int|string $rev revision timestamp of the page or empty string 210 * @return string page text. 211 * @throws AccessDeniedException if no permission for page 212 */ 213 public function rawPage($id, $rev = '') 214 { 215 $id = $this->resolvePageId($id); 216 if (auth_quickaclcheck($id) < AUTH_READ) { 217 throw new AccessDeniedException('You are not allowed to read this file', 111); 218 } 219 $text = rawWiki($id, $rev); 220 if (!$text) { 221 return pageTemplate($id); 222 } else { 223 return $text; 224 } 225 } 226 227 /** 228 * Return a media file 229 * 230 * @author Gina Haeussge <osd@foosel.net> 231 * 232 * @param string $id file id 233 * @return mixed media file 234 * @throws AccessDeniedException no permission for media 235 * @throws RemoteException not exist 236 */ 237 public function getAttachment($id) 238 { 239 $id = cleanID($id); 240 if (auth_quickaclcheck(getNS($id) . ':*') < AUTH_READ) { 241 throw new AccessDeniedException('You are not allowed to read this file', 211); 242 } 243 244 $file = mediaFN($id); 245 if (!@ file_exists($file)) { 246 throw new RemoteException('The requested file does not exist', 221); 247 } 248 249 $data = io_readFile($file, false); 250 return $this->api->toFile($data); 251 } 252 253 /** 254 * Return info about a media file 255 * 256 * @author Gina Haeussge <osd@foosel.net> 257 * 258 * @param string $id page id 259 * @return array 260 */ 261 public function getAttachmentInfo($id) 262 { 263 $id = cleanID($id); 264 $info = array( 265 'lastModified' => $this->api->toDate(0), 266 'size' => 0, 267 ); 268 269 $file = mediaFN($id); 270 if (auth_quickaclcheck(getNS($id) . ':*') >= AUTH_READ) { 271 if (file_exists($file)) { 272 $info['lastModified'] = $this->api->toDate(filemtime($file)); 273 $info['size'] = filesize($file); 274 } else { 275 //Is it deleted media with changelog? 276 $medialog = new MediaChangeLog($id); 277 $revisions = $medialog->getRevisions(0, 1); 278 if (!empty($revisions)) { 279 $info['lastModified'] = $this->api->toDate($revisions[0]); 280 } 281 } 282 } 283 284 return $info; 285 } 286 287 /** 288 * Return a wiki page rendered to html 289 * 290 * @param string $id page id 291 * @param string|int $rev revision timestamp or empty string 292 * @return null|string html 293 * @throws AccessDeniedException no access to page 294 */ 295 public function htmlPage($id, $rev = '') 296 { 297 $id = $this->resolvePageId($id); 298 if (auth_quickaclcheck($id) < AUTH_READ) { 299 throw new AccessDeniedException('You are not allowed to read this page', 111); 300 } 301 return p_wiki_xhtml($id, $rev, false); 302 } 303 304 /** 305 * List all pages - we use the indexer list here 306 * 307 * @return array 308 */ 309 public function listPages() 310 { 311 $list = array(); 312 $pages = idx_get_indexer()->getPages(); 313 $pages = array_filter(array_filter($pages, 'isVisiblePage'), 'page_exists'); 314 Sort::ksort($pages); 315 316 foreach (array_keys($pages) as $idx) { 317 $perm = auth_quickaclcheck($pages[$idx]); 318 if ($perm < AUTH_READ) { 319 continue; 320 } 321 $page = array(); 322 $page['id'] = trim($pages[$idx]); 323 $page['perms'] = $perm; 324 $page['size'] = @filesize(wikiFN($pages[$idx])); 325 $page['lastModified'] = $this->api->toDate(@filemtime(wikiFN($pages[$idx]))); 326 $list[] = $page; 327 } 328 329 return $list; 330 } 331 332 /** 333 * List all pages in the given namespace (and below) 334 * 335 * @param string $ns 336 * @param array $opts 337 * $opts['depth'] recursion level, 0 for all 338 * $opts['hash'] do md5 sum of content? 339 * @return array 340 */ 341 public function readNamespace($ns, $opts = array()) 342 { 343 global $conf; 344 345 if (!is_array($opts)) $opts = array(); 346 347 $ns = cleanID($ns); 348 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 349 $data = array(); 350 $opts['skipacl'] = 0; // no ACL skipping for XMLRPC 351 search($data, $conf['datadir'], 'search_allpages', $opts, $dir); 352 return $data; 353 } 354 355 /** 356 * List all pages in the given namespace (and below) 357 * 358 * @param string $query 359 * @return array 360 */ 361 public function search($query) 362 { 363 $regex = array(); 364 $data = ft_pageSearch($query, $regex); 365 $pages = array(); 366 367 // prepare additional data 368 $idx = 0; 369 foreach ($data as $id => $score) { 370 $file = wikiFN($id); 371 372 if ($idx < FT_SNIPPET_NUMBER) { 373 $snippet = ft_snippet($id, $regex); 374 $idx++; 375 } else { 376 $snippet = ''; 377 } 378 379 $pages[] = array( 380 'id' => $id, 381 'score' => intval($score), 382 'rev' => filemtime($file), 383 'mtime' => filemtime($file), 384 'size' => filesize($file), 385 'snippet' => $snippet, 386 'title' => useHeading('navigation') ? p_get_first_heading($id) : $id 387 ); 388 } 389 return $pages; 390 } 391 392 /** 393 * Returns the wiki title. 394 * 395 * @return string 396 */ 397 public function getTitle() 398 { 399 global $conf; 400 return $conf['title']; 401 } 402 403 /** 404 * List all media files. 405 * 406 * Available options are 'recursive' for also including the subnamespaces 407 * in the listing, and 'pattern' for filtering the returned files against 408 * a regular expression matching their name. 409 * 410 * @author Gina Haeussge <osd@foosel.net> 411 * 412 * @param string $ns 413 * @param array $options 414 * $options['depth'] recursion level, 0 for all 415 * $options['showmsg'] shows message if invalid media id is used 416 * $options['pattern'] check given pattern 417 * $options['hash'] add hashes to result list 418 * @return array 419 * @throws AccessDeniedException no access to the media files 420 */ 421 public function listAttachments($ns, $options = array()) 422 { 423 global $conf; 424 425 $ns = cleanID($ns); 426 427 if (!is_array($options)) $options = array(); 428 $options['skipacl'] = 0; // no ACL skipping for XMLRPC 429 430 if (auth_quickaclcheck($ns . ':*') >= AUTH_READ) { 431 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 432 433 $data = array(); 434 search($data, $conf['mediadir'], 'search_media', $options, $dir); 435 $len = count($data); 436 if (!$len) return array(); 437 438 for ($i = 0; $i < $len; $i++) { 439 unset($data[$i]['meta']); 440 $data[$i]['perms'] = $data[$i]['perm']; 441 unset($data[$i]['perm']); 442 $data[$i]['lastModified'] = $this->api->toDate($data[$i]['mtime']); 443 } 444 return $data; 445 } else { 446 throw new AccessDeniedException('You are not allowed to list media files.', 215); 447 } 448 } 449 450 /** 451 * Return a list of backlinks 452 * 453 * @param string $id page id 454 * @return array 455 */ 456 public function listBackLinks($id) 457 { 458 return ft_backlinks($this->resolvePageId($id)); 459 } 460 461 /** 462 * Return some basic data about a page 463 * 464 * @param string $id page id 465 * @param string|int $rev revision timestamp or empty string 466 * @return array 467 * @throws AccessDeniedException no access for page 468 * @throws RemoteException page not exist 469 */ 470 public function pageInfo($id, $rev = '') 471 { 472 $id = $this->resolvePageId($id); 473 if (auth_quickaclcheck($id) < AUTH_READ) { 474 throw new AccessDeniedException('You are not allowed to read this page', 111); 475 } 476 $file = wikiFN($id, $rev); 477 $time = @filemtime($file); 478 if (!$time) { 479 throw new RemoteException('The requested page does not exist', 121); 480 } 481 482 // set revision to current version if empty, use revision otherwise 483 // as the timestamps of old files are not necessarily correct 484 if ($rev === '') { 485 $rev = $time; 486 } 487 488 $pagelog = new PageChangeLog($id, 1024); 489 $info = $pagelog->getRevisionInfo($rev); 490 491 $data = array( 492 'name' => $id, 493 'lastModified' => $this->api->toDate($rev), 494 'author' => is_array($info) ? (($info['user']) ? $info['user'] : $info['ip']) : null, 495 'version' => $rev 496 ); 497 498 return ($data); 499 } 500 501 /** 502 * Save a wiki page 503 * 504 * @author Michael Klier <chi@chimeric.de> 505 * 506 * @param string $id page id 507 * @param string $text wiki text 508 * @param array $params parameters: summary, minor edit 509 * @return bool 510 * @throws AccessDeniedException no write access for page 511 * @throws RemoteException no id, empty new page or locked 512 */ 513 public function putPage($id, $text, $params = array()) 514 { 515 global $TEXT; 516 global $lang; 517 518 $id = $this->resolvePageId($id); 519 $TEXT = cleanText($text); 520 $sum = $params['sum']; 521 $minor = $params['minor']; 522 523 if (empty($id)) { 524 throw new RemoteException('Empty page ID', 131); 525 } 526 527 if (!page_exists($id) && trim($TEXT) == '') { 528 throw new RemoteException('Refusing to write an empty new wiki page', 132); 529 } 530 531 if (auth_quickaclcheck($id) < AUTH_EDIT) { 532 throw new AccessDeniedException('You are not allowed to edit this page', 112); 533 } 534 535 // Check, if page is locked 536 if (checklock($id)) { 537 throw new RemoteException('The page is currently locked', 133); 538 } 539 540 // SPAM check 541 if (checkwordblock()) { 542 throw new RemoteException('Positive wordblock check', 134); 543 } 544 545 // autoset summary on new pages 546 if (!page_exists($id) && empty($sum)) { 547 $sum = $lang['created']; 548 } 549 550 // autoset summary on deleted pages 551 if (page_exists($id) && empty($TEXT) && empty($sum)) { 552 $sum = $lang['deleted']; 553 } 554 555 lock($id); 556 557 saveWikiText($id, $TEXT, $sum, $minor); 558 559 unlock($id); 560 561 // run the indexer if page wasn't indexed yet 562 idx_addPage($id); 563 564 return true; 565 } 566 567 /** 568 * Appends text to a wiki page. 569 * 570 * @param string $id page id 571 * @param string $text wiki text 572 * @param array $params such as summary,minor 573 * @return bool|string 574 * @throws RemoteException 575 */ 576 public function appendPage($id, $text, $params = array()) 577 { 578 $currentpage = $this->rawPage($id); 579 if (!is_string($currentpage)) { 580 return $currentpage; 581 } 582 return $this->putPage($id, $currentpage . $text, $params); 583 } 584 585 /** 586 * Remove one or more users from the list of registered users 587 * 588 * @param string[] $usernames List of usernames to remove 589 * 590 * @return bool 591 * 592 * @throws AccessDeniedException 593 */ 594 public function deleteUsers($usernames) 595 { 596 if (!auth_isadmin()) { 597 throw new AccessDeniedException('Only admins are allowed to delete users', 114); 598 } 599 /** @var \dokuwiki\Extension\AuthPlugin $auth */ 600 global $auth; 601 return (bool)$auth->triggerUserMod('delete', array($usernames)); 602 } 603 604 /** 605 * Uploads a file to the wiki. 606 * 607 * Michael Klier <chi@chimeric.de> 608 * 609 * @param string $id page id 610 * @param string $file 611 * @param array $params such as overwrite 612 * @return false|string 613 * @throws RemoteException 614 */ 615 public function putAttachment($id, $file, $params = array()) 616 { 617 $id = cleanID($id); 618 $auth = auth_quickaclcheck(getNS($id) . ':*'); 619 620 if (!isset($id)) { 621 throw new RemoteException('Filename not given.', 231); 622 } 623 624 global $conf; 625 626 $ftmp = $conf['tmpdir'] . '/' . md5($id . clientIP()); 627 628 // save temporary file 629 @unlink($ftmp); 630 io_saveFile($ftmp, $file); 631 632 $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename'); 633 if (is_array($res)) { 634 throw new RemoteException($res[0], -$res[1]); 635 } else { 636 return $res; 637 } 638 } 639 640 /** 641 * Deletes a file from the wiki. 642 * 643 * @author Gina Haeussge <osd@foosel.net> 644 * 645 * @param string $id page id 646 * @return int 647 * @throws AccessDeniedException no permissions 648 * @throws RemoteException file in use or not deleted 649 */ 650 public function deleteAttachment($id) 651 { 652 $id = cleanID($id); 653 $auth = auth_quickaclcheck(getNS($id) . ':*'); 654 $res = media_delete($id, $auth); 655 if ($res & DOKU_MEDIA_DELETED) { 656 return 0; 657 } elseif ($res & DOKU_MEDIA_NOT_AUTH) { 658 throw new AccessDeniedException('You don\'t have permissions to delete files.', 212); 659 } elseif ($res & DOKU_MEDIA_INUSE) { 660 throw new RemoteException('File is still referenced', 232); 661 } else { 662 throw new RemoteException('Could not delete file', 233); 663 } 664 } 665 666 /** 667 * Returns the permissions of a given wiki page for the current user or another user 668 * 669 * @param string $id page id 670 * @param string|null $user username 671 * @param array|null $groups array of groups 672 * @return int permission level 673 */ 674 public function aclCheck($id, $user = null, $groups = null) 675 { 676 /** @var \dokuwiki\Extension\AuthPlugin $auth */ 677 global $auth; 678 679 $id = $this->resolvePageId($id); 680 if ($user === null) { 681 return auth_quickaclcheck($id); 682 } else { 683 if ($groups === null) { 684 $userinfo = $auth->getUserData($user); 685 if ($userinfo === false) { 686 $groups = array(); 687 } else { 688 $groups = $userinfo['grps']; 689 } 690 } 691 return auth_aclcheck($id, $user, $groups); 692 } 693 } 694 695 /** 696 * Lists all links contained in a wiki page 697 * 698 * @author Michael Klier <chi@chimeric.de> 699 * 700 * @param string $id page id 701 * @return array 702 * @throws AccessDeniedException no read access for page 703 */ 704 public function listLinks($id) 705 { 706 $id = $this->resolvePageId($id); 707 if (auth_quickaclcheck($id) < AUTH_READ) { 708 throw new AccessDeniedException('You are not allowed to read this page', 111); 709 } 710 $links = array(); 711 712 // resolve page instructions 713 $ins = p_cached_instructions(wikiFN($id)); 714 715 // instantiate new Renderer - needed for interwiki links 716 $Renderer = new Doku_Renderer_xhtml(); 717 $Renderer->interwiki = getInterwiki(); 718 719 // parse parse instructions 720 foreach ($ins as $in) { 721 $link = array(); 722 switch ($in[0]) { 723 case 'internallink': 724 $link['type'] = 'local'; 725 $link['page'] = $in[1][0]; 726 $link['href'] = wl($in[1][0]); 727 array_push($links, $link); 728 break; 729 case 'externallink': 730 $link['type'] = 'extern'; 731 $link['page'] = $in[1][0]; 732 $link['href'] = $in[1][0]; 733 array_push($links, $link); 734 break; 735 case 'interwikilink': 736 $url = $Renderer->_resolveInterWiki($in[1][2], $in[1][3]); 737 $link['type'] = 'extern'; 738 $link['page'] = $url; 739 $link['href'] = $url; 740 array_push($links, $link); 741 break; 742 } 743 } 744 745 return ($links); 746 } 747 748 /** 749 * Returns a list of recent changes since give timestamp 750 * 751 * @author Michael Hamann <michael@content-space.de> 752 * @author Michael Klier <chi@chimeric.de> 753 * 754 * @param int $timestamp unix timestamp 755 * @return array 756 * @throws RemoteException no valid timestamp 757 */ 758 public function getRecentChanges($timestamp) 759 { 760 if (strlen($timestamp) != 10) { 761 throw new RemoteException('The provided value is not a valid timestamp', 311); 762 } 763 764 $recents = getRecentsSince($timestamp); 765 766 $changes = array(); 767 768 foreach ($recents as $recent) { 769 $change = array(); 770 $change['name'] = $recent['id']; 771 $change['lastModified'] = $this->api->toDate($recent['date']); 772 $change['author'] = $recent['user']; 773 $change['version'] = $recent['date']; 774 $change['perms'] = $recent['perms']; 775 $change['size'] = @filesize(wikiFN($recent['id'])); 776 array_push($changes, $change); 777 } 778 779 if (!empty($changes)) { 780 return $changes; 781 } else { 782 // in case we still have nothing at this point 783 throw new RemoteException('There are no changes in the specified timeframe', 321); 784 } 785 } 786 787 /** 788 * Returns a list of recent media changes since give timestamp 789 * 790 * @author Michael Hamann <michael@content-space.de> 791 * @author Michael Klier <chi@chimeric.de> 792 * 793 * @param int $timestamp unix timestamp 794 * @return array 795 * @throws RemoteException no valid timestamp 796 */ 797 public function getRecentMediaChanges($timestamp) 798 { 799 if (strlen($timestamp) != 10) 800 throw new RemoteException('The provided value is not a valid timestamp', 311); 801 802 $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES); 803 804 $changes = array(); 805 806 foreach ($recents as $recent) { 807 $change = array(); 808 $change['name'] = $recent['id']; 809 $change['lastModified'] = $this->api->toDate($recent['date']); 810 $change['author'] = $recent['user']; 811 $change['version'] = $recent['date']; 812 $change['perms'] = $recent['perms']; 813 $change['size'] = @filesize(mediaFN($recent['id'])); 814 array_push($changes, $change); 815 } 816 817 if (!empty($changes)) { 818 return $changes; 819 } else { 820 // in case we still have nothing at this point 821 throw new RemoteException('There are no changes in the specified timeframe', 321); 822 } 823 } 824 825 /** 826 * Returns a list of available revisions of a given wiki page 827 * Number of returned pages is set by $conf['recent'] 828 * However not accessible pages are skipped, so less than $conf['recent'] could be returned 829 * 830 * @author Michael Klier <chi@chimeric.de> 831 * 832 * @param string $id page id 833 * @param int $first skip the first n changelog lines 834 * 0 = from current(if exists) 835 * 1 = from 1st old rev 836 * 2 = from 2nd old rev, etc 837 * @return array 838 * @throws AccessDeniedException no read access for page 839 * @throws RemoteException empty id 840 */ 841 public function pageVersions($id, $first = 0) 842 { 843 $id = $this->resolvePageId($id); 844 if (auth_quickaclcheck($id) < AUTH_READ) { 845 throw new AccessDeniedException('You are not allowed to read this page', 111); 846 } 847 global $conf; 848 849 $versions = array(); 850 851 if (empty($id)) { 852 throw new RemoteException('Empty page ID', 131); 853 } 854 855 $first = (int) $first; 856 $first_rev = $first - 1; 857 $first_rev = $first_rev < 0 ? 0 : $first_rev; 858 $pagelog = new PageChangeLog($id); 859 $revisions = $pagelog->getRevisions($first_rev, $conf['recent']); 860 861 if ($first == 0) { 862 array_unshift($revisions, ''); // include current revision 863 if (count($revisions) > $conf['recent']) { 864 array_pop($revisions); // remove extra log entry 865 } 866 } 867 868 if (!empty($revisions)) { 869 foreach ($revisions as $rev) { 870 $file = wikiFN($id, $rev); 871 $time = @filemtime($file); 872 // we check if the page actually exists, if this is not the 873 // case this can lead to less pages being returned than 874 // specified via $conf['recent'] 875 if ($time) { 876 $pagelog->setChunkSize(1024); 877 $info = $pagelog->getRevisionInfo($rev ? $rev : $time); 878 if (!empty($info)) { 879 $data = array(); 880 $data['user'] = $info['user']; 881 $data['ip'] = $info['ip']; 882 $data['type'] = $info['type']; 883 $data['sum'] = $info['sum']; 884 $data['modified'] = $this->api->toDate($info['date']); 885 $data['version'] = $info['date']; 886 array_push($versions, $data); 887 } 888 } 889 } 890 return $versions; 891 } else { 892 return array(); 893 } 894 } 895 896 /** 897 * The version of Wiki RPC API supported 898 */ 899 public function wikiRpcVersion() 900 { 901 return 2; 902 } 903 904 /** 905 * Locks or unlocks a given batch of pages 906 * 907 * Give an associative array with two keys: lock and unlock. Both should contain a 908 * list of pages to lock or unlock 909 * 910 * Returns an associative array with the keys locked, lockfail, unlocked and 911 * unlockfail, each containing lists of pages. 912 * 913 * @param array[] $set list pages with array('lock' => array, 'unlock' => array) 914 * @return array 915 */ 916 public function setLocks($set) 917 { 918 $locked = array(); 919 $lockfail = array(); 920 $unlocked = array(); 921 $unlockfail = array(); 922 923 foreach ((array) $set['lock'] as $id) { 924 $id = $this->resolvePageId($id); 925 if (auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)) { 926 $lockfail[] = $id; 927 } else { 928 lock($id); 929 $locked[] = $id; 930 } 931 } 932 933 foreach ((array) $set['unlock'] as $id) { 934 $id = $this->resolvePageId($id); 935 if (auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)) { 936 $unlockfail[] = $id; 937 } else { 938 $unlocked[] = $id; 939 } 940 } 941 942 return array( 943 'locked' => $locked, 944 'lockfail' => $lockfail, 945 'unlocked' => $unlocked, 946 'unlockfail' => $unlockfail, 947 ); 948 } 949 950 /** 951 * Return API version 952 * 953 * @return int 954 */ 955 public function getAPIVersion() 956 { 957 return self::API_VERSION; 958 } 959 960 /** 961 * Login 962 * 963 * @param string $user 964 * @param string $pass 965 * @return int 966 */ 967 public function login($user, $pass) 968 { 969 global $conf; 970 /** @var \dokuwiki\Extension\AuthPlugin $auth */ 971 global $auth; 972 973 if (!$conf['useacl']) return 0; 974 if (!$auth) return 0; 975 976 @session_start(); // reopen session for login 977 $ok = null; 978 if ($auth->canDo('external')) { 979 $ok = $auth->trustExternal($user, $pass, false); 980 } 981 if ($ok === null){ 982 $evdata = array( 983 'user' => $user, 984 'password' => $pass, 985 'sticky' => false, 986 'silent' => true, 987 ); 988 $ok = Event::createAndTrigger('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper'); 989 } 990 session_write_close(); // we're done with the session 991 992 return $ok; 993 } 994 995 /** 996 * Log off 997 * 998 * @return int 999 */ 1000 public function logoff() 1001 { 1002 global $conf; 1003 global $auth; 1004 if (!$conf['useacl']) return 0; 1005 if (!$auth) return 0; 1006 1007 auth_logoff(); 1008 1009 return 1; 1010 } 1011 1012 /** 1013 * Resolve page id 1014 * 1015 * @param string $id page id 1016 * @return string 1017 */ 1018 private function resolvePageId($id) 1019 { 1020 $id = cleanID($id); 1021 if (empty($id)) { 1022 global $conf; 1023 $id = cleanID($conf['start']); 1024 } 1025 return $id; 1026 } 1027 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body