[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> RemoteAPICore.php (source)

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