* This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ /** * Versions * * A class to handle the versioning of files. */ namespace OCA_Versions; class Storage { // config.php configuration: // - files_versions // - files_versionsfolder // - files_versionsblacklist // - files_versionsmaxfilesize // - files_versionsinterval // - files_versionmaxversions // // todo: // - port to oc_filesystem to enable network transparency // - check if it works well together with encryption // - do configuration web interface // - implement expire all function. And find a place to call it ;-) // - add transparent compression. first test if it´s worth it. const DEFAULTENABLED=true; const DEFAULTFOLDER='versions'; const DEFAULTBLACKLIST='avi mp3 mpg mp4'; const DEFAULTMAXFILESIZE=1048576; // 10MB const DEFAULTMININTERVAL=1; // 5 min const DEFAULTMAXVERSIONS=50; /** * init the versioning and create the versions folder. */ public static function init() { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { // create versions folder $foldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); if(!is_dir($foldername)){ mkdir($foldername); } } } /** * listen to write event. */ public static function write_hook($params) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $path = $params[\OC_Filesystem::signal_param_path]; if($path<>'') Storage::store($path); } } /** * store a new version of a file. */ public static function store($filename) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files'; Storage::init(); // check if filename is a directory if(is_dir($filesfoldername.$filename)){ return false; } // check filetype blacklist $blacklist=explode(' ',\OC_Config::getValue('files_versionsblacklist', Storage::DEFAULTBLACKLIST)); foreach($blacklist as $bl) { $parts=explode('.', $filename); $ext=end($parts); if(strtolower($ext)==$bl) { return false; } } // check filesize if(filesize($filesfoldername.$filename)>\OC_Config::getValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){ return false; } // check mininterval $matches=glob($versionsfoldername.$filename.'.v*'); sort($matches); $parts=explode('.v',end($matches)); if((end($parts)+Storage::DEFAULTMININTERVAL)>time()){ return false; } // create all parent folders $info=pathinfo($filename); @mkdir($versionsfoldername.$info['dirname'],0700,true); // store a new version of a file copy($filesfoldername.$filename,$versionsfoldername.$filename.'.v'.time()); // expire old revisions Storage::expire($filename); } } /** * rollback to an old version of a file. */ public static function rollback($filename,$revision) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); $filesfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/files'; // rollback @copy($versionsfoldername.$filename.'.v'.$revision,$filesfoldername.$filename); } } /** * check if old versions of a file exist. */ public static function isversioned($filename) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); // check for old versions $matches=glob($versionsfoldername.$filename.'.v*'); if(count($matches)>1){ return true; }else{ return false; } }else{ return(false); } } /** * get a list of old versions of a file. */ public static function getversions($filename,$count=0) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); $versions=array(); // fetch for old versions $matches=glob($versionsfoldername.$filename.'.v*'); sort($matches); foreach($matches as $ma) { $parts=explode('.v',$ma); $versions[]=(end($parts)); } // only show the newest commits if($count<>0 and (count($versions)>$count)) { $versions=array_slice($versions,count($versions)-$count); } return($versions); }else{ return(array()); } } /** * expire old versions of a file. */ public static function expire($filename) { if(\OC_Config::getValue('files_versions', Storage::DEFAULTENABLED)=='true') { $versionsfoldername=\OC_Config::getValue('datadirectory').'/'. \OC_User::getUser() .'/'.\OC_Config::getValue('files_versionsfolder', Storage::DEFAULTFOLDER); // check for old versions $matches=glob($versionsfoldername.$filename.'.v*'); if(count($matches)>\OC_Config::getValue('files_versionmaxversions', Storage::DEFAULTMAXVERSIONS)){ $numbertodelete=count($matches-\OC_Config::getValue('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); } } } } /** * expire all old versions. */ public static function expireall($filename) { // todo this should go through all the versions directories and delete all the not needed files and not needed directories. // useful to be included in a cleanup cronjob. } }