mirror of
https://github.com/splitbrain/dokuwiki-plugin-sync.git
synced 2025-07-21 11:32:16 +00:00
735 lines
27 KiB
PHP
735 lines
27 KiB
PHP
<?php
|
|
|
|
/**
|
|
* All DokuWiki plugins to extend the admin function
|
|
* need to inherit from this class
|
|
*/
|
|
class admin_plugin_sync extends DokuWiki_Admin_Plugin {
|
|
|
|
protected $profiles = array();
|
|
protected $profno = '';
|
|
protected $client = null;
|
|
protected $apiversion = 0;
|
|
protected $defaultTimeout = 15;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
function __construct(){
|
|
$this->_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 '<div class="centeralign" id="dw__loading">'.NL;
|
|
echo '<script type="text/javascript" charset="utf-8"><!--//--><![CDATA[//><!--'.NL;
|
|
echo 'showLoadBar();'.NL;
|
|
echo '//--><!]]></script>'.NL;
|
|
echo '<br /></div>'.NL;
|
|
flush();
|
|
ob_flush();
|
|
|
|
echo '<ul class="sync">';
|
|
|
|
if($_POST['sync_pages']){
|
|
$this->_sync($_POST['sync_pages'],'pages');
|
|
}
|
|
if($_POST['sync_media']){
|
|
$this->_sync($_POST['sync_media'],'media');
|
|
}
|
|
$this->_saveSyncTimes((int) $_POST['lnow'],
|
|
(int) $_POST['rnow']);
|
|
|
|
echo '</ul>';
|
|
|
|
//hide progressbar
|
|
echo '<script type="text/javascript" charset="utf-8"><!--//--><![CDATA[//><!--'.NL;
|
|
echo 'hideLoadBar("dw__loading");'.NL;
|
|
echo '//--><!]]></script>'.NL;
|
|
flush();
|
|
ob_flush();
|
|
|
|
|
|
echo '<p>'.$this->getLang('syncdone').'</p>';
|
|
}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 '<div class="sync_left">';
|
|
$this->_profilelist($this->profno);
|
|
if($this->profno !=='' ){
|
|
echo '<br />';
|
|
$this->_profileView($this->profno);
|
|
}
|
|
echo '</div>';
|
|
echo '<div class="sync_right">';
|
|
$this->_profileform($this->profno);
|
|
echo '</div>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 '<form action="" method="post">';
|
|
echo '<input type="hidden" name="no" value="'.hsc($no).'" />';
|
|
echo '<fieldset><legend>'.$this->getLang('syncstart').'</legend>';
|
|
if($version){
|
|
echo '<p>'.$this->getLang('remotever').' '.hsc($version).'</p>';
|
|
if($this->profiles[$no]['ltime']){
|
|
echo '<p>'.$this->getLang('lastsync').' '.strftime($conf['dformat'],$this->profiles[$no]['ltime']).'</p>';
|
|
}else{
|
|
echo '<p>'.$this->getLang('neversync').'</p>';
|
|
}
|
|
echo '<input name="startsync" type="submit" value="'.$this->getLang('syncstart').'" class="button" />';
|
|
}else{
|
|
echo '<p class="error">'.$this->getLang('noconnect').'<br />'.hsc($this->client->getErrorMessage()).'</p>';
|
|
}
|
|
echo '</fieldset>';
|
|
echo '</form>';
|
|
}
|
|
|
|
/**
|
|
* Dropdown list of available sync profiles
|
|
*/
|
|
function _profilelist($no=''){
|
|
echo '<form action="" method="post">';
|
|
echo '<fieldset><legend>'.$this->getLang('profile').'</legend>';
|
|
echo '<select name="no" class="edit">';
|
|
echo ' <option value="">'.$this->getLang('newprofile').'</option>';
|
|
foreach($this->profiles as $pno => $opts){
|
|
$srv = parse_url($opts['server']);
|
|
|
|
echo '<option value="'.hsc($pno).'" '.(($no!=='' && $pno == $no)?'selected="selected"':'').'>';
|
|
echo ($pno+1).'. ';
|
|
if($opts['user']) echo hsc($opts['user']).'@';
|
|
echo hsc($srv['host']);
|
|
if($opts['ns']) echo ':'.hsc($opts['ns']);
|
|
echo '</option>';
|
|
}
|
|
echo '</select>';
|
|
echo '<input type="submit" value="'.$this->getLang('select').'" class="button" />';
|
|
echo '</fieldset>';
|
|
echo '</form>';
|
|
}
|
|
|
|
/**
|
|
* Form to edit or create a sync profile
|
|
*/
|
|
function _profileform($no=''){
|
|
echo '<form action="" method="post" class="sync_profile">';
|
|
echo '<fieldset><legend>';
|
|
if($no !== ''){
|
|
echo $this->getLang('edit');
|
|
}else{
|
|
echo $this->getLang('create');
|
|
}
|
|
echo '</legend>';
|
|
|
|
echo '<input type="hidden" name="no" value="'.hsc($no).'" />';
|
|
|
|
echo '<label for="sync__server">'.$this->getLang('server').'</label> ';
|
|
echo '<input type="text" name="prf[server]" id="sync__server" class="edit" value="'.hsc($this->profiles[$no]['server']).'" />';
|
|
echo '<samp>http://example.com/dokuwiki/lib/exe/xmlrpc.php</samp>';
|
|
|
|
echo '<label for="sync__ns">'.$this->getLang('ns').'</label> ';
|
|
echo '<input type="text" name="prf[ns]" id="sync__ns" class="edit" value="'.hsc($this->profiles[$no]['ns']).'" />';
|
|
|
|
echo '<label for="sync__depth">'.$this->getLang('depth').'</label> ';
|
|
echo '<select name="prf[depth]" id="sync__depth" class="edit">';
|
|
echo '<option value="0" '.(($this->profiles[$no]['depth']==0)?'selected="selected"':'').'>'.$this->getLang('level0').'</option>';
|
|
echo '<option value="1" '.(($this->profiles[$no]['depth']==1)?'selected="selected"':'').'>'.$this->getLang('level1').'</option>';
|
|
echo '<option value="2" '.(($this->profiles[$no]['depth']==2)?'selected="selected"':'').'>'.$this->getLang('level2').'</option>';
|
|
echo '<option value="3" '.(($this->profiles[$no]['depth']==3)?'selected="selected"':'').'>'.$this->getLang('level3').'</option>';
|
|
echo '</select>';
|
|
|
|
|
|
echo '<label for="sync__user">'.$this->getLang('user').'</label> ';
|
|
echo '<input type="text" name="prf[user]" id="sync__user" class="edit" value="'.hsc($this->profiles[$no]['user']).'" />';
|
|
|
|
echo '<label for="sync__pass">'.$this->getLang('pass').'</label> ';
|
|
echo '<input type="password" name="prf[pass]" id="sync__pass" class="edit" value="'.hsc($this->profiles[$no]['pass']).'" />';
|
|
|
|
echo '<label for="sync__timeout">'.$this->getLang('timeout').'</label>';
|
|
echo '<input type="number" name="prf[timeout]" id="sync__timeout" class="edit" value="'.hsc($this->profiles[$no]['timeout']).'" />';
|
|
|
|
echo '<span>'.$this->getLang('type').'</span>';
|
|
|
|
echo '<div class="type">';
|
|
echo '<input type="radio" name="prf[type]" id="sync__type0" value="0" '.(($this->profiles[$no]['type'] == 0)?'checked="checked"':'').'/>';
|
|
echo '<label for="sync__type0">'.$this->getLang('type0').'</label> ';
|
|
|
|
echo '<input type="radio" name="prf[type]" id="sync__type1" value="1" '.(($this->profiles[$no]['type'] == 1)?'checked="checked"':'').'/>';
|
|
echo '<label for="sync__type1">'.$this->getLang('type1').'</label> ';
|
|
|
|
echo '<input type="radio" name="prf[type]" id="sync__type2" value="2" '.(($this->profiles[$no]['type'] == 2)?'checked="checked"':'').'/>';
|
|
echo '<label for="sync__type2">'.$this->getLang('type2').'</label> ';
|
|
echo '</div>';
|
|
|
|
|
|
echo '<div class="submit">';
|
|
echo '<input type="submit" value="'.$this->getLang('save').'" class="button" />';
|
|
if($no !== '' && $this->profiles[$no]['ltime']){
|
|
echo '<small>'.$this->getLang('changewarn').'</small>';
|
|
}
|
|
echo '</div>';
|
|
|
|
echo '<div class="submit">';
|
|
echo '<input name="sync__delete" type="submit" value="'.$this->getLang('delete').'" class="button" />';
|
|
echo '</div>';
|
|
|
|
echo '</fieldset>';
|
|
echo '</form>';
|
|
}
|
|
|
|
/**
|
|
* 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 '<li class="'.hsc($class).'"><div class="li">';
|
|
echo hsc($msg);
|
|
echo "</div></li>\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 '<form action="" method="post">';
|
|
echo '<table class="inline" id="sync__direction__table">';
|
|
echo '<input type="hidden" name="lnow" value="'.$lnow.'" />';
|
|
echo '<input type="hidden" name="rnow" value="'.$rnow.'" />';
|
|
echo '<input type="hidden" name="no" value="'.$no.'" />';
|
|
echo '<tr>
|
|
<th class="sync__file">'.$this->getLang('file').'</th>
|
|
<th class="sync__local">'.$this->getLang('local').'</th>
|
|
<th class="sync__push" id="sync__push">></th>
|
|
<th class="sync__skip" id="sync__skip">=</th>
|
|
<th class="sync__pull" id="sync__pull"><</th>
|
|
<th class="sync__remote">'.$this->getLang('remote').'</th>
|
|
<th class="sync__diff">'.$this->getLang('diff').'</th>
|
|
</tr>';
|
|
}
|
|
|
|
/**
|
|
* Close the direction form and table
|
|
*/
|
|
function _directionFormEnd(){
|
|
global $lang;
|
|
echo '</table>';
|
|
echo '<label for="the__summary">'.$lang['summary'].'</label> ';
|
|
echo '<input type="text" name="sum" id="the__summary" value="" class="edit" />';
|
|
echo '<input type="submit" value="'.$this->getLang('syncstart').'" class="button" />';
|
|
echo '</form>';
|
|
}
|
|
|
|
/**
|
|
* 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 '<tr>';
|
|
|
|
echo '<td class="sync__file">'.hsc($id).'</td>';
|
|
echo '<td class="sync__local">';
|
|
if(!isset($item['local'])){
|
|
echo '—';
|
|
}else{
|
|
echo '<div>'.strftime($conf['dformat'],$item['local']['mtime']).'</div>';
|
|
echo ' <div>('.$item['local']['size'].' bytes)</div>';
|
|
}
|
|
echo '</td>';
|
|
|
|
echo '<td class="sync__push">';
|
|
if(!isset($item['local'])){
|
|
echo '<input type="radio" name="sync_'.$type.'['.hsc($id).']" value="2" class="syncpush" title="'.$this->getLang('pushdel').'" '.(($dir == 2)?'checked="checked"':'').' />';
|
|
}else{
|
|
echo '<input type="radio" name="sync_'.$type.'['.hsc($id).']" value="1" class="syncpush" title="'.$this->getLang('push').'" '.(($dir == 1)?'checked="checked"':'').' />';
|
|
}
|
|
echo '</td>';
|
|
echo '<td class="sync__skip">';
|
|
echo '<input type="radio" name="sync_'.$type.'['.hsc($id).']" value="0" class="syncskip" title="'.$this->getLang('keep').'" '.(($dir == 0)?'checked="checked"':'').' />';
|
|
echo '</td>';
|
|
echo '<td class="sync__pull">';
|
|
if(!isset($item['remote'])){
|
|
echo '<input type="radio" name="sync_'.$type.'['.hsc($id).']" value="-2" class="syncpull" title="'.$this->getLang('pulldel').'" '.(($dir == -2)?'checked="checked"':'').' />';
|
|
}else{
|
|
echo '<input type="radio" name="sync_'.$type.'['.hsc($id).']" value="-1" class="syncpull" title="'.$this->getLang('pull').'" '.(($dir == -1)?'checked="checked"':'').' />';
|
|
}
|
|
echo '</td>';
|
|
|
|
echo '<td class="sync__remote">';
|
|
if(!isset($item['remote'])){
|
|
echo '—';
|
|
}else{
|
|
echo '<div>'.strftime($conf['dformat'],$item['remote']['mtime']).'</div>';
|
|
echo ' <div>('.$item['remote']['size'].' bytes)</div>';
|
|
}
|
|
echo '</td>';
|
|
|
|
echo '<td class="sync__diff">';
|
|
if($type == 'pages'){
|
|
echo '<a href="'.DOKU_BASE.'lib/plugins/sync/diff.php?id='.$id.'&no='.$no.'" target="_blank" class="sync_popup">'.$this->getLang('diff').'</a>';
|
|
}
|
|
echo '</td>';
|
|
|
|
echo '</tr>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 '<table class="diff">';
|
|
echo '<tr>';
|
|
echo '<th colspan="2">'.$this->getLang('local').'</th>';
|
|
echo '<th colspan="2">'.$this->getLang('remote').'</th>';
|
|
echo '</tr>';
|
|
echo $tdf->format($df);
|
|
echo '</table>';
|
|
}
|
|
}
|
|
//Setup VIM: ex: et ts=4 enc=utf-8 :
|