summaryrefslogtreecommitdiffstats
path: root/apps/files_versions/lib
diff options
context:
space:
mode:
authorSam Tuke <samtuke@owncloud.com>2012-07-03 17:42:51 +0100
committerSam Tuke <samtuke@owncloud.com>2012-07-03 17:42:51 +0100
commita775deaf22ee803ae2ff0dbe1068229d9c819232 (patch)
treef7a7f5ee4d57935710903ab649f53ee948108fad /apps/files_versions/lib
parentca2b1f78630257ff4a2beadb5e25d628e5195bb0 (diff)
downloadnextcloud-server-a775deaf22ee803ae2ff0dbe1068229d9c819232.tar.gz
nextcloud-server-a775deaf22ee803ae2ff0dbe1068229d9c819232.zip
Moved hooks and versions.php into new lib directory
Part-ported filesystem operations to OC_FilesystemView Refactored Storage class to depend on an OC_FsV object and have fewer static classes
Diffstat (limited to 'apps/files_versions/lib')
-rw-r--r--apps/files_versions/lib/hooks.php35
-rw-r--r--apps/files_versions/lib/versions.php353
2 files changed, 388 insertions, 0 deletions
diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php
new file mode 100644
index 00000000000..8a746705329
--- /dev/null
+++ b/apps/files_versions/lib/hooks.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * This class contains all hooks.
+ */
+
+namespace OCA_Versions;
+
+class Hooks {
+
+ /**
+ * listen to write event.
+ */
+ public static function write_hook( $params ) {
+
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+
+ $versions = new Storage( new \OC_FilesystemView('') );
+
+ $path = $params[\OC_Filesystem::signal_param_path];
+
+ if($path<>'') $versions->store( $path );
+
+ }
+ }
+
+}
+
+?>
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
new file mode 100644
index 00000000000..af7ae11a258
--- /dev/null
+++ b/apps/files_versions/lib/versions.php
@@ -0,0 +1,353 @@
+<?php
+/**
+ * Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
+ * 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:
+ // - 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 DEFAULTMININTERVAL=1; // 2 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);
+ }
+ }
+ }
+
+
+ /**
+ * listen to write event.
+ */
+ public static function write_hook($params) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
+ $path = $params[\OC_Filesystem::signal_param_path];
+ if($path<>'') $this->store($path);
+ }
+ }
+
+
+
+ /**
+ * store a new version of a file.
+ */
+ public function store($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);
+ $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);
+ $filesfoldername=\OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/files';
+ Storage::init();
+
+ // check if filename is a directory
+ if(is_dir($filesfoldername.'/'.$filename)){
+ return false;
+ }
+
+ // check filetype blacklist
+ $blacklist=explode(' ',\OCP\Config::getSystemValue('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)>\OCP\Config::getSystemValue('files_versionsmaxfilesize', Storage::DEFAULTMAXFILESIZE)){
+ return false;
+ }
+
+
+ // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval)
+ if ($uid == \OCP\User::getUser()) {
+ $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);
+ if(!file_exists($versionsFolderName.'/'.$info['dirname'])) mkdir($versionsFolderName.'/'.$info['dirname'],0700,true);
+
+ // store a new version of a file
+ copy($filesfoldername.'/'.$filename,$versionsFolderName.'/'.$filename.'.v'.time());
+
+ // expire old revisions if necessary
+ $this->expire($filename);
+ }
+ }
+
+
+ /**
+ * rollback to an old version of a file.
+ */
+ public function rollback( $filename, $revision ) {
+
+ 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);
+ $filename = substr($source, $pos + 6);
+ } 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 ( copy( $versionsFolderName.'/'.$filename.'.v'.$revision, $filesfoldername.'/'.$filename ) ) {
+
+ return true;
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+ }
+
+ /**
+ * check if old versions of a file exist.
+ */
+ public static function isversioned($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);
+ $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);
+
+ // check for old versions
+ $matches=glob($versionsFolderName.'/'.$filename.'.v*');
+ if(count($matches)>1){
+ return true;
+ }else{
+ return false;
+ }
+ }else{
+ return(false);
+ }
+ }
+
+
+
+ /**
+ * @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);
+ $filename = substr($source, $pos + 6);
+ } else {
+ $uid = \OCP\User::getUser();
+ }
+ $versionsFolderName = \OCP\Config::getSystemValue('datadirectory').'/'. $uid .'/'.\OCP\Config::getSystemValue('files_versionsfolder', Storage::DEFAULTFOLDER);
+ $versions = array();
+
+ // fetch for old versions
+ $matches = glob( $versionsFolderName.'/'.$filename.'.v*' );
+
+ sort( $matches );
+
+ $i = 0;
+
+ 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
+ $curFile['fileName'] = basename( $parts[0] );
+ $curFile['filePath'] = \OCP\Config::getSystemValue('datadirectory').\OC_Filesystem::getRoot().'/'.$curFile['fileName'];
+
+ ( \md5_file( $ma ) == \md5_file( $curFile['filePath'] ) ? $versions[$i]['fileMatch'] = 1 : $versions[$i]['fileMatch'] = 0 );
+
+ }
+
+ $versions = array_reverse( $versions );
+
+ 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 {
+
+ // if versioning isn't enabled then return an empty array
+ return( array() );
+
+ }
+
+ }
+
+ /**
+ * @brief Erase a file's versions which exceed the set quota
+ */
+ public 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);
+ $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);
+
+ // 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 ) {
+
+ $this->view->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 versions of deleted file
+ * @param array
+ *
+ * This function is connected to the delete signal of OC_Filesystem
+ * cleanup the versions directory if the actual file gets deleted
+ */
+ public static function removeVersions($params) {
+ $rel_path = $params['path'];
+ $abs_path = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$rel_path.'.v';
+ if(Storage::isversioned($rel_path)) {
+ $versions = Storage::getVersions($rel_path);
+ foreach ($versions as $v){
+ $this->view->unlink( $abs_path . $v['version'] );
+ }
+ }
+ }
+
+ /**
+ * @brief rename/move versions of renamed/moved files
+ * @param array with oldpath and newpath
+ *
+ * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
+ * of the stored versions along the actual file
+ */
+ public static function renameVersions($params) {
+ $rel_oldpath = $params['oldpath'];
+ $abs_oldpath = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$rel_oldpath.'.v';
+ $abs_newpath = \OCP\Config::getSystemValue('datadirectory').'/'.\OCP\User::getUser()."/versions".$params['newpath'].'.v';
+ if(Storage::isversioned($rel_oldpath)) {
+ $versions = Storage::getVersions($rel_oldpath);
+ foreach ($versions as $v){
+ rename($abs_oldpath.$v['version'], $abs_newpath.$v['version']);
+ }
+ }
+ }
+}