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