|
|
|
@ -22,41 +22,26 @@ class Storage { |
|
|
|
|
// - files_versionsfolder |
|
|
|
|
// - files_versionsblacklist |
|
|
|
|
// - files_versionsmaxfilesize |
|
|
|
|
// - files_versionsinterval |
|
|
|
|
// - files_versionmaxversions |
|
|
|
|
// - files_versionsinterval |
|
|
|
|
// - files_versionmaxversions |
|
|
|
|
// |
|
|
|
|
// todo: |
|
|
|
|
// - finish porting to OC_FilesystemView to enable network transparency |
|
|
|
|
// - add transparent compression. first test if it´s worth it. |
|
|
|
|
|
|
|
|
|
const DEFAULTENABLED=true; |
|
|
|
|
const DEFAULTFOLDER='versions'; |
|
|
|
|
const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp'; |
|
|
|
|
const DEFAULTMAXFILESIZE=1048576; // 10MB |
|
|
|
|
const DEFAULTENABLED=true; |
|
|
|
|
const DEFAULTBLACKLIST='avi mp3 mpg mp4 ctmp'; |
|
|
|
|
const DEFAULTMAXFILESIZE=1048576; // 10MB |
|
|
|
|
const DEFAULTMININTERVAL=60; // 1 min |
|
|
|
|
const DEFAULTMAXVERSIONS=50; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private $view; |
|
|
|
|
|
|
|
|
|
function __construct( $view ) { |
|
|
|
|
|
|
|
|
|
$this->view = $view; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* init the versioning and create the versions folder. |
|
|
|
|
*/ |
|
|
|
|
public static function init() { |
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
// create versions folder |
|
|
|
|
$foldername=\OCP\Config::getSystemValue('datadirectory').'/'. \OCP\USER::getUser() .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
if(!is_dir($foldername)){ |
|
|
|
|
mkdir($foldername); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
function __construct() { |
|
|
|
|
|
|
|
|
|
$this->view = \OCP\Files::getStorage('files_versions'); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* listen to write event. |
|
|
|
@ -75,11 +60,11 @@ class Storage { |
|
|
|
|
*/ |
|
|
|
|
public function store($filename) { |
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$files_view = \OCP\Files::getStorage("files"); |
|
|
|
|
$users_view = \OCP\Files::getStorage("files_versions"); |
|
|
|
|
$users_view->chroot(\OCP\User::getUser().'/'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { |
|
|
|
|
$pos = strpos($source, '/files', 1); |
|
|
|
|
$uid = substr($source, 1, $pos - 1); |
|
|
|
@ -87,15 +72,14 @@ class Storage { |
|
|
|
|
} else { |
|
|
|
|
$uid = \OCP\User::getUser(); |
|
|
|
|
} |
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
$filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files'; |
|
|
|
|
Storage::init(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory') . $this->view->getAbsolutePath(''); |
|
|
|
|
|
|
|
|
|
//check if source file already exist as version to avoid recursions. |
|
|
|
|
if ($users_view->file_exists($filename)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if filename is a directory |
|
|
|
|
if($files_view->is_dir($filename)){ |
|
|
|
|
return false; |
|
|
|
@ -110,7 +94,7 @@ class Storage { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check filesize |
|
|
|
|
if($files_view->filesize($filename)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){ |
|
|
|
|
return false; |
|
|
|
@ -129,12 +113,12 @@ class Storage { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// create all parent folders |
|
|
|
|
$info=pathinfo($filename); |
|
|
|
|
if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true); |
|
|
|
|
$info=pathinfo($filename); |
|
|
|
|
if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true); |
|
|
|
|
|
|
|
|
|
// store a new version of a file |
|
|
|
|
@$users_view->copy('files'.$filename, 'versions'.$filename.'.v'.time()); |
|
|
|
|
|
|
|
|
|
@$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.time()); |
|
|
|
|
|
|
|
|
|
// expire old revisions if necessary |
|
|
|
|
Storage::expire($filename); |
|
|
|
|
} |
|
|
|
@ -145,11 +129,11 @@ class Storage { |
|
|
|
|
* rollback to an old version of a file. |
|
|
|
|
*/ |
|
|
|
|
public static function rollback($filename,$revision) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
$users_view = \OCP\Files::getStorage("files_versions"); |
|
|
|
|
$users_view->chroot(\OCP\User::getUser().'/'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { |
|
|
|
|
$pos = strpos($source, '/files', 1); |
|
|
|
|
$uid = substr($source, 1, $pos - 1); |
|
|
|
@ -157,23 +141,20 @@ class Storage { |
|
|
|
|
} else { |
|
|
|
|
$uid = \OCP\User::getUser(); |
|
|
|
|
} |
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'.$uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
|
|
|
|
|
$filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// rollback |
|
|
|
|
if( @$users_view->copy('versions'.$filename.'.v'.$revision, 'files'.$filename) ) { |
|
|
|
|
|
|
|
|
|
if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}else{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -181,18 +162,17 @@ class Storage { |
|
|
|
|
*/ |
|
|
|
|
public static function isversioned($filename) { |
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
$versions_fileview = \OCP\Files::getStorage("files_versions"); |
|
|
|
|
if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { |
|
|
|
|
$pos = strpos($source, '/files', 1); |
|
|
|
|
$uid = substr($source, 1, $pos - 1); |
|
|
|
|
$filename = substr($source, $pos + 6); |
|
|
|
|
} else { |
|
|
|
|
$uid = \OCP\User::getUser(); |
|
|
|
|
} |
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
|
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); |
|
|
|
|
|
|
|
|
|
// check for old versions |
|
|
|
|
$matches=glob($versionsFolderName.'/'.$filename.'.v*'); |
|
|
|
|
if(count($matches)>1){ |
|
|
|
|
$matches=glob($versionsFolderName.$filename.'.v*'); |
|
|
|
|
if(count($matches)>0){ |
|
|
|
|
return true; |
|
|
|
|
}else{ |
|
|
|
|
return false; |
|
|
|
@ -203,17 +183,17 @@ class Storage { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief get a list of all available versions of a file in descending chronological order |
|
|
|
|
* @param $filename file to find versions of, relative to the user files dir |
|
|
|
|
* @param $count number of versions to return |
|
|
|
|
* @returns array |
|
|
|
|
*/ |
|
|
|
|
public static function getVersions( $filename, $count = 0 ) { |
|
|
|
|
|
|
|
|
|
if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief get a list of all available versions of a file in descending chronological order |
|
|
|
|
* @param $filename file to find versions of, relative to the user files dir |
|
|
|
|
* @param $count number of versions to return |
|
|
|
|
* @returns array |
|
|
|
|
*/ |
|
|
|
|
public static function getVersions( $filename, $count = 0 ) { |
|
|
|
|
|
|
|
|
|
if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { |
|
|
|
|
|
|
|
|
|
if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { |
|
|
|
|
$pos = strpos($source, '/files', 1); |
|
|
|
|
$uid = substr($source, 1, $pos - 1); |
|
|
|
@ -221,70 +201,71 @@ class Storage { |
|
|
|
|
} else { |
|
|
|
|
$uid = \OCP\User::getUser(); |
|
|
|
|
} |
|
|
|
|
$versionsFolderName = \OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
$versions = array(); |
|
|
|
|
|
|
|
|
|
$versions_fileview = \OCP\Files::getStorage('files_versions'); |
|
|
|
|
$versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); |
|
|
|
|
$versions = array(); |
|
|
|
|
|
|
|
|
|
// fetch for old versions |
|
|
|
|
$matches = glob( $versionsFolderName.'/'.$filename.'.v*' ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sort( $matches ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$i = 0; |
|
|
|
|
|
|
|
|
|
foreach( $matches as $ma ) { |
|
|
|
|
|
|
|
|
|
$files_view = \OCP\Files::getStorage('files'); |
|
|
|
|
$local_file = $files_view->getLocalFile($filename); |
|
|
|
|
foreach( $matches as $ma ) { |
|
|
|
|
|
|
|
|
|
$i++; |
|
|
|
|
$versions[$i]['cur'] = 0; |
|
|
|
|
$parts = explode( '.v', $ma ); |
|
|
|
|
$versions[$i]['version'] = ( end( $parts ) ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if file with modified date exists, flag it in array as currently enabled version |
|
|
|
|
$files_view = \OCP\Files::getStorage('files'); |
|
|
|
|
$local_file = $files_view->getLocalFile($filename); |
|
|
|
|
( \md5_file( $ma ) == \md5_file( $local_file ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$versions = array_reverse( $versions ); |
|
|
|
|
|
|
|
|
|
foreach( $versions as $key => $value ) { |
|
|
|
|
|
|
|
|
|
foreach( $versions as $key => $value ) { |
|
|
|
|
|
|
|
|
|
// flag the first matched file in array (which will have latest modification date) as current version |
|
|
|
|
if ( $versions[$key]['fileMatch'] ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$versions[$key]['cur'] = 1; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$versions = array_reverse( $versions ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// only show the newest commits |
|
|
|
|
if( $count != 0 and ( count( $versions )>$count ) ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$versions = array_slice( $versions, count( $versions ) - $count ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return( $versions ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
// if versioning isn't enabled then return an empty array |
|
|
|
|
return( array() ); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief Erase a file's versions which exceed the set quota |
|
|
|
|
*/ |
|
|
|
|
public static function expire($filename) { |
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
return( array() ); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief Erase a file's versions which exceed the set quota |
|
|
|
|
*/ |
|
|
|
|
public static function expire($filename) { |
|
|
|
|
if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { |
|
|
|
|
|
|
|
|
|
if (\OCP\App::isEnabled('files_sharing') && $source = \OC_Share::getSource('/'.\OCP\User::getUser().'/files'.$filename)) { |
|
|
|
|
$pos = strpos($source, '/files', 1); |
|
|
|
@ -293,36 +274,33 @@ class Storage { |
|
|
|
|
} else { |
|
|
|
|
$uid = \OCP\User::getUser(); |
|
|
|
|
} |
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
$versions_fileview = \OCP\Files::getStorage("files_versions"); |
|
|
|
|
$versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); |
|
|
|
|
|
|
|
|
|
// check for old versions |
|
|
|
|
$matches = glob( $versionsFolderName.'/'.$filename.'.v*' ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( count( $matches ) > \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$numberToDelete = count( $matches-\OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ); |
|
|
|
|
|
|
|
|
|
// delete old versions of a file |
|
|
|
|
$deleteItems = array_slice( $matches, 0, $numberToDelete ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach( $deleteItems as $de ) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unlink( $versionsFolderName.'/'.$filename.'.v'.$de ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief Erase all old versions of all user files |
|
|
|
|
* @return true/false |
|
|
|
|
*/ |
|
|
|
|
public function expireAll() { |
|
|
|
|
|
|
|
|
|
$dir = \OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER); |
|
|
|
|
|
|
|
|
|
return $this->view->deleteAll( $dir, true ); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @brief Erase all old versions of all user files |
|
|
|
|
* @return true/false |
|
|
|
|
*/ |
|
|
|
|
public function expireAll() { |
|
|
|
|
return $this->view->deleteAll('', true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|