_profileLoad(); $this->profno = preg_replace('/[^0-9]+/','',$_REQUEST['no']); } function _connect(){ if(!is_null($this->client)) return true; if ( isset($this->profiles[$this->profno]['timeout']) ){ $timeout = (int) $this->profiles[$this->profno]['timeout']; } else { $timeout = $this->defaultTimeout; } if(class_exists('IXR_Client')) { $this->client = new IXR_Client($this->profiles[$this->profno]['server'], false, 80, $timeout); } else { $this->client = new dokuwiki\Remote\IXR\Client($this->profiles[$this->profno]['server']); $this->client->timeout = $timeout; } // do the login if($this->profiles[$this->profno]['user']){ $ok = $this->client->query('dokuwiki.login', $this->profiles[$this->profno]['user'], $this->profiles[$this->profno]['pass'] ); if(!$ok){ msg($this->getLang('xmlerr').' '.hsc($this->client->getErrorMessage()),-1); $this->client = null; return false; } if(!$this->client->getResponse()){ msg($this->getLang('loginerr'),-1); $this->client = null; return false; } } $ok = $this->client->query('dokuwiki.getXMLRPCAPIVersion'); if(!$ok){ msg($this->getLang('xmlerr').' '.hsc($this->client->getErrorMessage()),-1); $this->client = null; return false; } $this->apiversion = (int) $this->client->getResponse(); if($this->apiversion < 1){ msg($this->getLang('versionerr'),-1); $this->client = null; return false; } return true; } /** * return sort order for position in admin menu */ function getMenuSort() { return 1020; } /** * handle profile saving/deleting */ function handle() { if(isset($_REQUEST['prf']) && is_array($_REQUEST['prf'])){ if(isset($_REQUEST['sync__delete']) && $this->profno !== ''){ // delete profile unset($this->profiles[$this->profno]); $this->profiles = array_values($this->profiles); //reindex $this->profno = ''; }else{ // add/edit profile if($this->profno === '') $this->profno = count($this->profiles); if ( !isset($_REQUEST['prf']['timeout']) || !is_numeric($_REQUEST['prf']['timeout']) ){ $_REQUEST['prf']['timeout'] = $this->defaultTimeout; } $this->profiles[$this->profno] = $_REQUEST['prf']; } $this->_profileSave(); // reset the client $this->client = null; } } /** * output appropriate html */ function html() { if(($_POST['sync_pages'] || $_POST['sync_media']) && $this->profno!==''){ // do the sync echo $this->locale_xhtml('sync'); //show progressbar echo '
'.NL; echo ''.NL; echo '
'.NL; flush(); ob_flush(); echo ''; //hide progressbar echo ''.NL; flush(); ob_flush(); echo '

'.$this->getLang('syncdone').'

'; }elseif($_REQUEST['startsync'] && $this->profno!==''){ // get sync list list($lnow,$rnow) = $this->_getTimes(); $pages = array(); $media = array(); if($rnow){ if($this->profiles[$this->profno]['type'] == 0 || $this->profiles[$this->profno]['type'] == 1){ $pages = $this->_getSyncList('pages'); } if(($this->profiles[$this->profno]['type'] == 0 || $this->profiles[$this->profno]['type'] == 2) && $pages !== false ){ $media = $this->_getSyncList('media'); } } if ( $pages === false || $media === false ){ return; } if(count($pages) || count($media)){ $this->_directionFormStart($lnow,$rnow); if(count($pages)) $this->_directionForm('pages',$pages); if(count($media)) $this->_directionForm('media',$media); $this->_directionFormEnd(); }else{ echo $this->locale_xhtml('nochange'); } }else{ echo $this->locale_xhtml('intro'); echo '
'; $this->_profilelist($this->profno); if($this->profno !=='' ){ echo '
'; $this->_profileView($this->profno); } echo '
'; echo '
'; $this->_profileform($this->profno); echo '
'; } } /** * Load profiles from serialized storage */ function _profileLoad(){ global $conf; $profiles = $conf['metadir'].'/sync.profiles'; if(file_exists($profiles)){ $this->profiles = unserialize(io_readFile($profiles,false)); } } /** * Save profiles to serialized storage */ function _profileSave(){ global $conf; $profiles = $conf['metadir'].'/sync.profiles'; io_saveFile($profiles,serialize($this->profiles)); } /** * Check connection for choosen profile and display last sync date. */ function _profileView(){ if(!$this->_connect()) return false; global $conf; $no = $this->profno; $ok = $this->client->query('dokuwiki.getVersion'); $version = ''; if($ok) $version = $this->client->getResponse(); echo '
'; echo ''; echo '
'.$this->getLang('syncstart').''; if($version){ echo '

'.$this->getLang('remotever').' '.hsc($version).'

'; if($this->profiles[$no]['ltime']){ echo '

'.$this->getLang('lastsync').' '.strftime($conf['dformat'],$this->profiles[$no]['ltime']).'

'; }else{ echo '

'.$this->getLang('neversync').'

'; } echo ''; }else{ echo '

'.$this->getLang('noconnect').'
'.hsc($this->client->getErrorMessage()).'

'; } echo '
'; echo '
'; } /** * Dropdown list of available sync profiles */ function _profilelist($no=''){ echo '
'; echo '
'.$this->getLang('profile').''; echo ''; echo ''; echo '
'; echo '
'; } /** * Form to edit or create a sync profile */ function _profileform($no=''){ echo '
'; echo '
'; if($no !== ''){ echo $this->getLang('edit'); }else{ echo $this->getLang('create'); } echo ''; echo ''; echo ' '; echo ''; echo 'http://example.com/dokuwiki/lib/exe/xmlrpc.php'; echo ' '; echo ''; echo ' '; echo ''; echo ' '; echo ''; echo ' '; echo ''; echo ''; echo ''; echo ''.$this->getLang('type').''; echo '
'; echo 'profiles[$no]['type'] == 0)?'checked="checked"':'').'/>'; echo ' '; echo 'profiles[$no]['type'] == 1)?'checked="checked"':'').'/>'; echo ' '; echo 'profiles[$no]['type'] == 2)?'checked="checked"':'').'/>'; echo ' '; echo '
'; echo '
'; echo ''; if($no !== '' && $this->profiles[$no]['ltime']){ echo ''.$this->getLang('changewarn').''; } echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo '
'; } /** * Lock files that will be modified on either side. * * Lock fails are printed and removed from synclist * * @returns list of locked files */ function _lockFiles(&$synclist){ if(!$this->_connect()) return array(); // lock the files $lock = array(); foreach((array) $synclist as $id => $dir){ if($dir == 0) continue; if(checklock($id)){ $this->_listOut($this->getLang('lockfail').' '.hsc($id),'error'); unset($synclist[$id]); }else{ lock($id); // lock local $lock[] = $id; } } // lock remote files $ok = $this->client->query('dokuwiki.setLocks',array('lock'=>$lock,'unlock'=>array())); if(!$ok){ $this->_listOut('failed RPC communication'); $synclist = array(); return array(); } $data = $this->client->getResponse(); foreach((array) $data['lockfail'] as $id){ $this->_listOut($this->getLang('lockfail').' '.hsc($id),'error'); unset($synclist[$id]); } return $lock; } /** * Print a message as list item using the given class */ function _listOut($msg,$class='ok'){ echo '
  • '; echo hsc($msg); echo "
  • \n"; flush(); ob_flush(); } /** * Execute the sync action and print the results */ function _sync(&$synclist,$type){ if(!$this->_connect()) return false; $no = $this->profno; $sum = $_REQUEST['sum']; if($type == 'pages') $lock = $this->_lockfiles($synclist); // do the sync foreach((array) $synclist as $id => $dir){ @set_time_limit(30); if($dir == 0){ $this->_listOut($this->getLang('skipped').' '.$id,'skipped'); continue; } if($dir == -2){ //delete local if($type == 'pages'){ saveWikiText($id,'',$sum,false); $this->_listOut($this->getLang('localdelok').' '.$id,'del_okay'); }else{ if(unlink(mediaFN($id))){ $this->_listOut($this->getLang('localdelok').' '.$id,'del_okay'); }else{ $this->_listOut($this->getLang('localdelfail').' '.$id,'del_fail'); } } continue; } if($dir == -1){ //pull if($type == 'pages'){ $ok = $this->client->query('wiki.getPage',$id); }else{ $ok = $this->client->query('wiki.getAttachment',$id); } if(!$ok){ $this->_listOut($this->getLang('pullfail').' '.$id.' '. $this->client->getErrorMessage(),'pull_fail'); continue; } $data = $this->client->getResponse(); if($type == 'pages'){ saveWikiText($id,$data,$sum,false); idx_addPage($id); }else{ if($this->apiversion < 7){ $data = base64_decode($data); } io_saveFile(mediaFN($id),$data); } $this->_listOut($this->getLang('pullok').' '.$id,'pull_okay'); continue; } if($dir == 1){ // push if($type == 'pages'){ $data = rawWiki($id); $ok = $this->client->query('wiki.putPage',$id,$data,array('sum'=>$sum)); }else{ $data = io_readFile(mediaFN($id),false); if($this->apiversion < 6){ $data = base64_encode($data); }else{ $data = new IXR_Base64($data); } $ok = $this->client->query('wiki.putAttachment',$id,$data,array('ow'=>true)); } if(!$ok){ $this->_listOut($this->getLang('pushfail').' '.$id.' '. $this->client->getErrorMessage(),'push_fail'); continue; } $this->_listOut($this->getLang('pushok').' '.$id,'push_okay'); continue; } if($dir == 2){ // remote delete if($type == 'pages'){ $ok = $this->client->query('wiki.putPage',$id,'',array('sum'=>$sum)); }else{ $ok = $this->client->query('wiki.deleteAttachment',$id); } if(!$ok){ $this->_listOut($this->getLang('remotedelfail').' '.$id.' '. $this->client->getErrorMessage(),'del_fail'); continue; } $this->_listOut($this->getLang('remotedelok').' '.$id,'del_okay'); continue; } } // unlock if($type == 'pages'){ foreach((array) $synclist as $id => $dir){ unlock($id); } $this->client->query('dokuwiki.setLocks',array('lock'=>array(),'unlock'=>$lock)); } } /** * Save synctimes */ function _saveSyncTimes($ltime,$rtime){ $no = $this->profno; list($letime,$retime) = $this->_getTimes(); $this->profiles[$no]['ltime'] = $ltime; $this->profiles[$no]['rtime'] = $rtime; $this->profiles[$no]['letime'] = $letime; $this->profiles[$no]['retime'] = $retime; $this->_profileSave(); } /** * Open the sync direction form and initialize the table */ function _directionFormStart($lnow,$rnow){ $no = $this->profno; echo $this->locale_xhtml('list'); echo '
    '; echo ''; echo ''; echo ''; echo ''; echo ''; } /** * Close the direction form and table */ function _directionFormEnd(){ global $lang; echo '
    '.$this->getLang('file').' '.$this->getLang('local').' > = < '.$this->getLang('remote').' '.$this->getLang('diff').'
    '; echo ' '; echo ''; echo ''; echo '
    '; } /** * Print a list of changed files and ask for the sync direction * * Tries to be clever about suggesting the direction */ function _directionForm($type,&$synclist){ global $conf; global $lang; $no = $this->profno; $ltime = (int) $this->profiles[$no]['ltime']; $rtime = (int) $this->profiles[$no]['rtime']; $letime = (int) $this->profiles[$no]['letime']; $retime = (int) $this->profiles[$no]['retime']; foreach($synclist as $id => $item){ // check direction $dir = 0; if($ltime && $rtime){ // synced before if($item['remote']['mtime'] > $rtime && $item['local']['mtime'] <= $letime){ $dir = -1; } if($item['remote']['mtime'] <= $retime && $item['local']['mtime'] > $ltime){ $dir = 1; } }else{ // never synced if(!$item['local']['mtime'] && $item['remote']['mtime']){ $dir = -1; } if($item['local']['mtime'] && !$item['remote']['mtime']){ $dir = 1; } } echo ''; echo ''.hsc($id).''; echo ''; if(!isset($item['local'])){ echo '—'; }else{ echo '
    '.strftime($conf['dformat'],$item['local']['mtime']).'
    '; echo '
    ('.$item['local']['size'].' bytes)
    '; } echo ''; echo ''; if(!isset($item['local'])){ echo ''; }else{ echo ''; } echo ''; echo ''; echo ''; echo ''; echo ''; if(!isset($item['remote'])){ echo ''; }else{ echo ''; } echo ''; echo ''; if(!isset($item['remote'])){ echo '—'; }else{ echo '
    '.strftime($conf['dformat'],$item['remote']['mtime']).'
    '; echo '
    ('.$item['remote']['size'].' bytes)
    '; } echo ''; echo ''; if($type == 'pages'){ echo ''.$this->getLang('diff').''; } echo ''; echo ''; } } /** * Get the local and remote time */ function _getTimes(){ if(!$this->_connect()) return false; // get remote time $ok = $this->client->query('dokuwiki.getTime'); if(!$ok){ msg('Failed to fetch remote time. '. $this->client->getErrorMessage(),-1); return false; } $rtime = $this->client->getResponse(); $ltime = time(); return array($ltime,$rtime); } /** * Get a list of changed files */ function _getSyncList($type='pages'){ if(!$this->_connect()) return array(); global $conf; $no = $this->profno; $list = array(); $ns = $this->profiles[$no]['ns']; // get remote file list if($type == 'pages'){ $ok = $this->client->query('dokuwiki.getPagelist',$ns, array('depth' => (int) $this->profiles[$no]['depth'], 'hash' => true)); }else{ $ok = $this->client->query('wiki.getAttachments',$ns, array('depth' => (int) $this->profiles[$no]['depth'], 'hash' => true)); } if(!$ok){ msg('Failed to fetch remote file list. '. $this->client->getErrorMessage(),-1); return false; } $remote = $this->client->getResponse(); // put into synclist foreach($remote as $item){ $list[$item['id']]['remote'] = $item; unset($list[$item['id']]['remote']['id']); } unset($remote); // get local file list $local = array(); $dir = utf8_encodeFN(str_replace(':', '/', $ns)); require_once(DOKU_INC.'inc/search.php'); if($type == 'pages'){ search($local, $conf['datadir'], 'search_allpages', array('depth' => (int) $this->profiles[$no]['depth'], 'hash' => true), $dir); }else{ search($local, $conf['mediadir'], 'search_media', array('depth' => (int) $this->profiles[$no]['depth'], 'hash' => true), $dir); } // put into synclist foreach($local as $item){ // skip identical files if($list[$item['id']]['remote']['hash'] == $item['hash']){ unset($list[$item['id']]); continue; } $list[$item['id']]['local'] = $item; unset($list[$item['id']]['local']['id']); } unset($local); ksort($list); return $list; } /** * show diff between the local and remote versions of the page */ function _diff($id){ if(!$this->_connect()) return false; $no = $this->profno; $ok = $this->client->query('wiki.getPage',$id); if(!$ok){ echo $this->getLang('pullfail').' '.hsc($id).' '; echo hsc($this->client->getErrorMessage()); die(); } $remote = $this->client->getResponse(); $local = rawWiki($id); $df = new Diff(explode("\n",htmlspecialchars($local)), explode("\n",htmlspecialchars($remote))); $tdf = new TableDiffFormatter(); echo ''; echo ''; echo ''; echo ''; echo ''; echo $tdf->format($df); echo '
    '.$this->getLang('local').''.$this->getLang('remote').'
    '; } } //Setup VIM: ex: et ts=4 enc=utf-8 :