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