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