1<?php 2if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../'); 3 4// fix when '<?xml' isn't on the very first line 5if(isset($HTTP_RAW_POST_DATA)) $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA); 6 7 8require_once(DOKU_INC.'inc/init.php'); 9require_once(DOKU_INC.'inc/common.php'); 10require_once(DOKU_INC.'inc/auth.php'); 11session_write_close(); //close session 12 13if(!$conf['xmlrpc']) { 14 die('XML-RPC server not enabled.'); 15 // FIXME check for groups allowed 16} 17 18require_once(DOKU_INC.'inc/IXR_Library.php'); 19 20 21/** 22 * Contains needed wrapper functions and registers all available 23 * XMLRPC functions. 24 */ 25class dokuwiki_xmlrpc_server extends IXR_IntrospectionServer { 26 var $methods = array(); 27 28 /** 29 * Constructor. Register methods and run Server 30 */ 31 function dokuwiki_xmlrpc_server(){ 32 $this->IXR_IntrospectionServer(); 33 34 /* DokuWiki's own methods */ 35 $this->addCallback( 36 'dokuwiki.getVersion', 37 'getVersion', 38 array('string'), 39 'Returns the running DokuWiki version.' 40 ); 41 42 $this->addCallback( 43 'dokuwiki.getPagelist', 44 'this:readNamespace', 45 array('struct','string','struct'), 46 'List all pages within the given namespace.' 47 ); 48 49 $this->addCallback( 50 'dokuwiki.getTime', 51 'time', 52 array('int'), 53 'Return the current time at the wiki server.' 54 ); 55 56 $this->addCallback( 57 'dokuwiki.setLocks', 58 'this:setLocks', 59 array('struct','struct'), 60 'Lock or unlock pages.' 61 ); 62 63 /* Wiki API v2 http://www.jspwiki.org/wiki/WikiRPCInterface2 */ 64 $this->addCallback( 65 'wiki.getRPCVersionSupported', 66 'this:wiki_RPCVersion', 67 array('int'), 68 'Returns 2 with the supported RPC API version.' 69 ); 70 $this->addCallback( 71 'wiki.getPage', 72 'this:rawPage', 73 array('string','string'), 74 'Get the raw Wiki text of page, latest version.' 75 ); 76 $this->addCallback( 77 'wiki.getPageVersion', 78 'this:rawPage', 79 array('string','string','int'), 80 'Get the raw Wiki text of page.' 81 ); 82 $this->addCallback( 83 'wiki.getPageHTML', 84 'this:htmlPage', 85 array('string','string'), 86 'Return page in rendered HTML, latest version.' 87 ); 88 $this->addCallback( 89 'wiki.getPageHTMLVersion', 90 'this:htmlPage', 91 array('string','string','int'), 92 'Return page in rendered HTML.' 93 ); 94 $this->addCallback( 95 'wiki.getAllPages', 96 'this:listPages', 97 array('struct'), 98 'Returns a list of all pages. The result is an array of utf8 pagenames.' 99 ); 100 $this->addCallback( 101 'wiki.getAttachments', 102 'this:listAttachments', 103 array('struct', 'string', 'struct'), 104 'Returns a list of all media files.' 105 ); 106 $this->addCallback( 107 'wiki.getBackLinks', 108 'this:listBackLinks', 109 array('struct','string'), 110 'Returns the pages that link to this page.' 111 ); 112 $this->addCallback( 113 'wiki.getPageInfo', 114 'this:pageInfo', 115 array('struct','string'), 116 'Returns a struct with infos about the page.' 117 ); 118 $this->addCallback( 119 'wiki.getPageInfoVersion', 120 'this:pageInfo', 121 array('struct','string','int'), 122 'Returns a struct with infos about the page.' 123 ); 124 $this->addCallback( 125 'wiki.getPageVersions', 126 'this:pageVersions', 127 array('struct','string','int'), 128 'Returns the available revisions of the page.' 129 ); 130 $this->addCallback( 131 'wiki.putPage', 132 'this:putPage', 133 array('int', 'string', 'string', 'struct'), 134 'Saves a wiki page.' 135 ); 136 $this->addCallback( 137 'wiki.listLinks', 138 'this:listLinks', 139 array('struct','string'), 140 'Lists all links contained in a wiki page.' 141 ); 142 $this->addCallback( 143 'wiki.getRecentChanges', 144 'this:getRecentChanges', 145 array('struct','int'), 146 'Returns a struct about all recent changes since given timestamp.' 147 ); 148 $this->addCallback( 149 'wiki.getRecentMediaChanges', 150 'this:getRecentMediaChanges', 151 array('struct','int'), 152 'Returns a struct about all recent media changes since given timestamp.' 153 ); 154 $this->addCallback( 155 'wiki.aclCheck', 156 'this:aclCheck', 157 array('int', 'string'), 158 'Returns the permissions of a given wiki page.' 159 ); 160 $this->addCallback( 161 'wiki.putAttachment', 162 'this:putAttachment', 163 array('struct', 'string', 'base64', 'struct'), 164 'Upload a file to the wiki.' 165 ); 166 $this->addCallback( 167 'wiki.deleteAttachment', 168 'this:deleteAttachment', 169 array('int', 'string'), 170 'Delete a file from the wiki.' 171 ); 172 $this->addCallback( 173 'wiki.getAttachment', 174 'this:getAttachment', 175 array('base64', 'string'), 176 'Download a file from the wiki.' 177 ); 178 $this->addCallback( 179 'wiki.getAttachmentInfo', 180 'this:getAttachmentInfo', 181 array('struct', 'string'), 182 'Returns a struct with infos about the attachment.' 183 ); 184 185 /** 186 * Trigger XMLRPC_CALLBACK_REGISTER, action plugins can use this event 187 * to extend the XMLRPC interface and register their own callbacks. 188 * 189 * Event data: 190 * The XMLRPC server object: 191 * 192 * $event->data->addCallback() - register a callback, the second 193 * paramter has to be of the form "plugin:<pluginname>:<plugin 194 * method>" 195 * 196 * $event->data->callbacks - an array which holds all awaylable 197 * callbacks 198 */ 199 trigger_event('XMLRPC_CALLBACK_REGISTER', $this); 200 201 $this->serve(); 202 } 203 204 /** 205 * Return a raw wiki page 206 */ 207 function rawPage($id,$rev=''){ 208 if(auth_quickaclcheck($id) < AUTH_READ){ 209 return new IXR_Error(1, 'You are not allowed to read this page'); 210 } 211 $text = rawWiki($id,$rev); 212 if(!$text) { 213 $data = array($id); 214 return trigger_event('HTML_PAGE_FROMTEMPLATE',$data,'pageTemplate',true); 215 } else { 216 return $text; 217 } 218 } 219 220 /** 221 * Return a media file encoded in base64 222 * 223 * @author Gina Haeussge <osd@foosel.net> 224 */ 225 function getAttachment($id){ 226 $id = cleanID($id); 227 if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ) 228 return new IXR_Error(1, 'You are not allowed to read this file'); 229 230 $file = mediaFN($id); 231 if (!@ file_exists($file)) 232 return new IXR_Error(1, 'The requested file does not exist'); 233 234 $data = io_readFile($file, false); 235 $base64 = base64_encode($data); 236 return $base64; 237 } 238 239 /** 240 * Return info about a media file 241 * 242 * @author Gina Haeussge <osd@foosel.net> 243 */ 244 function getAttachmentInfo($id){ 245 $id = cleanID($id); 246 $info = array( 247 'lastModified' => 0, 248 'size' => 0, 249 ); 250 251 $file = mediaFN($id); 252 if ((auth_quickaclcheck(getNS($id).':*') >= AUTH_READ) && file_exists($file)){ 253 $info['lastModified'] = new IXR_Date(filemtime($file)); 254 $info['size'] = filesize($file); 255 } 256 257 return $info; 258 } 259 260 /** 261 * Return a wiki page rendered to html 262 */ 263 function htmlPage($id,$rev=''){ 264 if(auth_quickaclcheck($id) < AUTH_READ){ 265 return new IXR_Error(1, 'You are not allowed to read this page'); 266 } 267 return p_wiki_xhtml($id,$rev,false); 268 } 269 270 /** 271 * List all pages - we use the indexer list here 272 */ 273 function listPages(){ 274 global $conf; 275 276 $list = array(); 277 $pages = file($conf['indexdir'] . '/page.idx'); 278 $pages = array_filter($pages, 'isVisiblePage'); 279 280 foreach(array_keys($pages) as $idx) { 281 if(page_exists($pages[$idx])) { 282 $perm = auth_quickaclcheck($pages[$idx]); 283 if($perm >= AUTH_READ) { 284 $page = array(); 285 $page['id'] = trim($pages[$idx]); 286 $page['perms'] = $perm; 287 $page['size'] = @filesize(wikiFN($pages[$idx])); 288 $page['lastModified'] = new IXR_Date(@filemtime(wikiFN($pages[$idx]))); 289 $list[] = $page; 290 } 291 } 292 } 293 294 return $list; 295 } 296 297 /** 298 * List all pages in the given namespace (and below) 299 */ 300 function readNamespace($ns,$opts){ 301 global $conf; 302 303 if(!is_array($opts)) $opts=array(); 304 305 $ns = cleanID($ns); 306 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 307dbglog('ggg'); 308 $data = array(); 309 require_once(DOKU_INC.'inc/search.php'); 310 search($data, $conf['datadir'], 'search_allpages', $opts, $dir); 311dbglog($data); 312 return $data; 313 } 314 315 /** 316 * List all media files. 317 * 318 * Available options are 'recursive' for also including the subnamespaces 319 * in the listing, and 'pattern' for filtering the returned files against 320 * a regular expression matching their name. 321 * 322 * @author Gina Haeussge <osd@foosel.net> 323 */ 324 function listAttachments($ns, $options = array()) { 325 global $conf; 326 global $lang; 327 328 $ns = cleanID($ns); 329 330 if (!is_array($options)) 331 $options = array(); 332 333 334 if(auth_quickaclcheck($ns.':*') >= AUTH_READ) { 335 $dir = utf8_encodeFN(str_replace(':', '/', $ns)); 336 337 $data = array(); 338 require_once(DOKU_INC.'inc/search.php'); 339 search($data, $conf['mediadir'], 'search_media', $options, $dir); 340 $len = count($data); 341 if(!$len) return array(); 342 343 for($i=0; $i<$len; $i++) { 344 unset($data[$i]['meta']); 345 $data[$i]['lastModified'] = new IXR_Date($data[$i]['mtime']); 346 } 347 return $data; 348 } else { 349 return new IXR_Error(1, 'You are not allowed to list media files.'); 350 } 351 } 352 353 /** 354 * Return a list of backlinks 355 */ 356 function listBackLinks($id){ 357 require_once(DOKU_INC.'inc/fulltext.php'); 358 return ft_backlinks($id); 359 } 360 361 /** 362 * Return some basic data about a page 363 */ 364 function pageInfo($id,$rev=''){ 365 if(auth_quickaclcheck($id) < AUTH_READ){ 366 return new IXR_Error(1, 'You are not allowed to read this page'); 367 } 368 $file = wikiFN($id,$rev); 369 $time = @filemtime($file); 370 if(!$time){ 371 return new IXR_Error(10, 'The requested page does not exist'); 372 } 373 374 $info = getRevisionInfo($id, $time, 1024); 375 376 $data = array( 377 'name' => $id, 378 'lastModified' => new IXR_Date($time), 379 'author' => (($info['user']) ? $info['user'] : $info['ip']), 380 'version' => $time 381 ); 382 383 return ($data); 384 } 385 386 /** 387 * Save a wiki page 388 * 389 * @author Michael Klier <chi@chimeric.de> 390 */ 391 function putPage($id, $text, $params) { 392 global $TEXT; 393 global $lang; 394 global $conf; 395 396 $id = cleanID($id); 397 $TEXT = trim($text); 398 $sum = $params['sum']; 399 $minor = $params['minor']; 400 401 if(empty($id)) 402 return new IXR_Error(1, 'Empty page ID'); 403 404 if(!page_exists($id) && empty($TEXT)) { 405 return new IXR_ERROR(1, 'Refusing to write an empty new wiki page'); 406 } 407 408 if(auth_quickaclcheck($id) < AUTH_EDIT) 409 return new IXR_Error(1, 'You are not allowed to edit this page'); 410 411 // Check, if page is locked 412 if(checklock($id)) 413 return new IXR_Error(1, 'The page is currently locked'); 414 415 // SPAM check 416 if(checkwordblock()) 417 return new IXR_Error(1, 'Positive wordblock check'); 418 419 // autoset summary on new pages 420 if(!page_exists($id) && empty($sum)) { 421 $sum = $lang['created']; 422 } 423 424 // autoset summary on deleted pages 425 if(page_exists($id) && empty($TEXT) && empty($sum)) { 426 $sum = $lang['deleted']; 427 } 428 429 lock($id); 430 431 saveWikiText($id,$TEXT,$sum,$minor); 432 433 unlock($id); 434 435 // run the indexer if page wasn't indexed yet 436 if(!@file_exists(metaFN($id, '.indexed'))) { 437 // try to aquire a lock 438 $lock = $conf['lockdir'].'/_indexer.lock'; 439 while(!@mkdir($lock,$conf['dmode'])){ 440 usleep(50); 441 if(time()-@filemtime($lock) > 60*5){ 442 // looks like a stale lock - remove it 443 @rmdir($lock); 444 }else{ 445 return false; 446 } 447 } 448 if($conf['dperm']) chmod($lock, $conf['dperm']); 449 450 require_once(DOKU_INC.'inc/indexer.php'); 451 452 // do the work 453 idx_addPage($id); 454 455 // we're finished - save and free lock 456 io_saveFile(metaFN($id,'.indexed'),INDEXER_VERSION); 457 @rmdir($lock); 458 } 459 460 return 0; 461 } 462 463 /** 464 * Uploads a file to the wiki. 465 * 466 * Michael Klier <chi@chimeric.de> 467 */ 468 function putAttachment($id, $file, $params) { 469 global $conf; 470 global $lang; 471 472 $auth = auth_quickaclcheck(getNS($id).':*'); 473 if($auth >= AUTH_UPLOAD) { 474 if(!isset($id)) { 475 return new IXR_ERROR(1, 'Filename not given.'); 476 } 477 478 $ftmp = $conf['tmpdir'] . '/' . $id; 479 480 // save temporary file 481 @unlink($ftmp); 482 $buff = base64_decode($file); 483 io_saveFile($ftmp, $buff); 484 485 // get filename 486 list($iext, $imime,$dl) = mimetype($id); 487 $id = cleanID($id); 488 $fn = mediaFN($id); 489 490 // get filetype regexp 491 $types = array_keys(getMimeTypes()); 492 $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types); 493 $regex = join('|',$types); 494 495 // because a temp file was created already 496 if(preg_match('/\.('.$regex.')$/i',$fn)) { 497 //check for overwrite 498 $overwrite = @file_exists($fn); 499 if($overwrite && (!$params['ow'] || $auth < AUTH_DELETE)) { 500 return new IXR_ERROR(1, $lang['uploadexist'].'1'); 501 } 502 // check for valid content 503 @require_once(DOKU_INC.'inc/media.php'); 504 $ok = media_contentcheck($ftmp, $imime); 505 if($ok == -1) { 506 return new IXR_ERROR(1, sprintf($lang['uploadexist'].'2', ".$iext")); 507 } elseif($ok == -2) { 508 return new IXR_ERROR(1, $lang['uploadspam']); 509 } elseif($ok == -3) { 510 return new IXR_ERROR(1, $lang['uploadxss']); 511 } 512 513 // prepare event data 514 $data[0] = $ftmp; 515 $data[1] = $fn; 516 $data[2] = $id; 517 $data[3] = $imime; 518 $data[4] = $overwrite; 519 520 // trigger event 521 require_once(DOKU_INC.'inc/events.php'); 522 return trigger_event('MEDIA_UPLOAD_FINISH', $data, array($this, '_media_upload_action'), true); 523 524 } else { 525 return new IXR_ERROR(1, $lang['uploadwrong']); 526 } 527 } else { 528 return new IXR_ERROR(1, "You don't have permissions to upload files."); 529 } 530 } 531 532 /** 533 * Deletes a file from the wiki. 534 * 535 * @author Gina Haeussge <osd@foosel.net> 536 */ 537 function deleteAttachment($id){ 538 $auth = auth_quickaclcheck(getNS($id).':*'); 539 if($auth < AUTH_DELETE) return new IXR_ERROR(1, "You don't have permissions to delete files."); 540 global $conf; 541 global $lang; 542 543 // check for references if needed 544 $mediareferences = array(); 545 if($conf['refcheck']){ 546 require_once(DOKU_INC.'inc/fulltext.php'); 547 $mediareferences = ft_mediause($id,$conf['refshow']); 548 } 549 550 if(!count($mediareferences)){ 551 $file = mediaFN($id); 552 if(@unlink($file)){ 553 require_once(DOKU_INC.'inc/changelog.php'); 554 addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE); 555 io_sweepNS($id,'mediadir'); 556 return 0; 557 } 558 //something went wrong 559 return new IXR_ERROR(1, 'Could not delete file'); 560 } else { 561 return new IXR_ERROR(1, 'File is still referenced'); 562 } 563 } 564 565 /** 566 * Moves the temporary file to its final destination. 567 * 568 * Michael Klier <chi@chimeric.de> 569 */ 570 function _media_upload_action($data) { 571 global $conf; 572 573 if(is_array($data) && count($data)===5) { 574 io_createNamespace($data[2], 'media'); 575 if(rename($data[0], $data[1])) { 576 chmod($data[1], $conf['fmode']); 577 media_notify($data[2], $data[1], $data[3]); 578 // add a log entry to the media changelog 579 require_once(DOKU_INC.'inc/changelog.php'); 580 if ($data[4]) { 581 addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_EDIT); 582 } else { 583 addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_CREATE); 584 } 585 return $data[2]; 586 } else { 587 return new IXR_ERROR(1, 'Upload failed.'); 588 } 589 } else { 590 return new IXR_ERROR(1, 'Upload failed.'); 591 } 592 } 593 594 /** 595 * Returns the permissions of a given wiki page 596 */ 597 function aclCheck($id) { 598 return auth_quickaclcheck($id); 599 } 600 601 /** 602 * Lists all links contained in a wiki page 603 * 604 * @author Michael Klier <chi@chimeric.de> 605 */ 606 function listLinks($id) { 607 if(auth_quickaclcheck($id) < AUTH_READ){ 608 return new IXR_Error(1, 'You are not allowed to read this page'); 609 } 610 $links = array(); 611 612 // resolve page instructions 613 $ins = p_cached_instructions(wikiFN(cleanID($id))); 614 615 // instantiate new Renderer - needed for interwiki links 616 include(DOKU_INC.'inc/parser/xhtml.php'); 617 $Renderer = new Doku_Renderer_xhtml(); 618 $Renderer->interwiki = getInterwiki(); 619 620 // parse parse instructions 621 foreach($ins as $in) { 622 $link = array(); 623 switch($in[0]) { 624 case 'internallink': 625 $link['type'] = 'local'; 626 $link['page'] = $in[1][0]; 627 $link['href'] = wl($in[1][0]); 628 array_push($links,$link); 629 break; 630 case 'externallink': 631 $link['type'] = 'extern'; 632 $link['page'] = $in[1][0]; 633 $link['href'] = $in[1][0]; 634 array_push($links,$link); 635 break; 636 case 'interwikilink': 637 $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]); 638 $link['type'] = 'extern'; 639 $link['page'] = $url; 640 $link['href'] = $url; 641 array_push($links,$link); 642 break; 643 } 644 } 645 646 return ($links); 647 } 648 649 /** 650 * Returns a list of recent changes since give timestamp 651 * 652 * @author Michael Hamann <michael@content-space.de> 653 * @author Michael Klier <chi@chimeric.de> 654 */ 655 function getRecentChanges($timestamp) { 656 if(strlen($timestamp) != 10) 657 return new IXR_Error(20, 'The provided value is not a valid timestamp'); 658 659 require_once(DOKU_INC.'inc/changelog.php'); 660 require_once(DOKU_INC.'inc/pageutils.php'); 661 662 $recents = getRecentsSince($timestamp); 663 664 $changes = array(); 665 666 foreach ($recents as $recent) { 667 $change = array(); 668 $change['name'] = $recent['id']; 669 $change['lastModified'] = new IXR_Date($recent['date']); 670 $change['author'] = $recent['user']; 671 $change['version'] = $recent['date']; 672 $change['perms'] = $recent['perms']; 673 $change['size'] = @filesize(wikiFN($recent['id'])); 674 array_push($changes, $change); 675 } 676 677 if (!empty($changes)) { 678 return $changes; 679 } else { 680 // in case we still have nothing at this point 681 return new IXR_Error(30, 'There are no changes in the specified timeframe'); 682 } 683 } 684 685 /** 686 * Returns a list of recent media changes since give timestamp 687 * 688 * @author Michael Hamann <michael@content-space.de> 689 * @author Michael Klier <chi@chimeric.de> 690 */ 691 function getRecentMediaChanges($timestamp) { 692 if(strlen($timestamp) != 10) 693 return new IXR_Error(20, 'The provided value is not a valid timestamp'); 694 695 require_once(DOKU_INC.'inc/changelog.php'); 696 require_once(DOKU_INC.'inc/pageutils.php'); 697 698 $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES); 699 700 $changes = array(); 701 702 foreach ($recents as $recent) { 703 $change = array(); 704 $change['name'] = $recent['id']; 705 $change['lastModified'] = new IXR_Date($recent['date']); 706 $change['author'] = $recent['user']; 707 $change['version'] = $recent['date']; 708 $change['perms'] = $recent['perms']; 709 $change['size'] = @filesize(mediaFN($recent['id'])); 710 array_push($changes, $change); 711 } 712 713 if (!empty($changes)) { 714 return $changes; 715 } else { 716 // in case we still have nothing at this point 717 return new IXR_Error(30, 'There are no changes in the specified timeframe'); 718 } 719 } 720 721 /** 722 * Returns a list of available revisions of a given wiki page 723 * 724 * @author Michael Klier <chi@chimeric.de> 725 */ 726 function pageVersions($id, $first) { 727 global $conf; 728 729 $versions = array(); 730 731 if(empty($id)) 732 return new IXR_Error(1, 'Empty page ID'); 733 734 require_once(DOKU_INC.'inc/changelog.php'); 735 736 $revisions = getRevisions($id, $first, $conf['recent']+1); 737 738 if(count($revisions)==0 && $first!=0) { 739 $first=0; 740 $revisions = getRevisions($id, $first, $conf['recent']+1); 741 } 742 743 if(count($revisions)>0 && $first==0) { 744 array_unshift($revisions, ''); // include current revision 745 array_pop($revisions); // remove extra log entry 746 } 747 748 $hasNext = false; 749 if(count($revisions)>$conf['recent']) { 750 $hasNext = true; 751 array_pop($revisions); // remove extra log entry 752 } 753 754 if(!empty($revisions)) { 755 foreach($revisions as $rev) { 756 $file = wikiFN($id,$rev); 757 $time = @filemtime($file); 758 // we check if the page actually exists, if this is not the 759 // case this can lead to less pages being returned than 760 // specified via $conf['recent'] 761 if($time){ 762 $info = getRevisionInfo($id, $time, 1024); 763 if(!empty($info)) { 764 $data['user'] = $info['user']; 765 $data['ip'] = $info['ip']; 766 $data['type'] = $info['type']; 767 $data['sum'] = $info['sum']; 768 $data['modified'] = new IXR_Date($info['date']); 769 $data['version'] = $info['date']; 770 array_push($versions, $data); 771 } 772 } 773 } 774 return $versions; 775 } else { 776 return array(); 777 } 778 } 779 780 /** 781 * The version of Wiki RPC API supported 782 */ 783 function wiki_RPCVersion(){ 784 return 2; 785 } 786 787 788 /** 789 * Locks or unlocks a given batch of pages 790 * 791 * Give an associative array with two keys: lock and unlock. Both should contain a 792 * list of pages to lock or unlock 793 * 794 * Returns an associative array with the keys locked, lockfail, unlocked and 795 * unlockfail, each containing lists of pages. 796 */ 797 function setLocks($set){ 798 $locked = array(); 799 $lockfail = array(); 800 $unlocked = array(); 801 $unlockfail = array(); 802 803 foreach((array) $set['lock'] as $id){ 804 if(checklock($id)){ 805 $lockfail[] = $id; 806 }else{ 807 lock($id); 808 $locked[] = $id; 809 } 810 } 811 812 foreach((array) $set['unlock'] as $id){ 813 if(unlock($id)){ 814 $unlocked[] = $id; 815 }else{ 816 $unlockfail[] = $id; 817 } 818 } 819 820 return array( 821 'locked' => $locked, 822 'lockfail' => $lockfail, 823 'unlocked' => $unlocked, 824 'unlockfail' => $unlockfail, 825 ); 826 } 827 828} 829 830$server = new dokuwiki_xmlrpc_server(); 831 832// vim:ts=4:sw=4:et:enc=utf-8: 833