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